aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNeo2003 <none@none>2008-10-11 14:16:25 -0500
committerNeo2003 <none@none>2008-10-11 14:16:25 -0500
commit95b91afbab455d917a2800d8936e1f29e4d9604d (patch)
tree85e9e642d493123cd8cbafbf8d88ce9cafa32ddf /src
parentf84ae3af9e9ab10bf43243187e5b458698e9a39a (diff)
[svn] * svn:eol-style native set on all files that need it
--HG-- branch : trunk
Diffstat (limited to 'src')
-rw-r--r--src/bindings/scripts/README.TXT12
-rw-r--r--src/bindings/scripts/ScriptMgr.cpp4270
-rw-r--r--src/bindings/scripts/ScriptMgr.h176
-rw-r--r--src/bindings/scripts/VC71/71ScriptDev2.vcproj3190
-rw-r--r--src/bindings/scripts/VC80/80ScriptDev2.vcproj4686
-rw-r--r--src/bindings/scripts/VC90/90ScriptDev2.vcproj4682
-rw-r--r--src/bindings/scripts/docs/EventAI.txt1402
-rw-r--r--src/bindings/scripts/include/sc_creature.cpp1216
-rw-r--r--src/bindings/scripts/include/sc_gossip.h366
-rw-r--r--src/bindings/scripts/sql/create_database.sql6
-rw-r--r--src/bindings/scripts/sql/scriptdev2_structure.sql228
-rw-r--r--src/bindings/scripts/trinityscript.conf.dist40
-rw-r--r--src/game/AggressorAI.cpp314
-rw-r--r--src/game/ArenaTeamHandler.cpp926
-rw-r--r--src/game/BattleGroundHandler.cpp1904
-rw-r--r--src/game/CharacterHandler.cpp2248
-rw-r--r--src/game/Chat.cpp2168
-rw-r--r--src/game/Chat.h822
-rw-r--r--src/game/ChatHandler.cpp1166
-rw-r--r--src/game/ConfusedMovementGenerator.cpp310
-rw-r--r--src/game/Creature.cpp4132
-rw-r--r--src/game/Creature.h1234
-rw-r--r--src/game/CreatureAISelector.cpp236
-rw-r--r--src/game/FleeingMovementGenerator.cpp758
-rw-r--r--src/game/GameObject.cpp2494
-rw-r--r--src/game/GameObject.h1184
-rw-r--r--src/game/GossipDef.cpp1524
-rw-r--r--src/game/GossipDef.h412
-rw-r--r--src/game/GridNotifiers.h1882
-rw-r--r--src/game/Group.cpp2908
-rw-r--r--src/game/GuardAI.cpp308
-rw-r--r--src/game/Guild.cpp3926
-rw-r--r--src/game/Guild.h874
-rw-r--r--src/game/HomeMovementGenerator.cpp172
-rw-r--r--src/game/ItemHandler.cpp2422
-rw-r--r--src/game/Language.h1326
-rw-r--r--src/game/Level1.cpp4958
-rw-r--r--src/game/Level2.cpp8018
-rw-r--r--src/game/Level3.cpp10932
-rw-r--r--src/game/MiscHandler.cpp3532
-rw-r--r--src/game/MotionMaster.cpp684
-rw-r--r--src/game/NPCHandler.cpp1652
-rw-r--r--src/game/Object.cpp2894
-rw-r--r--src/game/ObjectMgr.cpp13994
-rw-r--r--src/game/ObjectMgr.h1730
-rw-r--r--src/game/Pet.cpp3500
-rw-r--r--src/game/PetHandler.cpp1298
-rw-r--r--src/game/PetitionsHandler.cpp1872
-rw-r--r--src/game/Player.cpp36594
-rw-r--r--src/game/Player.h4672
-rw-r--r--src/game/PointMovementGenerator.cpp152
-rw-r--r--src/game/QuestDef.cpp400
-rw-r--r--src/game/QuestDef.h664
-rw-r--r--src/game/RandomMovementGenerator.cpp326
-rw-r--r--src/game/SocialMgr.cpp622
-rw-r--r--src/game/Spell.cpp10178
-rw-r--r--src/game/Spell.h1392
-rw-r--r--src/game/SpellAuras.cpp12720
-rw-r--r--src/game/SpellEffects.cpp12374
-rw-r--r--src/game/StatSystem.cpp1930
-rw-r--r--src/game/TargetedMovementGenerator.cpp422
-rw-r--r--src/game/TradeHandler.cpp1268
-rw-r--r--src/game/Unit.cpp21656
-rw-r--r--src/game/Unit.h2674
-rw-r--r--src/game/WaypointMovementGenerator.cpp1304
-rw-r--r--src/game/World.cpp5190
-rw-r--r--src/game/World.h1136
-rw-r--r--src/game/WorldSession.cpp1022
-rw-r--r--src/game/WorldSession.h1286
-rw-r--r--src/shared/Database/DBCStructure.h1876
-rw-r--r--src/shared/Database/DBCfmt.cpp156
-rw-r--r--src/shared/Database/DatabaseEnv.h108
-rw-r--r--src/shared/WheatyExceptionReport.cpp2028
-rw-r--r--src/shared/WheatyExceptionReport.h234
74 files changed, 116686 insertions, 116686 deletions
diff --git a/src/bindings/scripts/README.TXT b/src/bindings/scripts/README.TXT
index 5fe11481431..548dabd1cb7 100644
--- a/src/bindings/scripts/README.TXT
+++ b/src/bindings/scripts/README.TXT
@@ -1,6 +1,6 @@
-To compile this properly, you need to get the scripts from:
-
-http://svn.assembla.com/svn/trinityscripts
-
-and get them in a "scripts" folder inside this folder
-
+To compile this properly, you need to get the scripts from:
+
+http://svn.assembla.com/svn/trinityscripts
+
+and get them in a "scripts" folder inside this folder
+
diff --git a/src/bindings/scripts/ScriptMgr.cpp b/src/bindings/scripts/ScriptMgr.cpp
index 11d293ffe3d..d29999cb003 100644
--- a/src/bindings/scripts/ScriptMgr.cpp
+++ b/src/bindings/scripts/ScriptMgr.cpp
@@ -1,2135 +1,2135 @@
-/* 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);
-}
+/* 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 8ff1ee3cfdd..5709a9e496b 100644
--- a/src/bindings/scripts/ScriptMgr.h
+++ b/src/bindings/scripts/ScriptMgr.h
@@ -1,88 +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)
-#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
+/* 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/VC71/71ScriptDev2.vcproj b/src/bindings/scripts/VC71/71ScriptDev2.vcproj
index 38368d48de5..80d611c2afc 100644
--- a/src/bindings/scripts/VC71/71ScriptDev2.vcproj
+++ b/src/bindings/scripts/VC71/71ScriptDev2.vcproj
@@ -1,1595 +1,1595 @@
-<?xml version="1.0" encoding="windows-1251"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="7.10"
- Name="TrinityScript"
- ProjectGUID="{4295C8A9-79B7-4354-8064-F05FB9CA0C96}"
- RootNamespace="ScriptDev2"
- Keyword="Win32Proj">
- <Platforms>
- <Platform
- Name="Win32"/>
- </Platforms>
- <Configurations>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
- ConfigurationType="2"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
- PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT"
- MinimalRebuild="TRUE"
- BasicRuntimeChecks="3"
- RuntimeLibrary="1"
- UsePrecompiledHeader="3"
- PrecompiledHeaderThrough="precompiled.h"
- WarningLevel="3"
- Detect64BitPortabilityProblems="TRUE"
- DebugInformationFormat="4"/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="trinitycore.lib zthread.lib"
- OutputFile="$(OutDir)/TrinityScript.dll"
- LinkIncremental="2"
- AdditionalLibraryDirectories="..\..\..\..\win\VC71\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC71\trinitycore__$(PlatformName)_$(ConfigurationName)"
- GenerateDebugInformation="TRUE"
- ProgramDatabaseFile="$(OutDir)/MaNGOSScript.pdb"
- SubSystem="2"
- ImportLibrary="$(OutDir)/TrinityScript.lib"
- TargetMachine="1"/>
- <Tool
- Name="VCMIDLTool"/>
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCResourceCompilerTool"/>
- <Tool
- Name="VCWebServiceProxyGeneratorTool"/>
- <Tool
- Name="VCXMLDataGeneratorTool"/>
- <Tool
- Name="VCWebDeploymentTool"/>
- <Tool
- Name="VCManagedWrapperGeneratorTool"/>
- <Tool
- Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
- ConfigurationType="2"
- CharacterSet="2">
- <Tool
- Name="VCCLCompilerTool"
- AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
- PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT"
- RuntimeLibrary="0"
- EnableEnhancedInstructionSet="1"
- UsePrecompiledHeader="3"
- PrecompiledHeaderThrough="precompiled.h"
- WarningLevel="3"
- Detect64BitPortabilityProblems="TRUE"
- DebugInformationFormat="3"/>
- <Tool
- Name="VCCustomBuildTool"/>
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="trinitycore.lib zthread.lib"
- OutputFile="$(OutDir)/TrinityScript.dll"
- LinkIncremental="1"
- AdditionalLibraryDirectories="..\..\..\..\win\VC71\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC71\trinitycore__$(PlatformName)_$(ConfigurationName)"
- GenerateDebugInformation="FALSE"
- SubSystem="2"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- ImportLibrary="$(OutDir)/TrinityScript.lib"
- TargetMachine="1"/>
- <Tool
- Name="VCMIDLTool"/>
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"/>
- <Tool
- Name="VCPreBuildEventTool"/>
- <Tool
- Name="VCPreLinkEventTool"/>
- <Tool
- Name="VCResourceCompilerTool"/>
- <Tool
- Name="VCWebServiceProxyGeneratorTool"/>
- <Tool
- Name="VCXMLDataGeneratorTool"/>
- <Tool
- Name="VCWebDeploymentTool"/>
- <Tool
- Name="VCManagedWrapperGeneratorTool"/>
- <Tool
- Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
- </Configuration>
- </Configurations>
- <References>
- </References>
- <Files>
- <Filter
- Name="Scripts">
- <Filter
- Name="boss">
- <File
- RelativePath="..\scripts\boss\boss_emeriss.cpp">
- </File>
- <File
- RelativePath="..\scripts\boss\boss_lethon.cpp">
- </File>
- <File
- RelativePath="..\scripts\boss\boss_taerar.cpp">
- </File>
- <File
- RelativePath="..\scripts\boss\boss_ysondre.cpp">
- </File>
- </Filter>
- <Filter
- Name="creature">
- <File
- RelativePath="..\scripts\creature\mob_event_ai.cpp">
- </File>
- <File
- RelativePath="..\scripts\creature\mob_event_ai.h">
- </File>
- <File
- RelativePath="..\scripts\creature\mob_generic_creature.cpp">
- </File>
- <File
- RelativePath="..\scripts\creature\simple_ai.cpp">
- </File>
- <File
- RelativePath="..\scripts\creature\simple_ai.h">
- </File>
- </Filter>
- <Filter
- Name="guard">
- <File
- RelativePath="..\scripts\guard\guard_ai.cpp">
- </File>
- <File
- RelativePath="..\scripts\guard\guard_ai.h">
- </File>
- <File
- RelativePath="..\scripts\guard\guards.cpp">
- </File>
- </Filter>
- <Filter
- Name="honor">
- </Filter>
- <Filter
- Name="npc">
- <File
- RelativePath="..\scripts\npc\npc_escortAI.cpp">
- </File>
- <File
- RelativePath="..\scripts\npc\npc_escortAI.h">
- </File>
- <File
- RelativePath="..\scripts\npc\npc_innkeeper.cpp">
- </File>
- <File
- RelativePath="..\scripts\npc\npc_professions.cpp">
- </File>
- <File
- RelativePath="..\scripts\npc\npcs_special.cpp">
- </File>
- </Filter>
- <Filter
- Name="servers">
- </Filter>
- <Filter
- Name="custom">
- <File
- RelativePath="..\scripts\custom\custom_example.cpp">
- </File>
- <File
- RelativePath="..\scripts\custom\custom_gossip_codebox.cpp">
- </File>
- <File
- RelativePath="..\scripts\custom\test.cpp">
- </File>
- </Filter>
- <Filter
- Name="areatrigger">
- <File
- RelativePath="..\scripts\areatrigger\areatrigger_scripts.cpp">
- </File>
- </Filter>
- <Filter
- Name="go">
- <File
- RelativePath="..\scripts\go\go_scripts.cpp">
- </File>
- </Filter>
- <Filter
- Name="item">
- <File
- RelativePath="..\scripts\item\item_scripts.cpp">
- </File>
- <File
- RelativePath="..\scripts\item\item_test.cpp">
- </File>
- </Filter>
- <Filter
- Name="zone">
- <Filter
- Name="Alterac Mountains">
- <File
- RelativePath="..\scripts\zone\alterac_mountains\alterac_mountains.cpp">
- </File>
- </Filter>
- <Filter
- Name="Ashenvale Forest">
- </Filter>
- <Filter
- Name="Azshara">
- <File
- RelativePath="..\scripts\zone\azshara\azshara.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\azshara\boss_azuregos.cpp">
- </File>
- </Filter>
- <Filter
- Name="Badlands">
- </Filter>
- <Filter
- Name="Barrens">
- <File
- RelativePath="..\scripts\zone\barrens\the_barrens.cpp">
- </File>
- </Filter>
- <Filter
- Name="Blackfathom Depths">
- </Filter>
- <Filter
- Name="Arathi Highlands">
- </Filter>
- <Filter
- Name="Deadmines">
- <File
- RelativePath="..\scripts\zone\deadmines\deadmines.cpp">
- </File>
- </Filter>
- <Filter
- Name="Deadwind Pass">
- </Filter>
- <Filter
- Name="Desolace">
- </Filter>
- <Filter
- Name="Dire Maul">
- </Filter>
- <Filter
- Name="Dun Morogh">
- <File
- RelativePath="..\scripts\zone\dun_morogh\dun_morogh.cpp">
- </File>
- </Filter>
- <Filter
- Name="Durotar">
- </Filter>
- <Filter
- Name="Duskwood">
- </Filter>
- <Filter
- Name="Dustwallow Marsh">
- <File
- RelativePath="..\scripts\zone\dustwallow_marsh\dustwallow_marsh.cpp">
- </File>
- </Filter>
- <Filter
- Name="Blackwing Lair">
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_broodlord_lashlayer.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_chromaggus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_ebonroc.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_firemaw.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_flamegor.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_nefarian.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_razorgore.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_vaelastrasz.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_victor_nefarius.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\instance_blackwing_lair.cpp">
- </File>
- </Filter>
- <Filter
- Name="Bloodmyst Isle">
- <File
- RelativePath="..\scripts\zone\bloodmyst_isle\bloodmyst_isle.cpp">
- </File>
- </Filter>
- <Filter
- Name="Gruul&apos;s Lair">
- <File
- RelativePath="..\scripts\zone\gruuls_lair\boss_gruul.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\gruuls_lair\boss_high_king_maulgar.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\gruuls_lair\def_gruuls_lair.h">
- </File>
- <File
- RelativePath="..\scripts\zone\gruuls_lair\instance_gruuls_lair.cpp">
- </File>
- </Filter>
- <Filter
- Name="Burning Steppes">
- <File
- RelativePath="..\scripts\zone\burning_steppes\burning_steppes.cpp">
- </File>
- </Filter>
- <Filter
- Name="Darkshore">
- <File
- RelativePath="..\scripts\zone\darkshore\darkshore.cpp">
- </File>
- </Filter>
- <Filter
- Name="Eastern Plaguelands">
- <File
- RelativePath="..\scripts\zone\eastern_plaguelands\eastern_plaguelands.cpp">
- </File>
- </Filter>
- <Filter
- Name="Moonglade">
- <File
- RelativePath="..\scripts\zone\moonglade\moonglade.cpp">
- </File>
- </Filter>
- <Filter
- Name="Razorfen Kraul">
- </Filter>
- <Filter
- Name="Redridge Mountains">
- </Filter>
- <Filter
- Name="Ruins of Ahn&apos;Qiraj">
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ayamiss.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_buru.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_kurinnaxx.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_moam.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ossirian.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_rajaxx.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\instance_ruins_of_ahnqiraj.cpp">
- </File>
- </Filter>
- <Filter
- Name="Swamp of Sorrows">
- </Filter>
- <Filter
- Name="Scarlet Monastery">
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_arcanist_doan.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_azshir_the_sleepless.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_bloodmage_thalnos.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_herod.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_fairbanks.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_whitemane.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_houndmaster_loksey.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_interrogator_vishas.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_scarlet_commander_mograine.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_scorn.cpp">
- </File>
- </Filter>
- <Filter
- Name="Scholomance">
- <File
- RelativePath="..\scripts\zone\scholomance\boss_darkmaster_gandling.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_death_knight_darkreaver.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_doctor_theolen_krastinov.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_illucia_barov.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_instructor_malicia.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_jandice_barov.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_kormok.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_lord_alexei_barov.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_lorekeeper_polkelt.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_ras_frostwhisper.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_the_ravenian.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_vectus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\def_scholomance.h">
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\instance_scholomance.cpp">
- </File>
- </Filter>
- <Filter
- Name="Searing Gorge">
- <File
- RelativePath="..\scripts\zone\searing_gorge\searing_gorge.cpp">
- </File>
- </Filter>
- <Filter
- Name="Elwynn Forest">
- <File
- RelativePath="..\scripts\zone\elwynn_forest\elwynn_forest.cpp">
- </File>
- </Filter>
- <Filter
- Name="Felwood">
- <File
- RelativePath="..\scripts\zone\felwood\felwood.cpp">
- </File>
- </Filter>
- <Filter
- Name="Feralas">
- <File
- RelativePath="..\scripts\zone\feralas\feralas.cpp">
- </File>
- </Filter>
- <Filter
- Name="Gnomeregan">
- </Filter>
- <Filter
- Name="Hillsbrad Foothills">
- </Filter>
- <Filter
- Name="Hinterlands">
- </Filter>
- <Filter
- Name="Maraudon">
- <File
- RelativePath="..\scripts\zone\maraudon\boss_celebras_the_cursed.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\maraudon\boss_landslide.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\maraudon\boss_noxxion.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\maraudon\boss_princess_theradras.cpp">
- </File>
- </Filter>
- <Filter
- Name="Molten Core">
- <File
- RelativePath="..\scripts\zone\molten_core\boss_baron_geddon.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_garr.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_gehennas.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_golemagg.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_lucifron.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_magmadar.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_majordomo_executus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_ragnaros.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_shazzrah.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_sulfuron_harbinger.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\def_molten_core.h">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\instance_molten_core.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\molten_core.cpp">
- </File>
- </Filter>
- <Filter
- Name="Mulgore">
- <File
- RelativePath="..\scripts\zone\mulgore\mulgore.cpp">
- </File>
- </Filter>
- <Filter
- Name="Naxxramas">
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_anubrekhan.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_faerlina.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_feugen.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_gluth.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_gothik.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_grobbulus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_heigan.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_highlord_mograine.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_kelthuzad.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_lady_blaumeux.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_loatheb.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_maexxna.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_noth.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_patchwerk.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_razuvious.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_sapphiron.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_sir_zeliek.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_stalagg.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_thaddius.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_thane_korthazz.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\instance_naxxramas.cpp">
- </File>
- </Filter>
- <Filter
- Name="Onyxia&apos;s Lair">
- <File
- RelativePath="..\scripts\zone\onyxias_lair\boss_onyxia.cpp">
- </File>
- </Filter>
- <Filter
- Name="Ragefire Chasm">
- </Filter>
- <Filter
- Name="Razorfen Downs">
- <File
- RelativePath="..\scripts\zone\razorfen_downs\boss_amnennar_the_coldbringer.cpp">
- </File>
- </Filter>
- <Filter
- Name="Shadowfang Keep">
- <File
- RelativePath="..\scripts\zone\shadowfang_keep\def_shadowfang_keep.h">
- </File>
- <File
- RelativePath="..\scripts\zone\shadowfang_keep\instance_shadowfang_keep.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\shadowfang_keep\shadowfang_keep.cpp">
- </File>
- </Filter>
- <Filter
- Name="Stonetalon Mountains">
- <File
- RelativePath="..\scripts\zone\stonetalon_mountains\stonetalon_mountains.cpp">
- </File>
- </Filter>
- <Filter
- Name="Stranglethorn Vale">
- <File
- RelativePath="..\scripts\zone\stranglethorn_vale\stranglethorn_vale.cpp">
- </File>
- </Filter>
- <Filter
- Name="Stratholme">
- <File
- RelativePath="..\scripts\zone\stratholme\boss_baron_rivendare.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_baroness_anastari.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_cannon_master_willey.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_dathrohan_balnazzar.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_magistrate_barthilas.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_maleki_the_pallid.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_nerubenkan.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_order_of_silver_hand.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_postmaster_malown.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_ramstein_the_gorger.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_timmy_the_cruel.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\def_stratholme.h">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\instance_stratholme.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\stratholme.cpp">
- </File>
- </Filter>
- <Filter
- Name="Sunken Temple">
- </Filter>
- <Filter
- Name="Tanaris">
- <File
- RelativePath="..\scripts\zone\tanaris\tanaris.cpp">
- </File>
- </Filter>
- <Filter
- Name="Teldrassil">
- </Filter>
- <Filter
- Name="Temple of Ahn&apos;Qiraj">
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_bug_trio.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_cthun.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_fankriss.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_huhuran.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_ouro.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_sartura.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_skeram.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_twinemperors.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_viscidus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\def_temple_of_ahnqiraj.h">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\instance_temple_of_ahnqiraj.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\mob_anubisath_sentinel.cpp">
- </File>
- </Filter>
- <Filter
- Name="Thousand Needles">
- </Filter>
- <Filter
- Name="Silithus">
- <File
- RelativePath="..\scripts\zone\silithus\silithus.cpp">
- </File>
- </Filter>
- <Filter
- Name="Silverpine Forest">
- <File
- RelativePath="..\scripts\zone\silverpine_forest\silverpine_forest.cpp">
- </File>
- </Filter>
- <Filter
- Name="Stockade">
- </Filter>
- <Filter
- Name="Tirisfal Glades">
- <File
- RelativePath="..\scripts\zone\tirisfal_glades\tirisfal_glades.cpp">
- </File>
- </Filter>
- <Filter
- Name="Wailing Caverns">
- <File
- RelativePath="..\scripts\zone\wailing_caverns\instance_wailing_caverns.cpp">
- </File>
- </Filter>
- <Filter
- Name="Western Plaguelands">
- <File
- RelativePath="..\scripts\zone\western_plaguelands\western_plaguelands.cpp">
- </File>
- </Filter>
- <Filter
- Name="Westfall">
- </Filter>
- <Filter
- Name="Wetlands">
- </Filter>
- <Filter
- Name="Winterspring">
- <File
- RelativePath="..\scripts\zone\winterspring\winterspring.cpp">
- </File>
- </Filter>
- <Filter
- Name="Zul&apos;Farrak">
- <File
- RelativePath="..\scripts\zone\zulfarrak\zulfarrak.cpp">
- </File>
- </Filter>
- <Filter
- Name="Zul&apos;Gurub">
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_arlokk.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_gahzranka.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_grilek.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_hakkar.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_hazzarah.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_jeklik.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_jindo.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_mandokir.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_marli.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_renataki.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_thekal.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_venoxis.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_wushoolay.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\def_zulgurub.h">
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\instance_zulgurub.cpp">
- </File>
- </Filter>
- <Filter
- Name="Uldaman">
- <File
- RelativePath="..\scripts\zone\uldaman\boss_ironaya.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\uldaman\uldaman.cpp">
- </File>
- </Filter>
- <Filter
- Name="Un&apos;Goro Crater">
- </Filter>
- <Filter
- Name="Aunchindoun">
- <Filter
- Name="Auchenai Crypts">
- <File
- RelativePath="..\scripts\zone\aunchindoun\auchenai_crypts\boss_exarch_maladaar.cpp">
- </File>
- </Filter>
- <Filter
- Name="Mana Tombs">
- <File
- RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_nexusprince_shaffar.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_pandemonius.cpp">
- </File>
- </Filter>
- <Filter
- Name="Sethekk Halls">
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_darkweaver_syth.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_tailonking_ikiss.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\def_sethekk_halls.h">
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\instance_sethekk_halls.cpp">
- </File>
- </Filter>
- <Filter
- Name="Shadow Labyrinth">
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_ambassador_hellmaw.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_blackheart_the_inciter.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_grandmaster_vorpil.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_murmur.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\def_shadow_labyrinth.h">
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\instance_shadow_labyrinth.cpp">
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Azuremyst Isle">
- <File
- RelativePath="..\scripts\zone\azuremyst_isle\azuremyst_isle.cpp">
- </File>
- </Filter>
- <Filter
- Name="Black Temple">
- <File
- RelativePath="..\scripts\zone\black_temple\black_temple.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_bloodboil.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_illidan.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_mother_shahraz.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_reliquary_of_souls.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_shade_of_akama.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_supremus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_teron_gorefiend.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_warlord_najentus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\def_black_temple.h">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\illidari_council.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\instance_black_temple.cpp">
- </File>
- </Filter>
- <Filter
- Name="Eversong Woods">
- <File
- RelativePath="..\scripts\zone\eversong_woods\eversong_woods.cpp">
- </File>
- </Filter>
- <Filter
- Name="Ghostlands">
- <File
- RelativePath="..\scripts\zone\ghostlands\ghostlands.cpp">
- </File>
- </Filter>
- <Filter
- Name="Blade&apos;s Edge Mountains">
- <File
- RelativePath="..\scripts\zone\blades_edge_mountains\blades_edge_mountains.cpp">
- </File>
- </Filter>
- <Filter
- Name="Blasted Lands">
- <File
- RelativePath="..\scripts\zone\blasted_lands\blasted_lands.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blasted_lands\boss_kruul.cpp">
- </File>
- </Filter>
- <Filter
- Name="Hellfire Citadel">
- <Filter
- Name="Blood Furnace">
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_broggok.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_kelidan_the_breaker.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_the_maker.cpp">
- </File>
- </Filter>
- <Filter
- Name="Magtheridon&apos;s lair">
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\boss_magtheridon.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\def_magtheridons_lair.h">
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\instance_magtheridons_lair.cpp">
- </File>
- </Filter>
- <Filter
- Name="Hellfire Ramparts">
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_omor_the_unscarred.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_watchkeeper_gargolmar.cpp">
- </File>
- </Filter>
- <Filter
- Name="Shattered Halls">
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_nethekurse.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_warbringer_omrogg.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\def_shattered_halls.h">
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\instance_shattered_halls.cpp">
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Hellfire Peninsula">
- <File
- RelativePath="..\scripts\zone\hellfire_peninsula\boss_doomlord_kazzak.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_peninsula\hellfire_peninsula.cpp">
- </File>
- </Filter>
- <Filter
- Name="Karazhan">
- <File
- RelativePath="..\scripts\zone\karazhan\boss_curator.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_maiden_of_virtue.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_midnight.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_moroes.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_netherspite.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_nightbane.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_prince_malchezaar.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_shade_of_aran.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_terestian_illhoof.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\bosses_opera.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\def_karazhan.h">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\instance_karazhan.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\karazhan.cpp">
- </File>
- </Filter>
- <Filter
- Name="Nagrand">
- <File
- RelativePath="..\scripts\zone\nagrand\nagrand.cpp">
- </File>
- </Filter>
- <Filter
- Name="Netherstorm">
- <File
- RelativePath="..\scripts\zone\netherstorm\netherstorm.cpp">
- </File>
- </Filter>
- <Filter
- Name="Shadowmoon Valley">
- <File
- RelativePath="..\scripts\zone\shadowmoon_valley\boss_doomwalker.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\shadowmoon_valley\shadowmoon_valley.cpp">
- </File>
- </Filter>
- <Filter
- Name="Tempest Keep">
- <Filter
- Name="Arcatraz">
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\arcatraz.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\boss_harbinger_skyriss.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\def_arcatraz.h">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\instance_arcatraz.cpp">
- </File>
- </Filter>
- <Filter
- Name="Botanica">
- <File
- RelativePath="..\scripts\zone\tempest_keep\botanica\boss_high_botanist_freywinn.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\botanica\boss_laj.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\botanica\boss_warp_splinter.cpp">
- </File>
- </Filter>
- <Filter
- Name="The Eye">
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_astromancer.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_kaelthas.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_void_reaver.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\def_the_eye.h">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\instance_the_eye.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\the_eye.cpp">
- </File>
- </Filter>
- <Filter
- Name="The Mechanar">
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_gyrokill.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_ironhand.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_nethermancer_sepethrea.cpp">
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Terokkar Forest">
- <File
- RelativePath="..\scripts\zone\terokkar_forest\terokkar_forest.cpp">
- </File>
- </Filter>
- <Filter
- Name="Zangarmarsh">
- <File
- RelativePath="..\scripts\zone\zangarmarsh\zangarmarsh.cpp">
- </File>
- </Filter>
- <Filter
- Name="Blackrock Spire">
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_drakkisath.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_gyth.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_halycon.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_highlord_omokk.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_mother_smolderweb.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_overlord_wyrmthalak.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_pyroguard_emberseer.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_quartermaster_zigris.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_rend_blackhand.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_shadow_hunter_voshgajin.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_the_beast.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_warmaster_voone.cpp">
- </File>
- </Filter>
- <Filter
- Name="Stormwind City">
- <File
- RelativePath="..\scripts\zone\stormwind\stormwind_city.cpp">
- </File>
- </Filter>
- <Filter
- Name="Coilfang Resevoir">
- <Filter
- Name="Serpent Shrine Cavern">
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_fathomlord_karathress.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_hydross_the_unstable.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_lady_vashj.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_leotheras_the_blind.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_morogrim_tidewalker.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\def_serpent_shrine.h">
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\instance_serpent_shrine.cpp">
- </File>
- </Filter>
- <Filter
- Name="Slave Pens">
- </Filter>
- <Filter
- Name="Steam Vault">
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_hydromancer_thespia.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_mekgineer_steamrigger.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_warlord_kalithresh.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\def_steam_vault.h">
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\instance_steam_vault.cpp">
- </File>
- </Filter>
- <Filter
- Name="Underbog">
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\underbog\boss_hungarfen.cpp">
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Caverns of Time">
- <Filter
- Name="The Dark Portal">
- <File
- RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_aeonus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_chrono_lord_deja.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_temporus.cpp">
- </File>
- </Filter>
- <Filter
- Name="Battle for Mt. Hyjal">
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\boss_archimonde.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\def_hyjal.h">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjal.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.h">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\instance_hyjal.cpp">
- </File>
- </Filter>
- <Filter
- Name="Old Hillsbrad">
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_captain_skarloc.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_epoch_hunter.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_leutenant_drake.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\def_old_hillsbrad.h">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\instance_old_hillsbrad.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\old_hillsbrad.cpp">
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Silvermoon City">
- <File
- RelativePath="..\scripts\zone\silvermoon\silvermoon_city.cpp">
- </File>
- </Filter>
- <Filter
- Name="Darnassus">
- </Filter>
- <Filter
- Name="Exodar">
- </Filter>
- <Filter
- Name="Iron Forge">
- <File
- RelativePath="..\scripts\zone\ironforge\ironforge.cpp">
- </File>
- </Filter>
- <Filter
- Name="Orgrimmar">
- <File
- RelativePath="..\scripts\zone\orgrimmar\orgrimmar.cpp">
- </File>
- </Filter>
- <Filter
- Name="Shattrath City">
- <File
- RelativePath="..\scripts\zone\shattrath\shattrath_city.cpp">
- </File>
- </Filter>
- <Filter
- Name="Thunder Bluff">
- <File
- RelativePath="..\scripts\zone\thunder_bluff\thunder_bluff.cpp">
- </File>
- </Filter>
- <Filter
- Name="Undercity">
- <File
- RelativePath="..\scripts\zone\undercity\undercity.cpp">
- </File>
- </Filter>
- <Filter
- Name="Zul&apos;Aman">
- <File
- RelativePath="..\scripts\zone\zulaman\boss_janalai.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\boss_nalorakk.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\def_zulaman.h">
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\instance_zulaman.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\zulaman.cpp">
- </File>
- </Filter>
- <Filter
- Name="Isle of Quel&apos;Danas">
- <File
- RelativePath="..\scripts\zone\isle_of_queldanas\isle_of_queldanas.cpp">
- </File>
- </Filter>
- <Filter
- Name="Magister&apos;s Terrace">
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_felblood_kaelthas.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_priestess_delrissa.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_selin_fireheart.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_vexallus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\def_magisters_terrace.h">
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\instance_magisters_terrace.cpp">
- </File>
- </Filter>
- <Filter
- Name="Sunwell Plateau">
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\boss_brutallus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\boss_kalecgos.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\def_sunwell_plateau.h">
- </File>
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\instance_sunwell_plateau.cpp">
- </File>
- </Filter>
- <Filter
- Name="Blackrock Depths">
- <File
- RelativePath="..\scripts\zone\blackrock_depths\blackrock_depths.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_ambassador_flamelash.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_angerrel.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_anubshiah.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_doomrel.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_doperel.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_emperor_dagran_thaurissan.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_general_angerforge.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_gloomrel.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_gorosh_the_dervish.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_grizzle.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_haterel.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_high_interrogator_gerstahn.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_magmus.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_moira_bronzebeard.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_seethrel.cpp">
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_vilerel.cpp">
- </File>
- </Filter>
- <Filter
- Name="Loch Modan">
- <File
- RelativePath="..\scripts\zone\loch_modan\loch_modan.cpp">
- </File>
- </Filter>
- </Filter>
- </Filter>
- <Filter
- Name="Include">
- <File
- RelativePath="..\include\precompiled.cpp">
- <FileConfiguration
- Name="Debug|Win32">
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="1"/>
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32">
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="1"/>
- </FileConfiguration>
- </File>
- <File
- RelativePath="..\include\precompiled.h">
- </File>
- <File
- RelativePath="..\include\sc_creature.cpp">
- </File>
- <File
- RelativePath="..\include\sc_creature.h">
- </File>
- <File
- RelativePath="..\include\sc_gossip.h">
- </File>
- <File
- RelativePath="..\include\sc_instance.h">
- </File>
- <File
- RelativePath="..\include\sc_item.h">
- </File>
- </Filter>
- <File
- RelativePath="..\config.h">
- </File>
- <File
- RelativePath="..\ScriptMgr.cpp">
- </File>
- <File
- RelativePath="..\ScriptMgr.h">
- </File>
- <File
- RelativePath="..\svn_revision.h">
- </File>
- <File
- RelativePath="..\system.cpp">
- </File>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
+<?xml version="1.0" encoding="windows-1251"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="TrinityScript"
+ ProjectGUID="{4295C8A9-79B7-4354-8064-F05FB9CA0C96}"
+ RootNamespace="ScriptDev2"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
+ PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="precompiled.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="trinitycore.lib zthread.lib"
+ OutputFile="$(OutDir)/TrinityScript.dll"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="..\..\..\..\win\VC71\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC71\trinitycore__$(PlatformName)_$(ConfigurationName)"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/MaNGOSScript.pdb"
+ SubSystem="2"
+ ImportLibrary="$(OutDir)/TrinityScript.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT"
+ RuntimeLibrary="0"
+ EnableEnhancedInstructionSet="1"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="precompiled.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="trinitycore.lib zthread.lib"
+ OutputFile="$(OutDir)/TrinityScript.dll"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="..\..\..\..\win\VC71\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC71\trinitycore__$(PlatformName)_$(ConfigurationName)"
+ GenerateDebugInformation="FALSE"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ ImportLibrary="$(OutDir)/TrinityScript.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Scripts">
+ <Filter
+ Name="boss">
+ <File
+ RelativePath="..\scripts\boss\boss_emeriss.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\boss\boss_lethon.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\boss\boss_taerar.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\boss\boss_ysondre.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="creature">
+ <File
+ RelativePath="..\scripts\creature\mob_event_ai.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\creature\mob_event_ai.h">
+ </File>
+ <File
+ RelativePath="..\scripts\creature\mob_generic_creature.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\creature\simple_ai.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\creature\simple_ai.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="guard">
+ <File
+ RelativePath="..\scripts\guard\guard_ai.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\guard\guard_ai.h">
+ </File>
+ <File
+ RelativePath="..\scripts\guard\guards.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="honor">
+ </Filter>
+ <Filter
+ Name="npc">
+ <File
+ RelativePath="..\scripts\npc\npc_escortAI.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npc_escortAI.h">
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npc_innkeeper.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npc_professions.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npcs_special.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="servers">
+ </Filter>
+ <Filter
+ Name="custom">
+ <File
+ RelativePath="..\scripts\custom\custom_example.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\custom\custom_gossip_codebox.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\custom\test.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="areatrigger">
+ <File
+ RelativePath="..\scripts\areatrigger\areatrigger_scripts.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="go">
+ <File
+ RelativePath="..\scripts\go\go_scripts.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="item">
+ <File
+ RelativePath="..\scripts\item\item_scripts.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\item\item_test.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="zone">
+ <Filter
+ Name="Alterac Mountains">
+ <File
+ RelativePath="..\scripts\zone\alterac_mountains\alterac_mountains.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Ashenvale Forest">
+ </Filter>
+ <Filter
+ Name="Azshara">
+ <File
+ RelativePath="..\scripts\zone\azshara\azshara.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\azshara\boss_azuregos.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Badlands">
+ </Filter>
+ <Filter
+ Name="Barrens">
+ <File
+ RelativePath="..\scripts\zone\barrens\the_barrens.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackfathom Depths">
+ </Filter>
+ <Filter
+ Name="Arathi Highlands">
+ </Filter>
+ <Filter
+ Name="Deadmines">
+ <File
+ RelativePath="..\scripts\zone\deadmines\deadmines.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Deadwind Pass">
+ </Filter>
+ <Filter
+ Name="Desolace">
+ </Filter>
+ <Filter
+ Name="Dire Maul">
+ </Filter>
+ <Filter
+ Name="Dun Morogh">
+ <File
+ RelativePath="..\scripts\zone\dun_morogh\dun_morogh.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Durotar">
+ </Filter>
+ <Filter
+ Name="Duskwood">
+ </Filter>
+ <Filter
+ Name="Dustwallow Marsh">
+ <File
+ RelativePath="..\scripts\zone\dustwallow_marsh\dustwallow_marsh.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackwing Lair">
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_broodlord_lashlayer.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_chromaggus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_ebonroc.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_firemaw.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_flamegor.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_nefarian.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_razorgore.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_vaelastrasz.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_victor_nefarius.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\instance_blackwing_lair.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Bloodmyst Isle">
+ <File
+ RelativePath="..\scripts\zone\bloodmyst_isle\bloodmyst_isle.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Gruul&apos;s Lair">
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\boss_gruul.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\boss_high_king_maulgar.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\def_gruuls_lair.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\instance_gruuls_lair.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Burning Steppes">
+ <File
+ RelativePath="..\scripts\zone\burning_steppes\burning_steppes.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Darkshore">
+ <File
+ RelativePath="..\scripts\zone\darkshore\darkshore.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Eastern Plaguelands">
+ <File
+ RelativePath="..\scripts\zone\eastern_plaguelands\eastern_plaguelands.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Moonglade">
+ <File
+ RelativePath="..\scripts\zone\moonglade\moonglade.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Razorfen Kraul">
+ </Filter>
+ <Filter
+ Name="Redridge Mountains">
+ </Filter>
+ <Filter
+ Name="Ruins of Ahn&apos;Qiraj">
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ayamiss.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_buru.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_kurinnaxx.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_moam.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ossirian.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_rajaxx.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\instance_ruins_of_ahnqiraj.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Swamp of Sorrows">
+ </Filter>
+ <Filter
+ Name="Scarlet Monastery">
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_arcanist_doan.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_azshir_the_sleepless.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_bloodmage_thalnos.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_herod.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_fairbanks.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_whitemane.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_houndmaster_loksey.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_interrogator_vishas.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_scarlet_commander_mograine.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_scorn.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Scholomance">
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_darkmaster_gandling.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_death_knight_darkreaver.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_doctor_theolen_krastinov.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_illucia_barov.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_instructor_malicia.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_jandice_barov.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_kormok.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_lord_alexei_barov.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_lorekeeper_polkelt.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_ras_frostwhisper.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_the_ravenian.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_vectus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\def_scholomance.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\instance_scholomance.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Searing Gorge">
+ <File
+ RelativePath="..\scripts\zone\searing_gorge\searing_gorge.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Elwynn Forest">
+ <File
+ RelativePath="..\scripts\zone\elwynn_forest\elwynn_forest.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Felwood">
+ <File
+ RelativePath="..\scripts\zone\felwood\felwood.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Feralas">
+ <File
+ RelativePath="..\scripts\zone\feralas\feralas.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Gnomeregan">
+ </Filter>
+ <Filter
+ Name="Hillsbrad Foothills">
+ </Filter>
+ <Filter
+ Name="Hinterlands">
+ </Filter>
+ <Filter
+ Name="Maraudon">
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_celebras_the_cursed.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_landslide.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_noxxion.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_princess_theradras.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Molten Core">
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_baron_geddon.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_garr.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_gehennas.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_golemagg.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_lucifron.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_magmadar.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_majordomo_executus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_ragnaros.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_shazzrah.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_sulfuron_harbinger.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\def_molten_core.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\instance_molten_core.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\molten_core.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Mulgore">
+ <File
+ RelativePath="..\scripts\zone\mulgore\mulgore.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Naxxramas">
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_anubrekhan.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_faerlina.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_feugen.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_gluth.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_gothik.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_grobbulus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_heigan.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_highlord_mograine.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_kelthuzad.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_lady_blaumeux.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_loatheb.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_maexxna.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_noth.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_patchwerk.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_razuvious.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_sapphiron.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_sir_zeliek.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_stalagg.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_thaddius.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_thane_korthazz.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\instance_naxxramas.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Onyxia&apos;s Lair">
+ <File
+ RelativePath="..\scripts\zone\onyxias_lair\boss_onyxia.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Ragefire Chasm">
+ </Filter>
+ <Filter
+ Name="Razorfen Downs">
+ <File
+ RelativePath="..\scripts\zone\razorfen_downs\boss_amnennar_the_coldbringer.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Shadowfang Keep">
+ <File
+ RelativePath="..\scripts\zone\shadowfang_keep\def_shadowfang_keep.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\shadowfang_keep\instance_shadowfang_keep.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\shadowfang_keep\shadowfang_keep.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Stonetalon Mountains">
+ <File
+ RelativePath="..\scripts\zone\stonetalon_mountains\stonetalon_mountains.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Stranglethorn Vale">
+ <File
+ RelativePath="..\scripts\zone\stranglethorn_vale\stranglethorn_vale.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Stratholme">
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_baron_rivendare.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_baroness_anastari.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_cannon_master_willey.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_dathrohan_balnazzar.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_magistrate_barthilas.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_maleki_the_pallid.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_nerubenkan.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_order_of_silver_hand.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_postmaster_malown.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_ramstein_the_gorger.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_timmy_the_cruel.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\def_stratholme.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\instance_stratholme.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\stratholme.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Sunken Temple">
+ </Filter>
+ <Filter
+ Name="Tanaris">
+ <File
+ RelativePath="..\scripts\zone\tanaris\tanaris.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Teldrassil">
+ </Filter>
+ <Filter
+ Name="Temple of Ahn&apos;Qiraj">
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_bug_trio.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_cthun.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_fankriss.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_huhuran.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_ouro.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_sartura.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_skeram.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_twinemperors.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_viscidus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\def_temple_of_ahnqiraj.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\instance_temple_of_ahnqiraj.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\mob_anubisath_sentinel.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Thousand Needles">
+ </Filter>
+ <Filter
+ Name="Silithus">
+ <File
+ RelativePath="..\scripts\zone\silithus\silithus.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Silverpine Forest">
+ <File
+ RelativePath="..\scripts\zone\silverpine_forest\silverpine_forest.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Stockade">
+ </Filter>
+ <Filter
+ Name="Tirisfal Glades">
+ <File
+ RelativePath="..\scripts\zone\tirisfal_glades\tirisfal_glades.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Wailing Caverns">
+ <File
+ RelativePath="..\scripts\zone\wailing_caverns\instance_wailing_caverns.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Western Plaguelands">
+ <File
+ RelativePath="..\scripts\zone\western_plaguelands\western_plaguelands.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Westfall">
+ </Filter>
+ <Filter
+ Name="Wetlands">
+ </Filter>
+ <Filter
+ Name="Winterspring">
+ <File
+ RelativePath="..\scripts\zone\winterspring\winterspring.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Zul&apos;Farrak">
+ <File
+ RelativePath="..\scripts\zone\zulfarrak\zulfarrak.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Zul&apos;Gurub">
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_arlokk.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_gahzranka.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_grilek.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_hakkar.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_hazzarah.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_jeklik.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_jindo.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_mandokir.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_marli.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_renataki.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_thekal.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_venoxis.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_wushoolay.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\def_zulgurub.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\instance_zulgurub.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Uldaman">
+ <File
+ RelativePath="..\scripts\zone\uldaman\boss_ironaya.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\uldaman\uldaman.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Un&apos;Goro Crater">
+ </Filter>
+ <Filter
+ Name="Aunchindoun">
+ <Filter
+ Name="Auchenai Crypts">
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\auchenai_crypts\boss_exarch_maladaar.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Mana Tombs">
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_nexusprince_shaffar.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_pandemonius.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Sethekk Halls">
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_darkweaver_syth.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_tailonking_ikiss.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\def_sethekk_halls.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\instance_sethekk_halls.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Shadow Labyrinth">
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_ambassador_hellmaw.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_blackheart_the_inciter.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_grandmaster_vorpil.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_murmur.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\def_shadow_labyrinth.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\instance_shadow_labyrinth.cpp">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Azuremyst Isle">
+ <File
+ RelativePath="..\scripts\zone\azuremyst_isle\azuremyst_isle.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Black Temple">
+ <File
+ RelativePath="..\scripts\zone\black_temple\black_temple.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_bloodboil.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_illidan.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_mother_shahraz.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_reliquary_of_souls.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_shade_of_akama.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_supremus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_teron_gorefiend.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_warlord_najentus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\def_black_temple.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\illidari_council.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\instance_black_temple.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Eversong Woods">
+ <File
+ RelativePath="..\scripts\zone\eversong_woods\eversong_woods.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Ghostlands">
+ <File
+ RelativePath="..\scripts\zone\ghostlands\ghostlands.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Blade&apos;s Edge Mountains">
+ <File
+ RelativePath="..\scripts\zone\blades_edge_mountains\blades_edge_mountains.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Blasted Lands">
+ <File
+ RelativePath="..\scripts\zone\blasted_lands\blasted_lands.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blasted_lands\boss_kruul.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Hellfire Citadel">
+ <Filter
+ Name="Blood Furnace">
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_broggok.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_kelidan_the_breaker.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_the_maker.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Magtheridon&apos;s lair">
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\boss_magtheridon.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\def_magtheridons_lair.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\instance_magtheridons_lair.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Hellfire Ramparts">
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_omor_the_unscarred.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_watchkeeper_gargolmar.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Shattered Halls">
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_nethekurse.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_warbringer_omrogg.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\def_shattered_halls.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\instance_shattered_halls.cpp">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Hellfire Peninsula">
+ <File
+ RelativePath="..\scripts\zone\hellfire_peninsula\boss_doomlord_kazzak.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_peninsula\hellfire_peninsula.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Karazhan">
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_curator.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_maiden_of_virtue.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_midnight.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_moroes.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_netherspite.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_nightbane.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_prince_malchezaar.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_shade_of_aran.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_terestian_illhoof.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\bosses_opera.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\def_karazhan.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\instance_karazhan.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\karazhan.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Nagrand">
+ <File
+ RelativePath="..\scripts\zone\nagrand\nagrand.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Netherstorm">
+ <File
+ RelativePath="..\scripts\zone\netherstorm\netherstorm.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Shadowmoon Valley">
+ <File
+ RelativePath="..\scripts\zone\shadowmoon_valley\boss_doomwalker.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\shadowmoon_valley\shadowmoon_valley.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Tempest Keep">
+ <Filter
+ Name="Arcatraz">
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\arcatraz.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\boss_harbinger_skyriss.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\def_arcatraz.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\instance_arcatraz.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Botanica">
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\botanica\boss_high_botanist_freywinn.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\botanica\boss_laj.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\botanica\boss_warp_splinter.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="The Eye">
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_astromancer.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_kaelthas.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_void_reaver.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\def_the_eye.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\instance_the_eye.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\the_eye.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="The Mechanar">
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_gyrokill.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_ironhand.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_nethermancer_sepethrea.cpp">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Terokkar Forest">
+ <File
+ RelativePath="..\scripts\zone\terokkar_forest\terokkar_forest.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Zangarmarsh">
+ <File
+ RelativePath="..\scripts\zone\zangarmarsh\zangarmarsh.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackrock Spire">
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_drakkisath.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_gyth.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_halycon.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_highlord_omokk.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_mother_smolderweb.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_overlord_wyrmthalak.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_pyroguard_emberseer.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_quartermaster_zigris.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_rend_blackhand.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_shadow_hunter_voshgajin.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_the_beast.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_warmaster_voone.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Stormwind City">
+ <File
+ RelativePath="..\scripts\zone\stormwind\stormwind_city.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Coilfang Resevoir">
+ <Filter
+ Name="Serpent Shrine Cavern">
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_fathomlord_karathress.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_hydross_the_unstable.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_lady_vashj.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_leotheras_the_blind.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_morogrim_tidewalker.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\def_serpent_shrine.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\instance_serpent_shrine.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Slave Pens">
+ </Filter>
+ <Filter
+ Name="Steam Vault">
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_hydromancer_thespia.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_mekgineer_steamrigger.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_warlord_kalithresh.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\def_steam_vault.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\instance_steam_vault.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Underbog">
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\underbog\boss_hungarfen.cpp">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Caverns of Time">
+ <Filter
+ Name="The Dark Portal">
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_aeonus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_chrono_lord_deja.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_temporus.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Battle for Mt. Hyjal">
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\boss_archimonde.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\def_hyjal.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjal.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\instance_hyjal.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Old Hillsbrad">
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_captain_skarloc.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_epoch_hunter.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_leutenant_drake.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\def_old_hillsbrad.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\instance_old_hillsbrad.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\old_hillsbrad.cpp">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Silvermoon City">
+ <File
+ RelativePath="..\scripts\zone\silvermoon\silvermoon_city.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Darnassus">
+ </Filter>
+ <Filter
+ Name="Exodar">
+ </Filter>
+ <Filter
+ Name="Iron Forge">
+ <File
+ RelativePath="..\scripts\zone\ironforge\ironforge.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Orgrimmar">
+ <File
+ RelativePath="..\scripts\zone\orgrimmar\orgrimmar.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Shattrath City">
+ <File
+ RelativePath="..\scripts\zone\shattrath\shattrath_city.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Thunder Bluff">
+ <File
+ RelativePath="..\scripts\zone\thunder_bluff\thunder_bluff.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Undercity">
+ <File
+ RelativePath="..\scripts\zone\undercity\undercity.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Zul&apos;Aman">
+ <File
+ RelativePath="..\scripts\zone\zulaman\boss_janalai.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\boss_nalorakk.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\def_zulaman.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\instance_zulaman.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\zulaman.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Isle of Quel&apos;Danas">
+ <File
+ RelativePath="..\scripts\zone\isle_of_queldanas\isle_of_queldanas.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Magister&apos;s Terrace">
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_felblood_kaelthas.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_priestess_delrissa.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_selin_fireheart.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_vexallus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\def_magisters_terrace.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\instance_magisters_terrace.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Sunwell Plateau">
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\boss_brutallus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\boss_kalecgos.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\def_sunwell_plateau.h">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\instance_sunwell_plateau.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackrock Depths">
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\blackrock_depths.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_ambassador_flamelash.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_angerrel.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_anubshiah.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_doomrel.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_doperel.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_emperor_dagran_thaurissan.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_general_angerforge.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_gloomrel.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_gorosh_the_dervish.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_grizzle.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_haterel.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_high_interrogator_gerstahn.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_magmus.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_moira_bronzebeard.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_seethrel.cpp">
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_vilerel.cpp">
+ </File>
+ </Filter>
+ <Filter
+ Name="Loch Modan">
+ <File
+ RelativePath="..\scripts\zone\loch_modan\loch_modan.cpp">
+ </File>
+ </Filter>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Include">
+ <File
+ RelativePath="..\include\precompiled.cpp">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\include\precompiled.h">
+ </File>
+ <File
+ RelativePath="..\include\sc_creature.cpp">
+ </File>
+ <File
+ RelativePath="..\include\sc_creature.h">
+ </File>
+ <File
+ RelativePath="..\include\sc_gossip.h">
+ </File>
+ <File
+ RelativePath="..\include\sc_instance.h">
+ </File>
+ <File
+ RelativePath="..\include\sc_item.h">
+ </File>
+ </Filter>
+ <File
+ RelativePath="..\config.h">
+ </File>
+ <File
+ RelativePath="..\ScriptMgr.cpp">
+ </File>
+ <File
+ RelativePath="..\ScriptMgr.h">
+ </File>
+ <File
+ RelativePath="..\svn_revision.h">
+ </File>
+ <File
+ RelativePath="..\system.cpp">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/bindings/scripts/VC80/80ScriptDev2.vcproj b/src/bindings/scripts/VC80/80ScriptDev2.vcproj
index d23e8dcbc7d..79470fefa28 100644
--- a/src/bindings/scripts/VC80/80ScriptDev2.vcproj
+++ b/src/bindings/scripts/VC80/80ScriptDev2.vcproj
@@ -1,2343 +1,2343 @@
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="8,00"
- Name="TrinityScript"
- ProjectGUID="{4295C8A9-79B7-4354-8064-F05FB9CA0C96}"
- RootNamespace="ScriptDev2"
- Keyword="Win32Proj"
- >
- <Platforms>
- <Platform
- Name="Win32"
- />
- <Platform
- Name="x64"
- />
- </Platforms>
- <ToolFiles>
- </ToolFiles>
- <Configurations>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
- ConfigurationType="2"
- InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- CommandLine=""
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
- PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT"
- MinimalRebuild="true"
- BasicRuntimeChecks="3"
- RuntimeLibrary="1"
- UsePrecompiledHeader="2"
- PrecompiledHeaderThrough="precompiled.h"
- WarningLevel="3"
- Detect64BitPortabilityProblems="true"
- DebugInformationFormat="4"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="TrinityCore.lib zthread.lib"
- OutputFile="$(OutDir)/TrinityScript.dll"
- LinkIncremental="2"
- AdditionalLibraryDirectories="..\..\..\..\win\VC80\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC80\trinitycore__$(PlatformName)_$(ConfigurationName)"
- GenerateDebugInformation="true"
- ProgramDatabaseFile="$(OutDir)/MaNGOSScript.pdb"
- SubSystem="2"
- ImportLibrary="$(OutDir)/TrinityScript.lib"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
- />
- </Configuration>
- <Configuration
- Name="Debug|x64"
- OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
- ConfigurationType="2"
- InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- CommandLine=""
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- TargetEnvironment="3"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
- PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT"
- MinimalRebuild="true"
- BasicRuntimeChecks="3"
- RuntimeLibrary="1"
- UsePrecompiledHeader="2"
- PrecompiledHeaderThrough="precompiled.h"
- WarningLevel="3"
- Detect64BitPortabilityProblems="true"
- DebugInformationFormat="3"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="TrinityCore.lib zthread.lib"
- OutputFile="$(OutDir)/TrinityScript.dll"
- LinkIncremental="2"
- AdditionalLibraryDirectories="..\..\..\..\win\VC80\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC80\trinitycore__$(PlatformName)_$(ConfigurationName)"
- GenerateDebugInformation="true"
- ProgramDatabaseFile="$(OutDir)/MaNGOSScript.pdb"
- SubSystem="2"
- ImportLibrary="$(OutDir)/TrinityScript.lib"
- TargetMachine="17"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
- />
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
- ConfigurationType="2"
- InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- CommandLine=""
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/MP"
- AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
- PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT"
- RuntimeLibrary="0"
- EnableEnhancedInstructionSet="1"
- FloatingPointModel="2"
- UsePrecompiledHeader="2"
- PrecompiledHeaderThrough="precompiled.h"
- WarningLevel="3"
- Detect64BitPortabilityProblems="true"
- DebugInformationFormat="3"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="TrinityCore.lib zthread.lib"
- OutputFile="$(OutDir)/TrinityScript.dll"
- LinkIncremental="1"
- AdditionalLibraryDirectories="..\..\..\..\win\VC80\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC80\trinitycore__$(PlatformName)_$(ConfigurationName)"
- GenerateDebugInformation="false"
- SubSystem="2"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- ImportLibrary="$(OutDir)/TrinityScript.lib"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
- />
- </Configuration>
- <Configuration
- Name="Release|x64"
- OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
- ConfigurationType="2"
- InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- CommandLine=""
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- TargetEnvironment="3"
- />
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/MP"
- AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
- PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT"
- RuntimeLibrary="0"
- FloatingPointModel="2"
- UsePrecompiledHeader="2"
- PrecompiledHeaderThrough="precompiled.h"
- WarningLevel="3"
- Detect64BitPortabilityProblems="true"
- DebugInformationFormat="3"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="TrinityCore.lib zthread.lib"
- OutputFile="$(OutDir)/TrinityScript.dll"
- LinkIncremental="1"
- AdditionalLibraryDirectories="..\..\..\..\win\VC80\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC80\trinitycore__$(PlatformName)_$(ConfigurationName)"
- GenerateDebugInformation="false"
- SubSystem="2"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- ImportLibrary="$(OutDir)/TrinityScript.lib"
- TargetMachine="17"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
- />
- </Configuration>
- </Configurations>
- <References>
- </References>
- <Files>
- <Filter
- Name="Scripts"
- >
- <Filter
- Name="boss"
- >
- <File
- RelativePath="..\scripts\boss\boss_emeriss.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\boss\boss_lethon.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\boss\boss_taerar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\boss\boss_ysondre.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="creature"
- >
- <File
- RelativePath="..\scripts\creature\mob_event_ai.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\creature\mob_event_ai.h"
- >
- </File>
- <File
- RelativePath="..\scripts\creature\mob_generic_creature.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\creature\simple_ai.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\creature\simple_ai.h"
- >
- </File>
- </Filter>
- <Filter
- Name="guard"
- >
- <File
- RelativePath="..\scripts\guard\guard_ai.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\guard\guard_ai.h"
- >
- </File>
- <File
- RelativePath="..\scripts\guard\guards.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="honor"
- >
- </Filter>
- <Filter
- Name="npc"
- >
- <File
- RelativePath="..\scripts\npc\npc_escortAI.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\npc\npc_escortAI.h"
- >
- </File>
- <File
- RelativePath="..\scripts\npc\npc_innkeeper.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\npc\npc_professions.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\npc\npcs_special.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="servers"
- >
- </Filter>
- <Filter
- Name="custom"
- >
- <File
- RelativePath="..\scripts\custom\custom_example.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\custom\custom_gossip_codebox.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\custom\test.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="areatrigger"
- >
- <File
- RelativePath="..\scripts\areatrigger\areatrigger_scripts.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="go"
- >
- <File
- RelativePath="..\scripts\go\go_scripts.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="item"
- >
- <File
- RelativePath="..\scripts\item\item_scripts.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\item\item_test.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="zone"
- >
- <Filter
- Name="Alterac Mountains"
- >
- <File
- RelativePath="..\scripts\zone\alterac_mountains\alterac_mountains.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Ashenvale Forest"
- >
- </Filter>
- <Filter
- Name="Azshara"
- >
- <File
- RelativePath="..\scripts\zone\azshara\azshara.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\azshara\boss_azuregos.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Badlands"
- >
- </Filter>
- <Filter
- Name="Barrens"
- >
- <File
- RelativePath="..\scripts\zone\barrens\the_barrens.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blackfathom Depths"
- >
- </Filter>
- <Filter
- Name="Arathi Highlands"
- >
- </Filter>
- <Filter
- Name="Deadmines"
- >
- <File
- RelativePath="..\scripts\zone\deadmines\deadmines.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Deadwind Pass"
- >
- </Filter>
- <Filter
- Name="Desolace"
- >
- </Filter>
- <Filter
- Name="Dire Maul"
- >
- </Filter>
- <Filter
- Name="Dun Morogh"
- >
- <File
- RelativePath="..\scripts\zone\dun_morogh\dun_morogh.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Durotar"
- >
- </Filter>
- <Filter
- Name="Duskwood"
- >
- </Filter>
- <Filter
- Name="Dustwallow Marsh"
- >
- <File
- RelativePath="..\scripts\zone\dustwallow_marsh\dustwallow_marsh.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blackwing Lair"
- >
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_broodlord_lashlayer.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_chromaggus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_ebonroc.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_firemaw.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_flamegor.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_nefarian.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_razorgore.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_vaelastrasz.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_victor_nefarius.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\instance_blackwing_lair.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Bloodmyst Isle"
- >
- <File
- RelativePath="..\scripts\zone\bloodmyst_isle\bloodmyst_isle.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Gruul&apos;s Lair"
- >
- <File
- RelativePath="..\scripts\zone\gruuls_lair\boss_gruul.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\gruuls_lair\boss_high_king_maulgar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\gruuls_lair\def_gruuls_lair.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\gruuls_lair\instance_gruuls_lair.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Burning Steppes"
- >
- <File
- RelativePath="..\scripts\zone\burning_steppes\burning_steppes.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Darkshore"
- >
- <File
- RelativePath="..\scripts\zone\darkshore\darkshore.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Eastern Plaguelands"
- >
- <File
- RelativePath="..\scripts\zone\eastern_plaguelands\eastern_plaguelands.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Moonglade"
- >
- <File
- RelativePath="..\scripts\zone\moonglade\moonglade.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Razorfen Kraul"
- >
- </Filter>
- <Filter
- Name="Redridge Mountains"
- >
- </Filter>
- <Filter
- Name="Ruins of Ahn&apos;Qiraj"
- >
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ayamiss.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_buru.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_kurinnaxx.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_moam.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ossirian.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_rajaxx.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\instance_ruins_of_ahnqiraj.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Swamp of Sorrows"
- >
- </Filter>
- <Filter
- Name="Scarlet Monastery"
- >
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_arcanist_doan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_azshir_the_sleepless.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_bloodmage_thalnos.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_herod.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_fairbanks.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_whitemane.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_houndmaster_loksey.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_interrogator_vishas.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_scarlet_commander_mograine.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_scorn.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Scholomance"
- >
- <File
- RelativePath="..\scripts\zone\scholomance\boss_darkmaster_gandling.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_death_knight_darkreaver.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_doctor_theolen_krastinov.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_illucia_barov.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_instructor_malicia.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_jandice_barov.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_kormok.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_lord_alexei_barov.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_lorekeeper_polkelt.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_ras_frostwhisper.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_the_ravenian.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_vectus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\def_scholomance.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\instance_scholomance.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Searing Gorge"
- >
- <File
- RelativePath="..\scripts\zone\searing_gorge\searing_gorge.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Elwynn Forest"
- >
- <File
- RelativePath="..\scripts\zone\elwynn_forest\elwynn_forest.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Felwood"
- >
- <File
- RelativePath="..\scripts\zone\felwood\felwood.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Feralas"
- >
- <File
- RelativePath="..\scripts\zone\feralas\feralas.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Gnomeregan"
- >
- </Filter>
- <Filter
- Name="Hillsbrad Foothills"
- >
- </Filter>
- <Filter
- Name="Hinterlands"
- >
- </Filter>
- <Filter
- Name="Maraudon"
- >
- <File
- RelativePath="..\scripts\zone\maraudon\boss_celebras_the_cursed.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\maraudon\boss_landslide.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\maraudon\boss_noxxion.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\maraudon\boss_princess_theradras.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Molten Core"
- >
- <File
- RelativePath="..\scripts\zone\molten_core\boss_baron_geddon.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_garr.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_gehennas.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_golemagg.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_lucifron.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_magmadar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_majordomo_executus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_ragnaros.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_shazzrah.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_sulfuron_harbinger.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\def_molten_core.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\instance_molten_core.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\molten_core.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Mulgore"
- >
- <File
- RelativePath="..\scripts\zone\mulgore\mulgore.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Naxxramas"
- >
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_anubrekhan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_faerlina.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_feugen.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_gluth.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_gothik.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_grobbulus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_heigan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_highlord_mograine.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_kelthuzad.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_lady_blaumeux.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_loatheb.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_maexxna.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_noth.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_patchwerk.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_razuvious.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_sapphiron.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_sir_zeliek.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_stalagg.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_thaddius.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_thane_korthazz.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\instance_naxxramas.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Onyxia&apos;s Lair"
- >
- <File
- RelativePath="..\scripts\zone\onyxias_lair\boss_onyxia.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Ragefire Chasm"
- >
- </Filter>
- <Filter
- Name="Razorfen Downs"
- >
- <File
- RelativePath="..\scripts\zone\razorfen_downs\boss_amnennar_the_coldbringer.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Shadowfang Keep"
- >
- <File
- RelativePath="..\scripts\zone\shadowfang_keep\def_shadowfang_keep.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\shadowfang_keep\instance_shadowfang_keep.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\shadowfang_keep\shadowfang_keep.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Stonetalon Mountains"
- >
- <File
- RelativePath="..\scripts\zone\stonetalon_mountains\stonetalon_mountains.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Stranglethorn Vale"
- >
- <File
- RelativePath="..\scripts\zone\stranglethorn_vale\stranglethorn_vale.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Stratholme"
- >
- <File
- RelativePath="..\scripts\zone\stratholme\boss_baron_rivendare.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_baroness_anastari.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_cannon_master_willey.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_dathrohan_balnazzar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_magistrate_barthilas.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_maleki_the_pallid.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_nerubenkan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_order_of_silver_hand.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_postmaster_malown.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_ramstein_the_gorger.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_timmy_the_cruel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\def_stratholme.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\instance_stratholme.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\stratholme.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Sunken Temple"
- >
- </Filter>
- <Filter
- Name="Tanaris"
- >
- <File
- RelativePath="..\scripts\zone\tanaris\tanaris.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Teldrassil"
- >
- </Filter>
- <Filter
- Name="Temple of Ahn&apos;Qiraj"
- >
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_bug_trio.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_cthun.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_fankriss.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_huhuran.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_ouro.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_sartura.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_skeram.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_twinemperors.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_viscidus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\def_temple_of_ahnqiraj.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\instance_temple_of_ahnqiraj.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\mob_anubisath_sentinel.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Thousand Needles"
- >
- </Filter>
- <Filter
- Name="Silithus"
- >
- <File
- RelativePath="..\scripts\zone\silithus\silithus.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Silverpine Forest"
- >
- <File
- RelativePath="..\scripts\zone\silverpine_forest\silverpine_forest.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Stockade"
- >
- </Filter>
- <Filter
- Name="Tirisfal Glades"
- >
- <File
- RelativePath="..\scripts\zone\tirisfal_glades\tirisfal_glades.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Wailing Caverns"
- >
- <File
- RelativePath="..\scripts\zone\wailing_caverns\instance_wailing_caverns.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Western Plaguelands"
- >
- <File
- RelativePath="..\scripts\zone\western_plaguelands\western_plaguelands.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Westfall"
- >
- </Filter>
- <Filter
- Name="Wetlands"
- >
- </Filter>
- <Filter
- Name="Winterspring"
- >
- <File
- RelativePath="..\scripts\zone\winterspring\winterspring.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Zul&apos;Farrak"
- >
- <File
- RelativePath="..\scripts\zone\zulfarrak\zulfarrak.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Zul&apos;Gurub"
- >
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_arlokk.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_gahzranka.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_grilek.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_hakkar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_hazzarah.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_jeklik.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_jindo.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_mandokir.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_marli.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_renataki.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_thekal.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_venoxis.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_wushoolay.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\def_zulgurub.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\instance_zulgurub.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Uldaman"
- >
- <File
- RelativePath="..\scripts\zone\uldaman\boss_ironaya.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\uldaman\uldaman.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Un&apos;Goro Crater"
- >
- </Filter>
- <Filter
- Name="Aunchindoun"
- >
- <Filter
- Name="Auchenai Crypts"
- >
- <File
- RelativePath="..\scripts\zone\aunchindoun\auchenai_crypts\boss_exarch_maladaar.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Mana Tombs"
- >
- <File
- RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_nexusprince_shaffar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_pandemonius.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Sethekk Halls"
- >
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_darkweaver_syth.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_tailonking_ikiss.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\def_sethekk_halls.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\instance_sethekk_halls.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Shadow Labyrinth"
- >
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_ambassador_hellmaw.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_blackheart_the_inciter.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_grandmaster_vorpil.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_murmur.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\def_shadow_labyrinth.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\instance_shadow_labyrinth.cpp"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Azuremyst Isle"
- >
- <File
- RelativePath="..\scripts\zone\azuremyst_isle\azuremyst_isle.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Black Temple"
- >
- <File
- RelativePath="..\scripts\zone\black_temple\black_temple.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_bloodboil.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_illidan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_mother_shahraz.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_reliquary_of_souls.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_shade_of_akama.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_supremus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_teron_gorefiend.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_warlord_najentus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\def_black_temple.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\illidari_council.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\instance_black_temple.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Eversong Woods"
- >
- <File
- RelativePath="..\scripts\zone\eversong_woods\eversong_woods.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Ghostlands"
- >
- <File
- RelativePath="..\scripts\zone\ghostlands\ghostlands.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blade&apos;s Edge Mountains"
- >
- <File
- RelativePath="..\scripts\zone\blades_edge_mountains\blades_edge_mountains.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blasted Lands"
- >
- <File
- RelativePath="..\scripts\zone\blasted_lands\blasted_lands.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blasted_lands\boss_kruul.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Hellfire Citadel"
- >
- <Filter
- Name="Blood Furnace"
- >
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_broggok.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_kelidan_the_breaker.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_the_maker.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Magtheridon&apos;s lair"
- >
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\boss_magtheridon.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\def_magtheridons_lair.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\instance_magtheridons_lair.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Hellfire Ramparts"
- >
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_omor_the_unscarred.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_watchkeeper_gargolmar.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Shattered Halls"
- >
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_nethekurse.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_warbringer_omrogg.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\def_shattered_halls.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\instance_shattered_halls.cpp"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Hellfire Peninsula"
- >
- <File
- RelativePath="..\scripts\zone\hellfire_peninsula\boss_doomlord_kazzak.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_peninsula\hellfire_peninsula.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Karazhan"
- >
- <File
- RelativePath="..\scripts\zone\karazhan\boss_curator.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_maiden_of_virtue.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_midnight.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_moroes.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_netherspite.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_nightbane.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_prince_malchezaar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_shade_of_aran.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_terestian_illhoof.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\bosses_opera.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\def_karazhan.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\instance_karazhan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\karazhan.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Nagrand"
- >
- <File
- RelativePath="..\scripts\zone\nagrand\nagrand.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Netherstorm"
- >
- <File
- RelativePath="..\scripts\zone\netherstorm\netherstorm.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Shadowmoon Valley"
- >
- <File
- RelativePath="..\scripts\zone\shadowmoon_valley\boss_doomwalker.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\shadowmoon_valley\shadowmoon_valley.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Tempest Keep"
- >
- <Filter
- Name="Arcatraz"
- >
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\arcatraz.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\boss_harbinger_skyriss.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\def_arcatraz.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\instance_arcatraz.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Botanica"
- >
- <File
- RelativePath="..\scripts\zone\tempest_keep\botanica\boss_high_botanist_freywinn.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\botanica\boss_laj.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\botanica\boss_warp_splinter.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="The Eye"
- >
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_astromancer.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_kaelthas.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_void_reaver.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\def_the_eye.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\instance_the_eye.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\the_eye.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="The Mechanar"
- >
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_gyrokill.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_ironhand.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_nethermancer_sepethrea.cpp"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Terokkar Forest"
- >
- <File
- RelativePath="..\scripts\zone\terokkar_forest\terokkar_forest.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Zangarmarsh"
- >
- <File
- RelativePath="..\scripts\zone\zangarmarsh\zangarmarsh.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blackrock Spire"
- >
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_drakkisath.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_gyth.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_halycon.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_highlord_omokk.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_mother_smolderweb.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_overlord_wyrmthalak.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_pyroguard_emberseer.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_quartermaster_zigris.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_rend_blackhand.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_shadow_hunter_voshgajin.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_the_beast.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_warmaster_voone.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Stormwind City"
- >
- <File
- RelativePath="..\scripts\zone\stormwind\stormwind_city.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Coilfang Resevoir"
- >
- <Filter
- Name="Serpent Shrine Cavern"
- >
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_fathomlord_karathress.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_hydross_the_unstable.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_lady_vashj.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_leotheras_the_blind.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_morogrim_tidewalker.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\def_serpent_shrine.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\instance_serpent_shrine.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Slave Pens"
- >
- </Filter>
- <Filter
- Name="Steam Vault"
- >
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_hydromancer_thespia.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_mekgineer_steamrigger.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_warlord_kalithresh.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\def_steam_vault.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\instance_steam_vault.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Underbog"
- >
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\underbog\boss_hungarfen.cpp"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Caverns of Time"
- >
- <Filter
- Name="The Dark Portal"
- >
- <File
- RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_aeonus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_chrono_lord_deja.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_temporus.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Battle for Mt. Hyjal"
- >
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\boss_archimonde.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\def_hyjal.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjal.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\instance_hyjal.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Old Hillsbrad"
- >
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_captain_skarloc.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_epoch_hunter.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_leutenant_drake.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\def_old_hillsbrad.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\instance_old_hillsbrad.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\old_hillsbrad.cpp"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Silvermoon City"
- >
- <File
- RelativePath="..\scripts\zone\silvermoon\silvermoon_city.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Darnassus"
- >
- </Filter>
- <Filter
- Name="Exodar"
- >
- </Filter>
- <Filter
- Name="Iron Forge"
- >
- <File
- RelativePath="..\scripts\zone\ironforge\ironforge.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Orgrimmar"
- >
- <File
- RelativePath="..\scripts\zone\orgrimmar\orgrimmar.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Shattrath City"
- >
- <File
- RelativePath="..\scripts\zone\shattrath\shattrath_city.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Thunder Bluff"
- >
- <File
- RelativePath="..\scripts\zone\thunder_bluff\thunder_bluff.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Undercity"
- >
- <File
- RelativePath="..\scripts\zone\undercity\undercity.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Zul&apos;Aman"
- >
- <File
- RelativePath="..\scripts\zone\zulaman\boss_janalai.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\boss_nalorakk.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\def_zulaman.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\instance_zulaman.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\zulaman.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Isle of Quel&apos;Danas"
- >
- <File
- RelativePath="..\scripts\zone\isle_of_queldanas\isle_of_queldanas.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Magister&apos;s Terrace"
- >
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_felblood_kaelthas.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_priestess_delrissa.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_selin_fireheart.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_vexallus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\def_magisters_terrace.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\instance_magisters_terrace.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Sunwell Plateau"
- >
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\boss_brutallus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\boss_kalecgos.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\def_sunwell_plateau.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\instance_sunwell_plateau.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blackrock Depths"
- >
- <File
- RelativePath="..\scripts\zone\blackrock_depths\blackrock_depths.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_ambassador_flamelash.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_angerrel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_anubshiah.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_doomrel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_doperel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_emperor_dagran_thaurissan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_general_angerforge.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_gloomrel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_gorosh_the_dervish.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_grizzle.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_haterel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_high_interrogator_gerstahn.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_magmus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_moira_bronzebeard.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_seethrel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_vilerel.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Loch Modan"
- >
- <File
- RelativePath="..\scripts\zone\loch_modan\loch_modan.cpp"
- >
- </File>
- </Filter>
- </Filter>
- </Filter>
- <Filter
- Name="Include"
- >
- <File
- RelativePath="..\include\precompiled.cpp"
- >
- <FileConfiguration
- Name="Debug|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="1"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|x64"
- >
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="1"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="1"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release|x64"
- >
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="1"
- />
- </FileConfiguration>
- </File>
- <File
- RelativePath="..\include\precompiled.h"
- >
- </File>
- <File
- RelativePath="..\include\sc_creature.cpp"
- >
- </File>
- <File
- RelativePath="..\include\sc_creature.h"
- >
- </File>
- <File
- RelativePath="..\include\sc_gossip.h"
- >
- </File>
- <File
- RelativePath="..\include\sc_instance.h"
- >
- </File>
- <File
- RelativePath="..\include\sc_item.h"
- >
- </File>
- </Filter>
- <File
- RelativePath="..\config.h"
- >
- </File>
- <File
- RelativePath="..\ScriptMgr.cpp"
- >
- </File>
- <File
- RelativePath="..\ScriptMgr.h"
- >
- </File>
- <File
- RelativePath="..\svn_revision.h"
- >
- </File>
- <File
- RelativePath="..\system.cpp"
- >
- </File>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="TrinityScript"
+ ProjectGUID="{4295C8A9-79B7-4354-8064-F05FB9CA0C96}"
+ RootNamespace="ScriptDev2"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
+ PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precompiled.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="TrinityCore.lib zthread.lib"
+ OutputFile="$(OutDir)/TrinityScript.dll"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="..\..\..\..\win\VC80\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC80\trinitycore__$(PlatformName)_$(ConfigurationName)"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/MaNGOSScript.pdb"
+ SubSystem="2"
+ ImportLibrary="$(OutDir)/TrinityScript.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
+ PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precompiled.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="TrinityCore.lib zthread.lib"
+ OutputFile="$(OutDir)/TrinityScript.dll"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="..\..\..\..\win\VC80\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC80\trinitycore__$(PlatformName)_$(ConfigurationName)"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/MaNGOSScript.pdb"
+ SubSystem="2"
+ ImportLibrary="$(OutDir)/TrinityScript.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT"
+ RuntimeLibrary="0"
+ EnableEnhancedInstructionSet="1"
+ FloatingPointModel="2"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precompiled.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="TrinityCore.lib zthread.lib"
+ OutputFile="$(OutDir)/TrinityScript.dll"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="..\..\..\..\win\VC80\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC80\trinitycore__$(PlatformName)_$(ConfigurationName)"
+ GenerateDebugInformation="false"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ ImportLibrary="$(OutDir)/TrinityScript.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT"
+ RuntimeLibrary="0"
+ FloatingPointModel="2"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precompiled.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="TrinityCore.lib zthread.lib"
+ OutputFile="$(OutDir)/TrinityScript.dll"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="..\..\..\..\win\VC80\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC80\trinitycore__$(PlatformName)_$(ConfigurationName)"
+ GenerateDebugInformation="false"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ ImportLibrary="$(OutDir)/TrinityScript.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Scripts"
+ >
+ <Filter
+ Name="boss"
+ >
+ <File
+ RelativePath="..\scripts\boss\boss_emeriss.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\boss\boss_lethon.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\boss\boss_taerar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\boss\boss_ysondre.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="creature"
+ >
+ <File
+ RelativePath="..\scripts\creature\mob_event_ai.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\creature\mob_event_ai.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\creature\mob_generic_creature.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\creature\simple_ai.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\creature\simple_ai.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="guard"
+ >
+ <File
+ RelativePath="..\scripts\guard\guard_ai.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\guard\guard_ai.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\guard\guards.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="honor"
+ >
+ </Filter>
+ <Filter
+ Name="npc"
+ >
+ <File
+ RelativePath="..\scripts\npc\npc_escortAI.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npc_escortAI.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npc_innkeeper.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npc_professions.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npcs_special.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="servers"
+ >
+ </Filter>
+ <Filter
+ Name="custom"
+ >
+ <File
+ RelativePath="..\scripts\custom\custom_example.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\custom\custom_gossip_codebox.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\custom\test.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="areatrigger"
+ >
+ <File
+ RelativePath="..\scripts\areatrigger\areatrigger_scripts.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="go"
+ >
+ <File
+ RelativePath="..\scripts\go\go_scripts.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="item"
+ >
+ <File
+ RelativePath="..\scripts\item\item_scripts.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\item\item_test.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="zone"
+ >
+ <Filter
+ Name="Alterac Mountains"
+ >
+ <File
+ RelativePath="..\scripts\zone\alterac_mountains\alterac_mountains.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Ashenvale Forest"
+ >
+ </Filter>
+ <Filter
+ Name="Azshara"
+ >
+ <File
+ RelativePath="..\scripts\zone\azshara\azshara.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\azshara\boss_azuregos.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Badlands"
+ >
+ </Filter>
+ <Filter
+ Name="Barrens"
+ >
+ <File
+ RelativePath="..\scripts\zone\barrens\the_barrens.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackfathom Depths"
+ >
+ </Filter>
+ <Filter
+ Name="Arathi Highlands"
+ >
+ </Filter>
+ <Filter
+ Name="Deadmines"
+ >
+ <File
+ RelativePath="..\scripts\zone\deadmines\deadmines.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Deadwind Pass"
+ >
+ </Filter>
+ <Filter
+ Name="Desolace"
+ >
+ </Filter>
+ <Filter
+ Name="Dire Maul"
+ >
+ </Filter>
+ <Filter
+ Name="Dun Morogh"
+ >
+ <File
+ RelativePath="..\scripts\zone\dun_morogh\dun_morogh.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Durotar"
+ >
+ </Filter>
+ <Filter
+ Name="Duskwood"
+ >
+ </Filter>
+ <Filter
+ Name="Dustwallow Marsh"
+ >
+ <File
+ RelativePath="..\scripts\zone\dustwallow_marsh\dustwallow_marsh.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackwing Lair"
+ >
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_broodlord_lashlayer.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_chromaggus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_ebonroc.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_firemaw.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_flamegor.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_nefarian.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_razorgore.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_vaelastrasz.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_victor_nefarius.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\instance_blackwing_lair.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Bloodmyst Isle"
+ >
+ <File
+ RelativePath="..\scripts\zone\bloodmyst_isle\bloodmyst_isle.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Gruul&apos;s Lair"
+ >
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\boss_gruul.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\boss_high_king_maulgar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\def_gruuls_lair.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\instance_gruuls_lair.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Burning Steppes"
+ >
+ <File
+ RelativePath="..\scripts\zone\burning_steppes\burning_steppes.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Darkshore"
+ >
+ <File
+ RelativePath="..\scripts\zone\darkshore\darkshore.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Eastern Plaguelands"
+ >
+ <File
+ RelativePath="..\scripts\zone\eastern_plaguelands\eastern_plaguelands.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Moonglade"
+ >
+ <File
+ RelativePath="..\scripts\zone\moonglade\moonglade.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Razorfen Kraul"
+ >
+ </Filter>
+ <Filter
+ Name="Redridge Mountains"
+ >
+ </Filter>
+ <Filter
+ Name="Ruins of Ahn&apos;Qiraj"
+ >
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ayamiss.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_buru.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_kurinnaxx.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_moam.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ossirian.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_rajaxx.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\instance_ruins_of_ahnqiraj.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Swamp of Sorrows"
+ >
+ </Filter>
+ <Filter
+ Name="Scarlet Monastery"
+ >
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_arcanist_doan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_azshir_the_sleepless.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_bloodmage_thalnos.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_herod.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_fairbanks.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_whitemane.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_houndmaster_loksey.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_interrogator_vishas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_scarlet_commander_mograine.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_scorn.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Scholomance"
+ >
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_darkmaster_gandling.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_death_knight_darkreaver.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_doctor_theolen_krastinov.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_illucia_barov.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_instructor_malicia.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_jandice_barov.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_kormok.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_lord_alexei_barov.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_lorekeeper_polkelt.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_ras_frostwhisper.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_the_ravenian.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_vectus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\def_scholomance.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\instance_scholomance.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Searing Gorge"
+ >
+ <File
+ RelativePath="..\scripts\zone\searing_gorge\searing_gorge.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Elwynn Forest"
+ >
+ <File
+ RelativePath="..\scripts\zone\elwynn_forest\elwynn_forest.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Felwood"
+ >
+ <File
+ RelativePath="..\scripts\zone\felwood\felwood.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Feralas"
+ >
+ <File
+ RelativePath="..\scripts\zone\feralas\feralas.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Gnomeregan"
+ >
+ </Filter>
+ <Filter
+ Name="Hillsbrad Foothills"
+ >
+ </Filter>
+ <Filter
+ Name="Hinterlands"
+ >
+ </Filter>
+ <Filter
+ Name="Maraudon"
+ >
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_celebras_the_cursed.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_landslide.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_noxxion.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_princess_theradras.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Molten Core"
+ >
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_baron_geddon.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_garr.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_gehennas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_golemagg.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_lucifron.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_magmadar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_majordomo_executus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_ragnaros.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_shazzrah.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_sulfuron_harbinger.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\def_molten_core.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\instance_molten_core.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\molten_core.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Mulgore"
+ >
+ <File
+ RelativePath="..\scripts\zone\mulgore\mulgore.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Naxxramas"
+ >
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_anubrekhan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_faerlina.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_feugen.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_gluth.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_gothik.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_grobbulus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_heigan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_highlord_mograine.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_kelthuzad.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_lady_blaumeux.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_loatheb.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_maexxna.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_noth.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_patchwerk.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_razuvious.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_sapphiron.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_sir_zeliek.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_stalagg.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_thaddius.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_thane_korthazz.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\instance_naxxramas.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Onyxia&apos;s Lair"
+ >
+ <File
+ RelativePath="..\scripts\zone\onyxias_lair\boss_onyxia.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Ragefire Chasm"
+ >
+ </Filter>
+ <Filter
+ Name="Razorfen Downs"
+ >
+ <File
+ RelativePath="..\scripts\zone\razorfen_downs\boss_amnennar_the_coldbringer.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Shadowfang Keep"
+ >
+ <File
+ RelativePath="..\scripts\zone\shadowfang_keep\def_shadowfang_keep.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\shadowfang_keep\instance_shadowfang_keep.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\shadowfang_keep\shadowfang_keep.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Stonetalon Mountains"
+ >
+ <File
+ RelativePath="..\scripts\zone\stonetalon_mountains\stonetalon_mountains.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Stranglethorn Vale"
+ >
+ <File
+ RelativePath="..\scripts\zone\stranglethorn_vale\stranglethorn_vale.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Stratholme"
+ >
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_baron_rivendare.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_baroness_anastari.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_cannon_master_willey.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_dathrohan_balnazzar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_magistrate_barthilas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_maleki_the_pallid.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_nerubenkan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_order_of_silver_hand.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_postmaster_malown.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_ramstein_the_gorger.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_timmy_the_cruel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\def_stratholme.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\instance_stratholme.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\stratholme.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Sunken Temple"
+ >
+ </Filter>
+ <Filter
+ Name="Tanaris"
+ >
+ <File
+ RelativePath="..\scripts\zone\tanaris\tanaris.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Teldrassil"
+ >
+ </Filter>
+ <Filter
+ Name="Temple of Ahn&apos;Qiraj"
+ >
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_bug_trio.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_cthun.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_fankriss.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_huhuran.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_ouro.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_sartura.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_skeram.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_twinemperors.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_viscidus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\def_temple_of_ahnqiraj.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\instance_temple_of_ahnqiraj.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\mob_anubisath_sentinel.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Thousand Needles"
+ >
+ </Filter>
+ <Filter
+ Name="Silithus"
+ >
+ <File
+ RelativePath="..\scripts\zone\silithus\silithus.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Silverpine Forest"
+ >
+ <File
+ RelativePath="..\scripts\zone\silverpine_forest\silverpine_forest.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Stockade"
+ >
+ </Filter>
+ <Filter
+ Name="Tirisfal Glades"
+ >
+ <File
+ RelativePath="..\scripts\zone\tirisfal_glades\tirisfal_glades.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Wailing Caverns"
+ >
+ <File
+ RelativePath="..\scripts\zone\wailing_caverns\instance_wailing_caverns.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Western Plaguelands"
+ >
+ <File
+ RelativePath="..\scripts\zone\western_plaguelands\western_plaguelands.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Westfall"
+ >
+ </Filter>
+ <Filter
+ Name="Wetlands"
+ >
+ </Filter>
+ <Filter
+ Name="Winterspring"
+ >
+ <File
+ RelativePath="..\scripts\zone\winterspring\winterspring.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Zul&apos;Farrak"
+ >
+ <File
+ RelativePath="..\scripts\zone\zulfarrak\zulfarrak.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Zul&apos;Gurub"
+ >
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_arlokk.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_gahzranka.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_grilek.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_hakkar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_hazzarah.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_jeklik.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_jindo.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_mandokir.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_marli.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_renataki.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_thekal.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_venoxis.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_wushoolay.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\def_zulgurub.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\instance_zulgurub.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Uldaman"
+ >
+ <File
+ RelativePath="..\scripts\zone\uldaman\boss_ironaya.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\uldaman\uldaman.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Un&apos;Goro Crater"
+ >
+ </Filter>
+ <Filter
+ Name="Aunchindoun"
+ >
+ <Filter
+ Name="Auchenai Crypts"
+ >
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\auchenai_crypts\boss_exarch_maladaar.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Mana Tombs"
+ >
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_nexusprince_shaffar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_pandemonius.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Sethekk Halls"
+ >
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_darkweaver_syth.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_tailonking_ikiss.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\def_sethekk_halls.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\instance_sethekk_halls.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Shadow Labyrinth"
+ >
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_ambassador_hellmaw.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_blackheart_the_inciter.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_grandmaster_vorpil.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_murmur.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\def_shadow_labyrinth.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\instance_shadow_labyrinth.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Azuremyst Isle"
+ >
+ <File
+ RelativePath="..\scripts\zone\azuremyst_isle\azuremyst_isle.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Black Temple"
+ >
+ <File
+ RelativePath="..\scripts\zone\black_temple\black_temple.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_bloodboil.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_illidan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_mother_shahraz.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_reliquary_of_souls.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_shade_of_akama.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_supremus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_teron_gorefiend.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_warlord_najentus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\def_black_temple.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\illidari_council.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\instance_black_temple.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Eversong Woods"
+ >
+ <File
+ RelativePath="..\scripts\zone\eversong_woods\eversong_woods.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Ghostlands"
+ >
+ <File
+ RelativePath="..\scripts\zone\ghostlands\ghostlands.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blade&apos;s Edge Mountains"
+ >
+ <File
+ RelativePath="..\scripts\zone\blades_edge_mountains\blades_edge_mountains.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blasted Lands"
+ >
+ <File
+ RelativePath="..\scripts\zone\blasted_lands\blasted_lands.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blasted_lands\boss_kruul.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Hellfire Citadel"
+ >
+ <Filter
+ Name="Blood Furnace"
+ >
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_broggok.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_kelidan_the_breaker.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_the_maker.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Magtheridon&apos;s lair"
+ >
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\boss_magtheridon.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\def_magtheridons_lair.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\instance_magtheridons_lair.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Hellfire Ramparts"
+ >
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_omor_the_unscarred.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_watchkeeper_gargolmar.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Shattered Halls"
+ >
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_nethekurse.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_warbringer_omrogg.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\def_shattered_halls.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\instance_shattered_halls.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Hellfire Peninsula"
+ >
+ <File
+ RelativePath="..\scripts\zone\hellfire_peninsula\boss_doomlord_kazzak.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_peninsula\hellfire_peninsula.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Karazhan"
+ >
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_curator.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_maiden_of_virtue.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_midnight.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_moroes.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_netherspite.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_nightbane.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_prince_malchezaar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_shade_of_aran.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_terestian_illhoof.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\bosses_opera.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\def_karazhan.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\instance_karazhan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\karazhan.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Nagrand"
+ >
+ <File
+ RelativePath="..\scripts\zone\nagrand\nagrand.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Netherstorm"
+ >
+ <File
+ RelativePath="..\scripts\zone\netherstorm\netherstorm.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Shadowmoon Valley"
+ >
+ <File
+ RelativePath="..\scripts\zone\shadowmoon_valley\boss_doomwalker.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\shadowmoon_valley\shadowmoon_valley.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Tempest Keep"
+ >
+ <Filter
+ Name="Arcatraz"
+ >
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\arcatraz.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\boss_harbinger_skyriss.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\def_arcatraz.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\instance_arcatraz.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Botanica"
+ >
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\botanica\boss_high_botanist_freywinn.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\botanica\boss_laj.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\botanica\boss_warp_splinter.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="The Eye"
+ >
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_astromancer.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_kaelthas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_void_reaver.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\def_the_eye.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\instance_the_eye.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\the_eye.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="The Mechanar"
+ >
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_gyrokill.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_ironhand.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_nethermancer_sepethrea.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Terokkar Forest"
+ >
+ <File
+ RelativePath="..\scripts\zone\terokkar_forest\terokkar_forest.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Zangarmarsh"
+ >
+ <File
+ RelativePath="..\scripts\zone\zangarmarsh\zangarmarsh.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackrock Spire"
+ >
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_drakkisath.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_gyth.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_halycon.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_highlord_omokk.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_mother_smolderweb.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_overlord_wyrmthalak.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_pyroguard_emberseer.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_quartermaster_zigris.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_rend_blackhand.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_shadow_hunter_voshgajin.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_the_beast.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_warmaster_voone.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Stormwind City"
+ >
+ <File
+ RelativePath="..\scripts\zone\stormwind\stormwind_city.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Coilfang Resevoir"
+ >
+ <Filter
+ Name="Serpent Shrine Cavern"
+ >
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_fathomlord_karathress.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_hydross_the_unstable.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_lady_vashj.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_leotheras_the_blind.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_morogrim_tidewalker.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\def_serpent_shrine.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\instance_serpent_shrine.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Slave Pens"
+ >
+ </Filter>
+ <Filter
+ Name="Steam Vault"
+ >
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_hydromancer_thespia.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_mekgineer_steamrigger.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_warlord_kalithresh.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\def_steam_vault.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\instance_steam_vault.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Underbog"
+ >
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\underbog\boss_hungarfen.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Caverns of Time"
+ >
+ <Filter
+ Name="The Dark Portal"
+ >
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_aeonus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_chrono_lord_deja.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_temporus.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Battle for Mt. Hyjal"
+ >
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\boss_archimonde.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\def_hyjal.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjal.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\instance_hyjal.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Old Hillsbrad"
+ >
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_captain_skarloc.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_epoch_hunter.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_leutenant_drake.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\def_old_hillsbrad.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\instance_old_hillsbrad.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\old_hillsbrad.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Silvermoon City"
+ >
+ <File
+ RelativePath="..\scripts\zone\silvermoon\silvermoon_city.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Darnassus"
+ >
+ </Filter>
+ <Filter
+ Name="Exodar"
+ >
+ </Filter>
+ <Filter
+ Name="Iron Forge"
+ >
+ <File
+ RelativePath="..\scripts\zone\ironforge\ironforge.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Orgrimmar"
+ >
+ <File
+ RelativePath="..\scripts\zone\orgrimmar\orgrimmar.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Shattrath City"
+ >
+ <File
+ RelativePath="..\scripts\zone\shattrath\shattrath_city.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Thunder Bluff"
+ >
+ <File
+ RelativePath="..\scripts\zone\thunder_bluff\thunder_bluff.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Undercity"
+ >
+ <File
+ RelativePath="..\scripts\zone\undercity\undercity.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Zul&apos;Aman"
+ >
+ <File
+ RelativePath="..\scripts\zone\zulaman\boss_janalai.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\boss_nalorakk.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\def_zulaman.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\instance_zulaman.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\zulaman.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Isle of Quel&apos;Danas"
+ >
+ <File
+ RelativePath="..\scripts\zone\isle_of_queldanas\isle_of_queldanas.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Magister&apos;s Terrace"
+ >
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_felblood_kaelthas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_priestess_delrissa.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_selin_fireheart.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_vexallus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\def_magisters_terrace.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\instance_magisters_terrace.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Sunwell Plateau"
+ >
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\boss_brutallus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\boss_kalecgos.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\def_sunwell_plateau.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\instance_sunwell_plateau.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackrock Depths"
+ >
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\blackrock_depths.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_ambassador_flamelash.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_angerrel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_anubshiah.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_doomrel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_doperel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_emperor_dagran_thaurissan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_general_angerforge.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_gloomrel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_gorosh_the_dervish.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_grizzle.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_haterel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_high_interrogator_gerstahn.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_magmus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_moira_bronzebeard.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_seethrel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_vilerel.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Loch Modan"
+ >
+ <File
+ RelativePath="..\scripts\zone\loch_modan\loch_modan.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Include"
+ >
+ <File
+ RelativePath="..\include\precompiled.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\include\precompiled.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\sc_creature.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\include\sc_creature.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\sc_gossip.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\sc_instance.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\sc_item.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath="..\config.h"
+ >
+ </File>
+ <File
+ RelativePath="..\ScriptMgr.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\ScriptMgr.h"
+ >
+ </File>
+ <File
+ RelativePath="..\svn_revision.h"
+ >
+ </File>
+ <File
+ RelativePath="..\system.cpp"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/bindings/scripts/VC90/90ScriptDev2.vcproj b/src/bindings/scripts/VC90/90ScriptDev2.vcproj
index 0492d923dd5..e3ddffba8eb 100644
--- a/src/bindings/scripts/VC90/90ScriptDev2.vcproj
+++ b/src/bindings/scripts/VC90/90ScriptDev2.vcproj
@@ -1,2341 +1,2341 @@
-<?xml version="1.0" encoding="windows-1250"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="9,00"
- Name="TrinityScript"
- ProjectGUID="{4295C8A9-79B7-4354-8064-F05FB9CA0C96}"
- RootNamespace="ScriptDev2"
- Keyword="Win32Proj"
- TargetFrameworkVersion="131072"
- >
- <Platforms>
- <Platform
- Name="Win32"
- />
- <Platform
- Name="x64"
- />
- </Platforms>
- <ToolFiles>
- </ToolFiles>
- <Configurations>
- <Configuration
- Name="Debug|Win32"
- OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
- ConfigurationType="2"
- InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- CommandLine=""
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
- PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT"
- MinimalRebuild="true"
- BasicRuntimeChecks="3"
- RuntimeLibrary="1"
- UsePrecompiledHeader="2"
- PrecompiledHeaderThrough="precompiled.h"
- WarningLevel="3"
- Detect64BitPortabilityProblems="false"
- DebugInformationFormat="4"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="trinitycore.lib zthread.lib"
- OutputFile="$(OutDir)/TrinityScript.dll"
- LinkIncremental="2"
- AdditionalLibraryDirectories="..\..\..\..\win\VC90\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC90\trinitycore__$(PlatformName)_$(ConfigurationName)"
- GenerateDebugInformation="true"
- ProgramDatabaseFile="$(OutDir)/MaNGOSScript.pdb"
- SubSystem="2"
- RandomizedBaseAddress="1"
- DataExecutionPrevention="0"
- ImportLibrary="$(OutDir)/TrinityScript.lib"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
- />
- </Configuration>
- <Configuration
- Name="Debug|x64"
- OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
- ConfigurationType="2"
- InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- CommandLine=""
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- TargetEnvironment="3"
- />
- <Tool
- Name="VCCLCompilerTool"
- Optimization="0"
- AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
- PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT"
- MinimalRebuild="true"
- BasicRuntimeChecks="3"
- RuntimeLibrary="1"
- UsePrecompiledHeader="2"
- PrecompiledHeaderThrough="precompiled.h"
- WarningLevel="3"
- Detect64BitPortabilityProblems="false"
- DebugInformationFormat="3"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="trinitycore.lib zthread.lib"
- OutputFile="$(OutDir)/TrinityScript.dll"
- LinkIncremental="2"
- AdditionalLibraryDirectories="..\..\..\..\win\VC90\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC90\trinitycore__$(PlatformName)_$(ConfigurationName)"
- GenerateDebugInformation="true"
- ProgramDatabaseFile="$(OutDir)/MaNGOSScript.pdb"
- SubSystem="2"
- RandomizedBaseAddress="1"
- DataExecutionPrevention="0"
- ImportLibrary="$(OutDir)/TrinityScript.lib"
- TargetMachine="17"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
- />
- </Configuration>
- <Configuration
- Name="Release|Win32"
- OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
- ConfigurationType="2"
- InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- CommandLine=""
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/MP"
- AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
- PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT"
- MinimalRebuild="false"
- RuntimeLibrary="0"
- EnableEnhancedInstructionSet="1"
- FloatingPointModel="2"
- UsePrecompiledHeader="2"
- PrecompiledHeaderThrough="precompiled.h"
- WarningLevel="3"
- Detect64BitPortabilityProblems="false"
- DebugInformationFormat="3"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="trinitycore.lib zthread.lib"
- OutputFile="$(OutDir)/TrinityScript.dll"
- LinkIncremental="1"
- AdditionalLibraryDirectories="..\..\..\..\win\VC90\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC90\trinitycore__$(PlatformName)_$(ConfigurationName)"
- GenerateDebugInformation="false"
- SubSystem="2"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- RandomizedBaseAddress="1"
- DataExecutionPrevention="0"
- ImportLibrary="$(OutDir)/TrinityScript.lib"
- TargetMachine="1"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
- />
- </Configuration>
- <Configuration
- Name="Release|x64"
- OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
- ConfigurationType="2"
- InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- CommandLine=""
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- TargetEnvironment="3"
- />
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/MP"
- AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
- PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT"
- MinimalRebuild="false"
- RuntimeLibrary="0"
- UsePrecompiledHeader="2"
- PrecompiledHeaderThrough="precompiled.h"
- WarningLevel="3"
- Detect64BitPortabilityProblems="false"
- DebugInformationFormat="3"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="trinitycore.lib zthread.lib"
- OutputFile="$(OutDir)/TrinityScript.dll"
- LinkIncremental="1"
- AdditionalLibraryDirectories="..\..\..\..\win\VC90\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC90\trinitycore__$(PlatformName)_$(ConfigurationName)"
- GenerateDebugInformation="false"
- SubSystem="2"
- OptimizeReferences="2"
- EnableCOMDATFolding="2"
- RandomizedBaseAddress="1"
- DataExecutionPrevention="0"
- ImportLibrary="$(OutDir)/TrinityScript.lib"
- TargetMachine="17"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
- />
- </Configuration>
- </Configurations>
- <References>
- </References>
- <Files>
- <Filter
- Name="Scripts"
- >
- <Filter
- Name="boss"
- >
- <File
- RelativePath="..\scripts\boss\boss_emeriss.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\boss\boss_lethon.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\boss\boss_taerar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\boss\boss_ysondre.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="creature"
- >
- <File
- RelativePath="..\scripts\creature\mob_event_ai.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\creature\mob_event_ai.h"
- >
- </File>
- <File
- RelativePath="..\scripts\creature\mob_generic_creature.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\creature\simple_ai.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\creature\simple_ai.h"
- >
- </File>
- </Filter>
- <Filter
- Name="guard"
- >
- <File
- RelativePath="..\scripts\guard\guard_ai.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\guard\guard_ai.h"
- >
- </File>
- <File
- RelativePath="..\scripts\guard\guards.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="honor"
- >
- </Filter>
- <Filter
- Name="npc"
- >
- <File
- RelativePath="..\scripts\npc\npc_escortAI.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\npc\npc_escortAI.h"
- >
- </File>
- <File
- RelativePath="..\scripts\npc\npc_innkeeper.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\npc\npc_professions.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\npc\npcs_special.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="servers"
- >
- </Filter>
- <Filter
- Name="custom"
- >
- <File
- RelativePath="..\scripts\custom\custom_example.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\custom\custom_gossip_codebox.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\custom\test.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="areatrigger"
- >
- <File
- RelativePath="..\scripts\areatrigger\areatrigger_scripts.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="go"
- >
- <File
- RelativePath="..\scripts\go\go_scripts.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="item"
- >
- <File
- RelativePath="..\scripts\item\item_scripts.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\item\item_test.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="zone"
- >
- <Filter
- Name="Alterac Mountains"
- >
- <File
- RelativePath="..\scripts\zone\alterac_mountains\alterac_mountains.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Ashenvale Forest"
- >
- </Filter>
- <Filter
- Name="Azshara"
- >
- <File
- RelativePath="..\scripts\zone\azshara\azshara.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\azshara\boss_azuregos.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Badlands"
- >
- </Filter>
- <Filter
- Name="Barrens"
- >
- <File
- RelativePath="..\scripts\zone\barrens\the_barrens.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blackfathom Depths"
- >
- </Filter>
- <Filter
- Name="Arathi Highlands"
- >
- </Filter>
- <Filter
- Name="Deadmines"
- >
- <File
- RelativePath="..\scripts\zone\deadmines\deadmines.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Deadwind Pass"
- >
- </Filter>
- <Filter
- Name="Desolace"
- >
- </Filter>
- <Filter
- Name="Dire Maul"
- >
- </Filter>
- <Filter
- Name="Dun Morogh"
- >
- <File
- RelativePath="..\scripts\zone\dun_morogh\dun_morogh.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Durotar"
- >
- </Filter>
- <Filter
- Name="Duskwood"
- >
- </Filter>
- <Filter
- Name="Dustwallow Marsh"
- >
- <File
- RelativePath="..\scripts\zone\dustwallow_marsh\dustwallow_marsh.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blackwing Lair"
- >
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_broodlord_lashlayer.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_chromaggus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_ebonroc.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_firemaw.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_flamegor.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_nefarian.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_razorgore.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_vaelastrasz.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\boss_victor_nefarius.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackwing_lair\instance_blackwing_lair.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Bloodmyst Isle"
- >
- <File
- RelativePath="..\scripts\zone\bloodmyst_isle\bloodmyst_isle.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Gruul&apos;s Lair"
- >
- <File
- RelativePath="..\scripts\zone\gruuls_lair\boss_gruul.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\gruuls_lair\boss_high_king_maulgar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\gruuls_lair\def_gruuls_lair.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\gruuls_lair\instance_gruuls_lair.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Burning Steppes"
- >
- <File
- RelativePath="..\scripts\zone\burning_steppes\burning_steppes.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Darkshore"
- >
- <File
- RelativePath="..\scripts\zone\darkshore\darkshore.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Eastern Plaguelands"
- >
- <File
- RelativePath="..\scripts\zone\eastern_plaguelands\eastern_plaguelands.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Moonglade"
- >
- <File
- RelativePath="..\scripts\zone\moonglade\moonglade.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Razorfen Kraul"
- >
- </Filter>
- <Filter
- Name="Redridge Mountains"
- >
- </Filter>
- <Filter
- Name="Ruins of Ahn&apos;Qiraj"
- >
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ayamiss.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_buru.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_kurinnaxx.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_moam.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ossirian.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_rajaxx.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\ruins_of_ahnqiraj\instance_ruins_of_ahnqiraj.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Swamp of Sorrows"
- >
- </Filter>
- <Filter
- Name="Scarlet Monastery"
- >
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_arcanist_doan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_azshir_the_sleepless.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_bloodmage_thalnos.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_herod.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_fairbanks.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_whitemane.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_houndmaster_loksey.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_interrogator_vishas.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_scarlet_commander_mograine.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scarlet_monastery\boss_scorn.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Scholomance"
- >
- <File
- RelativePath="..\scripts\zone\scholomance\boss_darkmaster_gandling.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_death_knight_darkreaver.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_doctor_theolen_krastinov.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_illucia_barov.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_instructor_malicia.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_jandice_barov.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_kormok.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_lord_alexei_barov.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_lorekeeper_polkelt.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_ras_frostwhisper.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_the_ravenian.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\boss_vectus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\def_scholomance.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\scholomance\instance_scholomance.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Searing Gorge"
- >
- <File
- RelativePath="..\scripts\zone\searing_gorge\searing_gorge.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Elwynn Forest"
- >
- <File
- RelativePath="..\scripts\zone\elwynn_forest\elwynn_forest.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Felwood"
- >
- <File
- RelativePath="..\scripts\zone\felwood\felwood.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Feralas"
- >
- <File
- RelativePath="..\scripts\zone\feralas\feralas.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Gnomeregan"
- >
- </Filter>
- <Filter
- Name="Hillsbrad Foothills"
- >
- </Filter>
- <Filter
- Name="Hinterlands"
- >
- </Filter>
- <Filter
- Name="Maraudon"
- >
- <File
- RelativePath="..\scripts\zone\maraudon\boss_celebras_the_cursed.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\maraudon\boss_landslide.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\maraudon\boss_noxxion.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\maraudon\boss_princess_theradras.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Molten Core"
- >
- <File
- RelativePath="..\scripts\zone\molten_core\boss_baron_geddon.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_garr.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_gehennas.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_golemagg.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_lucifron.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_magmadar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_majordomo_executus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_ragnaros.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_shazzrah.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\boss_sulfuron_harbinger.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\def_molten_core.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\instance_molten_core.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\molten_core\molten_core.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Mulgore"
- >
- <File
- RelativePath="..\scripts\zone\mulgore\mulgore.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Naxxramas"
- >
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_anubrekhan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_faerlina.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_feugen.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_gluth.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_gothik.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_grobbulus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_heigan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_highlord_mograine.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_kelthuzad.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_lady_blaumeux.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_loatheb.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_maexxna.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_noth.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_patchwerk.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_razuvious.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_sapphiron.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_sir_zeliek.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_stalagg.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_thaddius.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\boss_thane_korthazz.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\naxxramas\instance_naxxramas.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Onyxia&apos;s Lair"
- >
- <File
- RelativePath="..\scripts\zone\onyxias_lair\boss_onyxia.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Ragefire Chasm"
- >
- </Filter>
- <Filter
- Name="Razorfen Downs"
- >
- <File
- RelativePath="..\scripts\zone\razorfen_downs\boss_amnennar_the_coldbringer.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Shadowfang Keep"
- >
- <File
- RelativePath="..\scripts\zone\shadowfang_keep\def_shadowfang_keep.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\shadowfang_keep\instance_shadowfang_keep.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\shadowfang_keep\shadowfang_keep.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Stonetalon Mountains"
- >
- <File
- RelativePath="..\scripts\zone\stonetalon_mountains\stonetalon_mountains.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Stranglethorn Vale"
- >
- <File
- RelativePath="..\scripts\zone\stranglethorn_vale\stranglethorn_vale.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Stratholme"
- >
- <File
- RelativePath="..\scripts\zone\stratholme\boss_baron_rivendare.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_baroness_anastari.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_cannon_master_willey.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_dathrohan_balnazzar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_magistrate_barthilas.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_maleki_the_pallid.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_nerubenkan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_order_of_silver_hand.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_postmaster_malown.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_ramstein_the_gorger.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\boss_timmy_the_cruel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\def_stratholme.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\instance_stratholme.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\stratholme\stratholme.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Sunken Temple"
- >
- </Filter>
- <Filter
- Name="Tanaris"
- >
- <File
- RelativePath="..\scripts\zone\tanaris\tanaris.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Teldrassil"
- >
- </Filter>
- <Filter
- Name="Temple of Ahn&apos;Qiraj"
- >
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_bug_trio.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_cthun.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_fankriss.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_huhuran.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_ouro.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_sartura.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_skeram.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_twinemperors.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_viscidus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\def_temple_of_ahnqiraj.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\instance_temple_of_ahnqiraj.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\temple_of_ahnqiraj\mob_anubisath_sentinel.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Thousand Needles"
- >
- </Filter>
- <Filter
- Name="Silithus"
- >
- <File
- RelativePath="..\scripts\zone\silithus\silithus.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Silverpine Forest"
- >
- <File
- RelativePath="..\scripts\zone\silverpine_forest\silverpine_forest.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Stockade"
- >
- </Filter>
- <Filter
- Name="Tirisfal Glades"
- >
- <File
- RelativePath="..\scripts\zone\tirisfal_glades\tirisfal_glades.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Wailing Caverns"
- >
- <File
- RelativePath="..\scripts\zone\wailing_caverns\instance_wailing_caverns.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Western Plaguelands"
- >
- <File
- RelativePath="..\scripts\zone\western_plaguelands\western_plaguelands.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Westfall"
- >
- </Filter>
- <Filter
- Name="Wetlands"
- >
- </Filter>
- <Filter
- Name="Winterspring"
- >
- <File
- RelativePath="..\scripts\zone\winterspring\winterspring.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Zul&apos;Farrak"
- >
- <File
- RelativePath="..\scripts\zone\zulfarrak\zulfarrak.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Zul&apos;Gurub"
- >
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_arlokk.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_gahzranka.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_grilek.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_hakkar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_hazzarah.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_jeklik.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_jindo.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_mandokir.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_marli.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_renataki.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_thekal.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_venoxis.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\boss_wushoolay.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\def_zulgurub.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulgurub\instance_zulgurub.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Uldaman"
- >
- <File
- RelativePath="..\scripts\zone\uldaman\boss_ironaya.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\uldaman\uldaman.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Un&apos;Goro Crater"
- >
- </Filter>
- <Filter
- Name="Aunchindoun"
- >
- <Filter
- Name="Auchenai Crypts"
- >
- <File
- RelativePath="..\scripts\zone\aunchindoun\auchenai_crypts\boss_exarch_maladaar.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Mana Tombs"
- >
- <File
- RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_nexusprince_shaffar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_pandemonius.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Sethekk Halls"
- >
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_darkweaver_syth.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_tailonking_ikiss.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\def_sethekk_halls.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\instance_sethekk_halls.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Shadow Labyrinth"
- >
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_ambassador_hellmaw.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_blackheart_the_inciter.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_grandmaster_vorpil.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_murmur.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\def_shadow_labyrinth.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\instance_shadow_labyrinth.cpp"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Azuremyst Isle"
- >
- <File
- RelativePath="..\scripts\zone\azuremyst_isle\azuremyst_isle.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Black Temple"
- >
- <File
- RelativePath="..\scripts\zone\black_temple\black_temple.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_bloodboil.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_illidan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_mother_shahraz.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_reliquary_of_souls.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_shade_of_akama.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_supremus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_teron_gorefiend.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\boss_warlord_najentus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\def_black_temple.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\illidari_council.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\black_temple\instance_black_temple.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Eversong Woods"
- >
- <File
- RelativePath="..\scripts\zone\eversong_woods\eversong_woods.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Ghostlands"
- >
- <File
- RelativePath="..\scripts\zone\ghostlands\ghostlands.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blade&apos;s Edge Mountains"
- >
- <File
- RelativePath="..\scripts\zone\blades_edge_mountains\blades_edge_mountains.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blasted Lands"
- >
- <File
- RelativePath="..\scripts\zone\blasted_lands\blasted_lands.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blasted_lands\boss_kruul.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Hellfire Citadel"
- >
- <Filter
- Name="Blood Furnace"
- >
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_broggok.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_kelidan_the_breaker.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_the_maker.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Magtheridon&apos;s lair"
- >
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\boss_magtheridon.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\def_magtheridons_lair.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\instance_magtheridons_lair.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Hellfire Ramparts"
- >
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_omor_the_unscarred.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_watchkeeper_gargolmar.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Shattered Halls"
- >
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_nethekurse.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_warbringer_omrogg.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\def_shattered_halls.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\instance_shattered_halls.cpp"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Hellfire Peninsula"
- >
- <File
- RelativePath="..\scripts\zone\hellfire_peninsula\boss_doomlord_kazzak.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\hellfire_peninsula\hellfire_peninsula.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Karazhan"
- >
- <File
- RelativePath="..\scripts\zone\karazhan\boss_curator.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_maiden_of_virtue.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_midnight.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_moroes.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_netherspite.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_nightbane.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_prince_malchezaar.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_shade_of_aran.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\boss_terestian_illhoof.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\bosses_opera.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\def_karazhan.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\instance_karazhan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\karazhan\karazhan.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Nagrand"
- >
- <File
- RelativePath="..\scripts\zone\nagrand\nagrand.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Netherstorm"
- >
- <File
- RelativePath="..\scripts\zone\netherstorm\netherstorm.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Shadowmoon Valley"
- >
- <File
- RelativePath="..\scripts\zone\shadowmoon_valley\boss_doomwalker.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\shadowmoon_valley\shadowmoon_valley.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Tempest Keep"
- >
- <Filter
- Name="Arcatraz"
- >
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\arcatraz.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\boss_harbinger_skyriss.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\def_arcatraz.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\arcatraz\instance_arcatraz.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Botanica"
- >
- <File
- RelativePath="..\scripts\zone\tempest_keep\botanica\boss_high_botanist_freywinn.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\botanica\boss_laj.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\botanica\boss_warp_splinter.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="The Eye"
- >
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_astromancer.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_kaelthas.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_void_reaver.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\def_the_eye.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\instance_the_eye.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_eye\the_eye.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="The Mechanar"
- >
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_gyrokill.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_ironhand.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_nethermancer_sepethrea.cpp"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Terokkar Forest"
- >
- <File
- RelativePath="..\scripts\zone\terokkar_forest\terokkar_forest.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Zangarmarsh"
- >
- <File
- RelativePath="..\scripts\zone\zangarmarsh\zangarmarsh.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blackrock Spire"
- >
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_drakkisath.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_gyth.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_halycon.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_highlord_omokk.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_mother_smolderweb.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_overlord_wyrmthalak.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_pyroguard_emberseer.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_quartermaster_zigris.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_rend_blackhand.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_shadow_hunter_voshgajin.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_the_beast.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_spire\boss_warmaster_voone.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Stormwind City"
- >
- <File
- RelativePath="..\scripts\zone\stormwind\stormwind_city.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Coilfang Resevoir"
- >
- <Filter
- Name="Serpent Shrine Cavern"
- >
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_fathomlord_karathress.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_hydross_the_unstable.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_lady_vashj.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_leotheras_the_blind.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_morogrim_tidewalker.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\def_serpent_shrine.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\instance_serpent_shrine.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Slave Pens"
- >
- </Filter>
- <Filter
- Name="Steam Vault"
- >
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_hydromancer_thespia.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_mekgineer_steamrigger.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_warlord_kalithresh.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\def_steam_vault.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\instance_steam_vault.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Underbog"
- >
- <File
- RelativePath="..\scripts\zone\coilfang_resevoir\underbog\boss_hungarfen.cpp"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Caverns of Time"
- >
- <Filter
- Name="The Dark Portal"
- >
- <File
- RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_aeonus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_chrono_lord_deja.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_temporus.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Battle for Mt. Hyjal"
- >
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\boss_archimonde.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\def_hyjal.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjal.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\hyjal\instance_hyjal.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Old Hillsbrad"
- >
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_captain_skarloc.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_epoch_hunter.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_leutenant_drake.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\def_old_hillsbrad.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\instance_old_hillsbrad.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\old_hillsbrad.cpp"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Silvermoon City"
- >
- <File
- RelativePath="..\scripts\zone\silvermoon\silvermoon_city.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Darnassus"
- >
- </Filter>
- <Filter
- Name="Exodar"
- >
- </Filter>
- <Filter
- Name="Iron Forge"
- >
- <File
- RelativePath="..\scripts\zone\ironforge\ironforge.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Orgrimmar"
- >
- <File
- RelativePath="..\scripts\zone\orgrimmar\orgrimmar.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Shattrath City"
- >
- <File
- RelativePath="..\scripts\zone\shattrath\shattrath_city.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Thunder Bluff"
- >
- <File
- RelativePath="..\scripts\zone\thunder_bluff\thunder_bluff.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Undercity"
- >
- <File
- RelativePath="..\scripts\zone\undercity\undercity.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Zul&apos;Aman"
- >
- <File
- RelativePath="..\scripts\zone\zulaman\boss_janalai.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\boss_nalorakk.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\def_zulaman.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\instance_zulaman.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\zulaman\zulaman.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Isle of Quel&apos;Danas"
- >
- <File
- RelativePath="..\scripts\zone\isle_of_queldanas\isle_of_queldanas.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Magister&apos;s Terrace"
- >
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_felblood_kaelthas.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_priestess_delrissa.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_selin_fireheart.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\boss_vexallus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\def_magisters_terrace.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\magisters_terrace\instance_magisters_terrace.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Sunwell Plateau"
- >
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\boss_brutallus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\boss_kalecgos.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\def_sunwell_plateau.h"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\sunwell_plateau\instance_sunwell_plateau.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Blackrock Depths"
- >
- <File
- RelativePath="..\scripts\zone\blackrock_depths\blackrock_depths.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_ambassador_flamelash.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_angerrel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_anubshiah.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_doomrel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_doperel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_emperor_dagran_thaurissan.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_general_angerforge.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_gloomrel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_gorosh_the_dervish.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_grizzle.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_haterel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_high_interrogator_gerstahn.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_magmus.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_moira_bronzebeard.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_seethrel.cpp"
- >
- </File>
- <File
- RelativePath="..\scripts\zone\blackrock_depths\boss_vilerel.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="Loch Modan"
- >
- <File
- RelativePath="..\scripts\zone\loch_modan\loch_modan.cpp"
- >
- </File>
- </Filter>
- </Filter>
- </Filter>
- <Filter
- Name="Include"
- >
- <File
- RelativePath="..\include\precompiled.cpp"
- >
- <FileConfiguration
- Name="Debug|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="1"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Debug|x64"
- >
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="1"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="1"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release|x64"
- >
- <Tool
- Name="VCCLCompilerTool"
- UsePrecompiledHeader="1"
- />
- </FileConfiguration>
- </File>
- <File
- RelativePath="..\include\precompiled.h"
- >
- </File>
- <File
- RelativePath="..\include\sc_creature.cpp"
- >
- </File>
- <File
- RelativePath="..\include\sc_creature.h"
- >
- </File>
- <File
- RelativePath="..\include\sc_gossip.h"
- >
- </File>
- <File
- RelativePath="..\include\sc_instance.h"
- >
- </File>
- <File
- RelativePath="..\include\sc_item.h"
- >
- </File>
- </Filter>
- <File
- RelativePath="..\config.h"
- >
- </File>
- <File
- RelativePath="..\ScriptMgr.cpp"
- >
- </File>
- <File
- RelativePath="..\ScriptMgr.h"
- >
- </File>
- <File
- RelativePath="..\svn_revision.h"
- >
- </File>
- <File
- RelativePath="..\system.cpp"
- >
- </File>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
+<?xml version="1.0" encoding="windows-1250"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="TrinityScript"
+ ProjectGUID="{4295C8A9-79B7-4354-8064-F05FB9CA0C96}"
+ RootNamespace="ScriptDev2"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
+ PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precompiled.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="trinitycore.lib zthread.lib"
+ OutputFile="$(OutDir)/TrinityScript.dll"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="..\..\..\..\win\VC90\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC90\trinitycore__$(PlatformName)_$(ConfigurationName)"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/MaNGOSScript.pdb"
+ SubSystem="2"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(OutDir)/TrinityScript.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
+ PreprocessorDefinitions="WIN32;_DEBUG;MANGOS_DEBUG;_WINDOWS;_USRDLL;SCRIPT"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precompiled.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="trinitycore.lib zthread.lib"
+ OutputFile="$(OutDir)/TrinityScript.dll"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="..\..\..\..\win\VC90\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC90\trinitycore__$(PlatformName)_$(ConfigurationName)"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/MaNGOSScript.pdb"
+ SubSystem="2"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(OutDir)/TrinityScript.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT"
+ MinimalRebuild="false"
+ RuntimeLibrary="0"
+ EnableEnhancedInstructionSet="1"
+ FloatingPointModel="2"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precompiled.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="trinitycore.lib zthread.lib"
+ OutputFile="$(OutDir)/TrinityScript.dll"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="..\..\..\..\win\VC90\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC90\trinitycore__$(PlatformName)_$(ConfigurationName)"
+ GenerateDebugInformation="false"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(OutDir)/TrinityScript.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\ScriptDev2__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine=""
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ AdditionalIncludeDirectories="..\..\..\..\dep\include\;..\..\..\shared\;..\..\..\framework\;..\..\..\game\;..\include\;..\..\..\..\dep\ACE_wrappers"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SCRIPT"
+ MinimalRebuild="false"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precompiled.h"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="trinitycore.lib zthread.lib"
+ OutputFile="$(OutDir)/TrinityScript.dll"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="..\..\..\..\win\VC90\zthread__$(PlatformName)_$(ConfigurationName);..\..\..\..\win\VC90\trinitycore__$(PlatformName)_$(ConfigurationName)"
+ GenerateDebugInformation="false"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(OutDir)/TrinityScript.lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy ..\trinityscript.conf.dist ..\..\..\..\bin\$(PlatformName)_$(ConfigurationName)\TrinityScript.conf.dist"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Scripts"
+ >
+ <Filter
+ Name="boss"
+ >
+ <File
+ RelativePath="..\scripts\boss\boss_emeriss.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\boss\boss_lethon.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\boss\boss_taerar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\boss\boss_ysondre.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="creature"
+ >
+ <File
+ RelativePath="..\scripts\creature\mob_event_ai.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\creature\mob_event_ai.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\creature\mob_generic_creature.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\creature\simple_ai.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\creature\simple_ai.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="guard"
+ >
+ <File
+ RelativePath="..\scripts\guard\guard_ai.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\guard\guard_ai.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\guard\guards.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="honor"
+ >
+ </Filter>
+ <Filter
+ Name="npc"
+ >
+ <File
+ RelativePath="..\scripts\npc\npc_escortAI.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npc_escortAI.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npc_innkeeper.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npc_professions.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\npc\npcs_special.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="servers"
+ >
+ </Filter>
+ <Filter
+ Name="custom"
+ >
+ <File
+ RelativePath="..\scripts\custom\custom_example.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\custom\custom_gossip_codebox.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\custom\test.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="areatrigger"
+ >
+ <File
+ RelativePath="..\scripts\areatrigger\areatrigger_scripts.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="go"
+ >
+ <File
+ RelativePath="..\scripts\go\go_scripts.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="item"
+ >
+ <File
+ RelativePath="..\scripts\item\item_scripts.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\item\item_test.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="zone"
+ >
+ <Filter
+ Name="Alterac Mountains"
+ >
+ <File
+ RelativePath="..\scripts\zone\alterac_mountains\alterac_mountains.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Ashenvale Forest"
+ >
+ </Filter>
+ <Filter
+ Name="Azshara"
+ >
+ <File
+ RelativePath="..\scripts\zone\azshara\azshara.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\azshara\boss_azuregos.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Badlands"
+ >
+ </Filter>
+ <Filter
+ Name="Barrens"
+ >
+ <File
+ RelativePath="..\scripts\zone\barrens\the_barrens.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackfathom Depths"
+ >
+ </Filter>
+ <Filter
+ Name="Arathi Highlands"
+ >
+ </Filter>
+ <Filter
+ Name="Deadmines"
+ >
+ <File
+ RelativePath="..\scripts\zone\deadmines\deadmines.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Deadwind Pass"
+ >
+ </Filter>
+ <Filter
+ Name="Desolace"
+ >
+ </Filter>
+ <Filter
+ Name="Dire Maul"
+ >
+ </Filter>
+ <Filter
+ Name="Dun Morogh"
+ >
+ <File
+ RelativePath="..\scripts\zone\dun_morogh\dun_morogh.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Durotar"
+ >
+ </Filter>
+ <Filter
+ Name="Duskwood"
+ >
+ </Filter>
+ <Filter
+ Name="Dustwallow Marsh"
+ >
+ <File
+ RelativePath="..\scripts\zone\dustwallow_marsh\dustwallow_marsh.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackwing Lair"
+ >
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_broodlord_lashlayer.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_chromaggus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_ebonroc.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_firemaw.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_flamegor.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_nefarian.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_razorgore.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_vaelastrasz.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\boss_victor_nefarius.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackwing_lair\instance_blackwing_lair.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Bloodmyst Isle"
+ >
+ <File
+ RelativePath="..\scripts\zone\bloodmyst_isle\bloodmyst_isle.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Gruul&apos;s Lair"
+ >
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\boss_gruul.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\boss_high_king_maulgar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\def_gruuls_lair.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\gruuls_lair\instance_gruuls_lair.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Burning Steppes"
+ >
+ <File
+ RelativePath="..\scripts\zone\burning_steppes\burning_steppes.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Darkshore"
+ >
+ <File
+ RelativePath="..\scripts\zone\darkshore\darkshore.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Eastern Plaguelands"
+ >
+ <File
+ RelativePath="..\scripts\zone\eastern_plaguelands\eastern_plaguelands.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Moonglade"
+ >
+ <File
+ RelativePath="..\scripts\zone\moonglade\moonglade.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Razorfen Kraul"
+ >
+ </Filter>
+ <Filter
+ Name="Redridge Mountains"
+ >
+ </Filter>
+ <Filter
+ Name="Ruins of Ahn&apos;Qiraj"
+ >
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ayamiss.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_buru.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_kurinnaxx.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_moam.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_ossirian.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\boss_rajaxx.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\ruins_of_ahnqiraj\instance_ruins_of_ahnqiraj.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Swamp of Sorrows"
+ >
+ </Filter>
+ <Filter
+ Name="Scarlet Monastery"
+ >
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_arcanist_doan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_azshir_the_sleepless.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_bloodmage_thalnos.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_herod.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_fairbanks.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_high_inquisitor_whitemane.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_houndmaster_loksey.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_interrogator_vishas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_scarlet_commander_mograine.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scarlet_monastery\boss_scorn.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Scholomance"
+ >
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_darkmaster_gandling.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_death_knight_darkreaver.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_doctor_theolen_krastinov.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_illucia_barov.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_instructor_malicia.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_jandice_barov.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_kormok.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_lord_alexei_barov.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_lorekeeper_polkelt.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_ras_frostwhisper.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_the_ravenian.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\boss_vectus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\def_scholomance.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\scholomance\instance_scholomance.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Searing Gorge"
+ >
+ <File
+ RelativePath="..\scripts\zone\searing_gorge\searing_gorge.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Elwynn Forest"
+ >
+ <File
+ RelativePath="..\scripts\zone\elwynn_forest\elwynn_forest.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Felwood"
+ >
+ <File
+ RelativePath="..\scripts\zone\felwood\felwood.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Feralas"
+ >
+ <File
+ RelativePath="..\scripts\zone\feralas\feralas.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Gnomeregan"
+ >
+ </Filter>
+ <Filter
+ Name="Hillsbrad Foothills"
+ >
+ </Filter>
+ <Filter
+ Name="Hinterlands"
+ >
+ </Filter>
+ <Filter
+ Name="Maraudon"
+ >
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_celebras_the_cursed.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_landslide.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_noxxion.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\maraudon\boss_princess_theradras.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Molten Core"
+ >
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_baron_geddon.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_garr.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_gehennas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_golemagg.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_lucifron.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_magmadar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_majordomo_executus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_ragnaros.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_shazzrah.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\boss_sulfuron_harbinger.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\def_molten_core.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\instance_molten_core.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\molten_core\molten_core.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Mulgore"
+ >
+ <File
+ RelativePath="..\scripts\zone\mulgore\mulgore.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Naxxramas"
+ >
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_anubrekhan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_faerlina.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_feugen.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_gluth.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_gothik.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_grobbulus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_heigan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_highlord_mograine.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_kelthuzad.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_lady_blaumeux.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_loatheb.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_maexxna.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_noth.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_patchwerk.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_razuvious.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_sapphiron.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_sir_zeliek.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_stalagg.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_thaddius.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\boss_thane_korthazz.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\naxxramas\instance_naxxramas.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Onyxia&apos;s Lair"
+ >
+ <File
+ RelativePath="..\scripts\zone\onyxias_lair\boss_onyxia.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Ragefire Chasm"
+ >
+ </Filter>
+ <Filter
+ Name="Razorfen Downs"
+ >
+ <File
+ RelativePath="..\scripts\zone\razorfen_downs\boss_amnennar_the_coldbringer.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Shadowfang Keep"
+ >
+ <File
+ RelativePath="..\scripts\zone\shadowfang_keep\def_shadowfang_keep.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\shadowfang_keep\instance_shadowfang_keep.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\shadowfang_keep\shadowfang_keep.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Stonetalon Mountains"
+ >
+ <File
+ RelativePath="..\scripts\zone\stonetalon_mountains\stonetalon_mountains.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Stranglethorn Vale"
+ >
+ <File
+ RelativePath="..\scripts\zone\stranglethorn_vale\stranglethorn_vale.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Stratholme"
+ >
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_baron_rivendare.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_baroness_anastari.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_cannon_master_willey.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_dathrohan_balnazzar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_magistrate_barthilas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_maleki_the_pallid.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_nerubenkan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_order_of_silver_hand.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_postmaster_malown.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_ramstein_the_gorger.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\boss_timmy_the_cruel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\def_stratholme.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\instance_stratholme.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\stratholme\stratholme.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Sunken Temple"
+ >
+ </Filter>
+ <Filter
+ Name="Tanaris"
+ >
+ <File
+ RelativePath="..\scripts\zone\tanaris\tanaris.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Teldrassil"
+ >
+ </Filter>
+ <Filter
+ Name="Temple of Ahn&apos;Qiraj"
+ >
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_bug_trio.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_cthun.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_fankriss.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_huhuran.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_ouro.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_sartura.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_skeram.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_twinemperors.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\boss_viscidus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\def_temple_of_ahnqiraj.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\instance_temple_of_ahnqiraj.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\temple_of_ahnqiraj\mob_anubisath_sentinel.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Thousand Needles"
+ >
+ </Filter>
+ <Filter
+ Name="Silithus"
+ >
+ <File
+ RelativePath="..\scripts\zone\silithus\silithus.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Silverpine Forest"
+ >
+ <File
+ RelativePath="..\scripts\zone\silverpine_forest\silverpine_forest.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Stockade"
+ >
+ </Filter>
+ <Filter
+ Name="Tirisfal Glades"
+ >
+ <File
+ RelativePath="..\scripts\zone\tirisfal_glades\tirisfal_glades.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Wailing Caverns"
+ >
+ <File
+ RelativePath="..\scripts\zone\wailing_caverns\instance_wailing_caverns.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Western Plaguelands"
+ >
+ <File
+ RelativePath="..\scripts\zone\western_plaguelands\western_plaguelands.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Westfall"
+ >
+ </Filter>
+ <Filter
+ Name="Wetlands"
+ >
+ </Filter>
+ <Filter
+ Name="Winterspring"
+ >
+ <File
+ RelativePath="..\scripts\zone\winterspring\winterspring.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Zul&apos;Farrak"
+ >
+ <File
+ RelativePath="..\scripts\zone\zulfarrak\zulfarrak.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Zul&apos;Gurub"
+ >
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_arlokk.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_gahzranka.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_grilek.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_hakkar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_hazzarah.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_jeklik.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_jindo.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_mandokir.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_marli.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_renataki.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_thekal.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_venoxis.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\boss_wushoolay.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\def_zulgurub.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulgurub\instance_zulgurub.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Uldaman"
+ >
+ <File
+ RelativePath="..\scripts\zone\uldaman\boss_ironaya.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\uldaman\uldaman.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Un&apos;Goro Crater"
+ >
+ </Filter>
+ <Filter
+ Name="Aunchindoun"
+ >
+ <Filter
+ Name="Auchenai Crypts"
+ >
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\auchenai_crypts\boss_exarch_maladaar.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Mana Tombs"
+ >
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_nexusprince_shaffar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\mana_tombs\boss_pandemonius.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Sethekk Halls"
+ >
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_darkweaver_syth.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\boss_tailonking_ikiss.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\def_sethekk_halls.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\sethekk_halls\instance_sethekk_halls.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Shadow Labyrinth"
+ >
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_ambassador_hellmaw.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_blackheart_the_inciter.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_grandmaster_vorpil.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\boss_murmur.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\def_shadow_labyrinth.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\aunchindoun\shadow_labyrinth\instance_shadow_labyrinth.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Azuremyst Isle"
+ >
+ <File
+ RelativePath="..\scripts\zone\azuremyst_isle\azuremyst_isle.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Black Temple"
+ >
+ <File
+ RelativePath="..\scripts\zone\black_temple\black_temple.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_bloodboil.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_illidan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_mother_shahraz.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_reliquary_of_souls.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_shade_of_akama.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_supremus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_teron_gorefiend.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\boss_warlord_najentus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\def_black_temple.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\illidari_council.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\black_temple\instance_black_temple.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Eversong Woods"
+ >
+ <File
+ RelativePath="..\scripts\zone\eversong_woods\eversong_woods.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Ghostlands"
+ >
+ <File
+ RelativePath="..\scripts\zone\ghostlands\ghostlands.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blade&apos;s Edge Mountains"
+ >
+ <File
+ RelativePath="..\scripts\zone\blades_edge_mountains\blades_edge_mountains.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blasted Lands"
+ >
+ <File
+ RelativePath="..\scripts\zone\blasted_lands\blasted_lands.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blasted_lands\boss_kruul.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Hellfire Citadel"
+ >
+ <Filter
+ Name="Blood Furnace"
+ >
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_broggok.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_kelidan_the_breaker.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\blood_furnace\boss_the_maker.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Magtheridon&apos;s lair"
+ >
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\boss_magtheridon.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\def_magtheridons_lair.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\magtheridons_lair\instance_magtheridons_lair.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Hellfire Ramparts"
+ >
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_omor_the_unscarred.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\hellfire_ramparts\boss_watchkeeper_gargolmar.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Shattered Halls"
+ >
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_nethekurse.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\boss_warbringer_omrogg.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\def_shattered_halls.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_citadel\shattered_halls\instance_shattered_halls.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Hellfire Peninsula"
+ >
+ <File
+ RelativePath="..\scripts\zone\hellfire_peninsula\boss_doomlord_kazzak.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\hellfire_peninsula\hellfire_peninsula.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Karazhan"
+ >
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_curator.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_maiden_of_virtue.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_midnight.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_moroes.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_netherspite.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_nightbane.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_prince_malchezaar.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_shade_of_aran.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\boss_terestian_illhoof.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\bosses_opera.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\def_karazhan.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\instance_karazhan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\karazhan\karazhan.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Nagrand"
+ >
+ <File
+ RelativePath="..\scripts\zone\nagrand\nagrand.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Netherstorm"
+ >
+ <File
+ RelativePath="..\scripts\zone\netherstorm\netherstorm.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Shadowmoon Valley"
+ >
+ <File
+ RelativePath="..\scripts\zone\shadowmoon_valley\boss_doomwalker.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\shadowmoon_valley\shadowmoon_valley.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Tempest Keep"
+ >
+ <Filter
+ Name="Arcatraz"
+ >
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\arcatraz.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\boss_harbinger_skyriss.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\def_arcatraz.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\arcatraz\instance_arcatraz.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Botanica"
+ >
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\botanica\boss_high_botanist_freywinn.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\botanica\boss_laj.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\botanica\boss_warp_splinter.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="The Eye"
+ >
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_astromancer.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_kaelthas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\boss_void_reaver.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\def_the_eye.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\instance_the_eye.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_eye\the_eye.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="The Mechanar"
+ >
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_gyrokill.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_gatewatcher_ironhand.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\tempest_keep\the_mechanar\boss_nethermancer_sepethrea.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Terokkar Forest"
+ >
+ <File
+ RelativePath="..\scripts\zone\terokkar_forest\terokkar_forest.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Zangarmarsh"
+ >
+ <File
+ RelativePath="..\scripts\zone\zangarmarsh\zangarmarsh.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackrock Spire"
+ >
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_drakkisath.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_gyth.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_halycon.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_highlord_omokk.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_mother_smolderweb.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_overlord_wyrmthalak.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_pyroguard_emberseer.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_quartermaster_zigris.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_rend_blackhand.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_shadow_hunter_voshgajin.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_the_beast.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_spire\boss_warmaster_voone.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Stormwind City"
+ >
+ <File
+ RelativePath="..\scripts\zone\stormwind\stormwind_city.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Coilfang Resevoir"
+ >
+ <Filter
+ Name="Serpent Shrine Cavern"
+ >
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_fathomlord_karathress.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_hydross_the_unstable.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_lady_vashj.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_leotheras_the_blind.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\boss_morogrim_tidewalker.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\def_serpent_shrine.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\serpent_shrine\instance_serpent_shrine.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Slave Pens"
+ >
+ </Filter>
+ <Filter
+ Name="Steam Vault"
+ >
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_hydromancer_thespia.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_mekgineer_steamrigger.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\boss_warlord_kalithresh.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\def_steam_vault.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\steam_vault\instance_steam_vault.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Underbog"
+ >
+ <File
+ RelativePath="..\scripts\zone\coilfang_resevoir\underbog\boss_hungarfen.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Caverns of Time"
+ >
+ <Filter
+ Name="The Dark Portal"
+ >
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_aeonus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_chrono_lord_deja.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\dark_portal\boss_temporus.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Battle for Mt. Hyjal"
+ >
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\boss_archimonde.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\def_hyjal.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjal.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\hyjalAI.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\hyjal\instance_hyjal.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Old Hillsbrad"
+ >
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_captain_skarloc.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_epoch_hunter.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\boss_leutenant_drake.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\def_old_hillsbrad.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\instance_old_hillsbrad.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\caverns_of_time\old_hillsbrad\old_hillsbrad.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Silvermoon City"
+ >
+ <File
+ RelativePath="..\scripts\zone\silvermoon\silvermoon_city.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Darnassus"
+ >
+ </Filter>
+ <Filter
+ Name="Exodar"
+ >
+ </Filter>
+ <Filter
+ Name="Iron Forge"
+ >
+ <File
+ RelativePath="..\scripts\zone\ironforge\ironforge.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Orgrimmar"
+ >
+ <File
+ RelativePath="..\scripts\zone\orgrimmar\orgrimmar.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Shattrath City"
+ >
+ <File
+ RelativePath="..\scripts\zone\shattrath\shattrath_city.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Thunder Bluff"
+ >
+ <File
+ RelativePath="..\scripts\zone\thunder_bluff\thunder_bluff.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Undercity"
+ >
+ <File
+ RelativePath="..\scripts\zone\undercity\undercity.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Zul&apos;Aman"
+ >
+ <File
+ RelativePath="..\scripts\zone\zulaman\boss_janalai.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\boss_nalorakk.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\def_zulaman.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\instance_zulaman.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\zulaman\zulaman.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Isle of Quel&apos;Danas"
+ >
+ <File
+ RelativePath="..\scripts\zone\isle_of_queldanas\isle_of_queldanas.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Magister&apos;s Terrace"
+ >
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_felblood_kaelthas.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_priestess_delrissa.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_selin_fireheart.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\boss_vexallus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\def_magisters_terrace.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\magisters_terrace\instance_magisters_terrace.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Sunwell Plateau"
+ >
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\boss_brutallus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\boss_kalecgos.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\def_sunwell_plateau.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\sunwell_plateau\instance_sunwell_plateau.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Blackrock Depths"
+ >
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\blackrock_depths.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_ambassador_flamelash.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_angerrel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_anubshiah.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_doomrel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_doperel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_emperor_dagran_thaurissan.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_general_angerforge.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_gloomrel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_gorosh_the_dervish.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_grizzle.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_haterel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_high_interrogator_gerstahn.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_magmus.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_moira_bronzebeard.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_seethrel.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\scripts\zone\blackrock_depths\boss_vilerel.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Loch Modan"
+ >
+ <File
+ RelativePath="..\scripts\zone\loch_modan\loch_modan.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Include"
+ >
+ <File
+ RelativePath="..\include\precompiled.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\include\precompiled.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\sc_creature.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\include\sc_creature.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\sc_gossip.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\sc_instance.h"
+ >
+ </File>
+ <File
+ RelativePath="..\include\sc_item.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath="..\config.h"
+ >
+ </File>
+ <File
+ RelativePath="..\ScriptMgr.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\ScriptMgr.h"
+ >
+ </File>
+ <File
+ RelativePath="..\svn_revision.h"
+ >
+ </File>
+ <File
+ RelativePath="..\system.cpp"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/bindings/scripts/docs/EventAI.txt b/src/bindings/scripts/docs/EventAI.txt
index f1f5a1e9f7f..1549fe74f1c 100644
--- a/src/bindings/scripts/docs/EventAI.txt
+++ b/src/bindings/scripts/docs/EventAI.txt
@@ -1,702 +1,702 @@
-=========================================
-Event AI documentation
-=========================================
-
-Scriptdev2 Revision 220 introduces a new database defined AI named EventAI.
-This system allows users to create new creature scripts entierly within the Database.
-ScriptName must still be set to "mob_eventai" within the MaNGOS database creature_template.scriptname field.
-
-=========================================
-Basic Structure of EventAI
-=========================================
-Event AI follows a basic if (Event) then do {Action} format.
-Below is a the list of current fields within the Eventai_scripts table.
-
-(Field_Name Discription)
-id This value is mearly an incrementing counter of the current Event number. Required for sql queries.
-creature_id Creature id which this event should occur on.
-
-event_type Type of event (See Event Types below)
-event_inverse_phase_mask Mask which phases this event should NOT trigger in*
-event_chance Percent chance of this event occuring (1 - 100)
-event_flags Event flags such as if the event is repeatable (see below)
-event_param1 Variable for event (dependant on Event type)
-event_param2
-event_param3
-event_param4
-
-action1_type First Type of Action to take when event occurs (See Action Types below)
-action1_param1 Variables used for Action1 (dependant on Action type)
-action1_param2
-action1_param3
-
-action2_type Second Type of Action to take when event occurs (See Action Types below)
-action2_param1 Variables used for Action2 (dependant on Action type)
-action2_param2
-action2_param3
-
-action3_type Third Type of Action to take when event occurs (See Action Types below)
-action3_param1 Variables used for Action3 (dependant on Action type)
-action3_param2
-action3_param3
-
-All params are signed 32 bit values (+/- 2147483647). If param specifies time then time is in milliseconds. If param specifies percentage then percentages are value/100 (ex: if param = 500 then that means 500%, -50 = -50%)
-
-*Phase mask is a bit mask of which phases this event should not trigger in. Example: Phase mask value of 12 (1100) would mean that this event would trigger 0, 1 and all other phases except for 2 and 3 (0 counts as the first phase).
-
-=========================================
-Event Types
-=========================================
-Below is the list of current Event types that EventAI can handle.
-Each event type has its own specific interpretation of the params that accompany it.
-Params are always read from Param1, then Param2, then Param3.
-Events will not repeat until the creature exits combat unless EFLAG_REPEATABLE is set. Some events such as EVENT_T_AGGRO, EVENT_T_DEATH, EVENT_T_SPAWNED, and EVENT_T_EVADE cannot repeat.
-
-# Internal Name Pamarm usage Description
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-0 EVENT_T_TIMER InitialMin, InitialMax, RepeatMin, RepeatMax Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4). but only in combat.
-1 EVENT_T_TIMER_OOC InitialMin, InitialMax, RepeatMin, RepeatMax Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4). but only out of combat.
-2 EVENT_T_HP HPMax%, HPMin%, RepeatMin, RepeatMax Expires when HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
-3 EVENT_T_MANA ManaMax%,ManaMin% RepeatMin, RepeatMax Expires once Mana% is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
-4 EVENT_T_AGGRO NONE Expires upon initial aggro (does not repeat).
-5 EVENT_T_KILL RepeatMin, RepeatMax Expires upon killing a player. Will repeat between every (Param1) and (Param2).
-6 EVENT_T_DEATH NONE Expires upon Death of the Creature.
-7 EVENT_T_EVADE NONE Expires upon creature EnterEvadeMode().
-8 EVENT_T_SPELLHIT SpellID, School, RepeatMin, RepeatMax Expires upon Spell hit. If (param1) is set will only expire on that spell. If (param2) will only expire on spells of that school (-1 for all). Will repeat every (Param3) and (Param4) .
-9 EVENT_T_RANGE MinDist, MaxDist, RepeatMin, RepeatMax Expires when the highest threat target distance is greater than (Param1) and less than (Param2). Will repeat every (Param3) and (Param4) .
-10 EVENT_T_OOC_LOS NoHostile, NoFriendly, RepeatMin, RepeatMax Expires when a Player moves within visible distance to creature. Does not expire for Hostile Players if (Param1) is not 0. Does not expire for Friendly Players if (Param2) is not 0. Will repeat every (Param3) and (Param4) . Does not expire for creatures or pet or when the creature is in combat.
-11 EVENT_T_SPAWNED NONE Expires at initial spawn and at creature respawn (useful for setting ranged movement type)
-12 EVENT_T_TARGET_HP HPMax%, HPMin%, RepeatMin, RepeatMax Expires when Current Target's HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4) .
-13 EVENT_T_TARGET_CASTING RepeatMin, RepeatatMax Expires when the current target is casting a spell. Will repeat every (Param1) and (Param2) .
-14 EVENT_T_FRIENDLY_HP HPDeficit, Radius, RepeatMin, RepeatMax Expires when a friendly unit in radius has at least (param1) hp missing. Will repeat every (Param3) and (Param4) .
-15 EVENT_T_FRIENDLY_IS_CC DispelType, Radius, RepeatMin, RepeatMax Expires when a friendly unit is Crowd controlled within the given radius (param2). Will repeat every (Param3) and (Param4) .
-16 EVENT_T_MISSING_BUFF SpellId, Radius, RepeatMin, RepeatMax Expires when a friendly unit is missing aura's given by spell (param1) within radius (param2). Will repeat every (Param3) and (Param4) .
-17 EVENT_T_SUMMONED_UNIT CreatureId, RepeatMin, RepeatMax Expires after creature with entry = (param1) is spawned or for all spawns if param1 = 0. Will repeat every (Param2) and (Param3) .
-
-=========================================
-Action Types
-=========================================
-Below is the list of current Action types that EventAI can handle.
-Each event type has its own specific interpretation of the params that accompany it.
-Params are always read from Param1, then Param2, then Param3.
-
-(# Internal Name Param usage Discription)
-0 ACTION_T_NONE No Action Does Nothing
-1 ACTION_T_SAY TextId Says Text
-2 ACTION_T_YELL TextId Yells Text
-3 ACTION_T_TEXTEMOTE TextId Text Emotes Text
-4 ACTION_T_SOUND SoundId Plays Sound
-5 ACTION_T_EMOTE EmoteId Does emote
-6 ACTION_T_RANDOM_SAY TextId1, TextId2, TextId3 Says random text between 3 params*
-7 ACTION_T_RANDOM_YELL TextId1, TextId2, TextId3 Yells random text between 3 params*
-8 ACTION_T_RANDOM_TEXTEMOTE TextId1, TextId2, TextId3 Text Emotes random text between 3 params*
-9 ACTION_T_RANDOM_SOUND SoundId1, SoundId2, SoundId3 Plays random sound between 3 params*
-10 ACTION_T_RANDOM_EMOTE EmoteId1, EmoteId2, EmoteId3 Emotes random emote between 3 params
-11 ACTION_T_CAST SpellId, Target, CastFlags Casts spell (param1) on target type (param2). Uses Cast Flags (specified below target types)
-12 ACTION_T_SUMMON CreatureID, Target, Duration Summons creature (param1) to attack target (param2) for (param3) duration. Spawns on top of current creature.
-13 ACTION_T_THREAT_SINGLE_PCT Threat%, Target Modifies threat by (param1) on target type (param2)
-14 ACTION_T_THREAT_ALL_PCT Threat% Modifies threat by (param1) on all targets (using -100% on all will result in full aggro dump)
-15 ACTION_T_QUEST_EVENT QuestID, Target Calls AreaExploredOrEventHappens with (param1) for target type (Param2)
-16 ACTION_T_QUEST_CASTCREATUREGO CreatureID, SpellId, Target Sends CastCreatureOrGo for CreatureId (param1) with SpellId (param2) for target (param3)
-17 ACTION_T_SET_UNIT_FIELD Field_Number, Value, Target Sets Unit Field (param1) to Value (param2) on target type (param3)
-18 ACTION_T_SET_UNIT_FLAG Flags, Target Sets flag (flags can be binary OR together to modify multiple flags at once) on for Target type (param2)
-19 ACTION_T_REMOVE_UNIT_FLAG Flags, Target Removes flag (flags can be binary OR together to modify multiple flags at once) on for Target type (param2)
-20 ACTION_T_AUTO_ATTACK AllowAutoAttack 0 = stop melee attack, anything else means continue attacking/allow melee attacking
-21 ACTION_T_COMBAT_MOVEMENT AllowCombatMovement 0 = stop combat based movement, anything else continue/allow combat based movement (targeted movement generator)
-22 ACTION_T_SET_PHASE Phase Sets the current phase to (param1)
-23 ACTION_T_INC_PHASE Value Increments the phase by (param1). May be negative to decrement phase but should not be 0.
-24 ACTION_T_EVADE No Params Forces the creature to evade. Wiping all threat and dropping combat.
-25 ACTION_T_FLEE No Params Causes the .creature to flee. Please use this action instead of directly casting this spell so we may change this when a more correct approach is found.
-26 ACTION_T_QUEST_EVENT_ALL QuestId Calls GroupEventHappens with (param1). Only used if it's _expected_ event should complete for all players in current party
-27 ACTION_T_CASTCREATUREGO_ALL QuestId, SpellId Calls CastedCreatureOrGo for all players on the threat list with QuestID(Param1) and SpellId(Param2)
-28 ACTION_T_REMOVEAURASFROMSPELL Target, Spellid Removes all auras on Target caused by Spellid
-29 ACTION_T_RANGED_MOVEMENT Distance, Angle Changes the movement generator type to a ranged type. Note: Default melee type can still be done with this. Specify 0 angle and 0 distance.
-30 ACTION_T_RANDOM_PHASE PhaseId1, PhaseId2, PhaseId3 Sets the phase to the id between 3 params*
-31 ACTION_T_RANDOM_PHASE_RANGE PhaseMin, PhaseMax Sets the phase to a random id (Phase = PhaseMin + rnd % PhaseMin-PhaseMax). PhaseMax must be greater than PhaseMin.
-32 ACTION_T_SUMMON CreatureID, Target, SummonID Summons creature (param1) to attack target (param2) at location specified by EventAI_Summons (param3).
-33 ACTION_T_KILLED_MONSTER CreatureID, Target Calls KilledMonster (param1) for target of type (param2)
-34 ACTION_T_SET_INST_DATA Field, Data Calls ScriptedInstance::SetData with field (param1) and data (param2)
-35 ACTION_T_SET_INST_DATA64 Field, Target Calls ScriptedInstance::SetData64 with field (param1) and data (param2) target's GUID.
-36 ACTION_T_UPDATE_TEMPLATE TemplateId, Team Changes the creature to a new creature template of (param1) with team = Alliance if (param2) = false or Horde if (param2) = true
-37 ACTION_T_DIE No Params Kills the creature
-38 ACTION_T_ZONE_COMBAT_PULSE No Params Places all players within the instance into combat with the creature. Only works in combat and only works inside of instances.
-
-* = Use -1 to specify that if this param is picked to do nothing. Random is constant between actions within an event. So if you have a random Yell and a random Sound they will match up (ex: param2 with param2)
-
-=========================================
-Event Types
-=========================================
-Note:
-COMBAT ONLY - Means that this event will only trigger durring combat.
-OUT OF COMBAT ONLY - Means that this event will only trigger while out of combat.
-BOTH - This event can trigger both in and out of combat.
-
-Events that do not have lables on them are events that are directly involved with the in and out of combat state.
-
-------------------
-0 = EVENT_T_TIMER:
-------------------
-Parameter 1: InitialMin - Minumum Time used to calculate Random Initial Expire
-Parameter 2: InitialMax - Maximum Time used to calculate Random Initial Expire
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-COMBAT ONLY! - Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4) from then on.
-This is commonly used for spells that repeat cast during combat (Simulate Spell Cooldown).
-
-----------------------
-1 = EVENT_T_TIMER_OOC:
-----------------------
-Parameter 1: InitialMin - Minumum Time used to calculate Random Initial Expire
-Parameter 2: InitialMax - Maximum Time used to calculate Random Initial Expire
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-OUT OF COMBAT ONLY! - Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4) from then on.
-This is commonly used for events that occur and repeat outside of combat.
-
----------------
-2 = EVENT_T_HP:
----------------
-Parameter 1: HPMax% - Maximum HP% That this Event will Expire
-Parameter 2: HPMin% - Minimum HP% That this Event will Expire
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-BOTH - Expires when HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
-This is commonly used for events that trigger at a specific HP% (Such as Heal/Enrage Spells or NPC's that Flee).
-
------------------
-3 = EVENT_T_MANA:
------------------
-Parameter 1: ManaMax% - Maximum Mana% That this Event will Expire
-Parameter 2: ManaMin% - Minimum Mana% That this Event will Expire
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-BOTH - Expires once Mana% is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
-This is commonly used for events where an NPC low on Mana will do something (Such as stop casting spells and switch to melee).
-
-------------------
-4 = EVENT_T_AGGRO:
-------------------
-This Event Expires upon initial aggro (does not repeat).
-
------------------
-5 = EVENT_T_KILL:
------------------
-Parameter 1: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 2: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-COMBAT ONLY! - Expires upon killing a player. Will repeat every (Param1) and (Param2).
-This Event Expires upon killing a player. It is commonly used for NPC's who yell or do something after killing a player.
-
-------------------
-6 = EVENT_T_DEATH:
-------------------
-This Event Expires upon Death of the Scripted NPC.
-This is commonly used for NPC's who have a yell on death or cast some kind if summon spell when they die.
-
-------------------
-7 = EVENT_T_EVADE:
-------------------
-This Event Expires upon the creature EnterEvadeMode().
-This is commonly used for NPC's who use phases, allows you to reset their phase to 0 upon evade to prevent possible strange behavior.
-
----------------------
-8 = EVENT_T_SPELLHIT:
----------------------
-Parameter 1: SpellID - The Spell ID that will trigger the event to occur (NOTE: If you use Spell School as the trigger set this value to 0)
-Parameter 2: School - Spell School to trigger the event (NOTE: If you use a SpellID then set this value to -1) - *See Below for Spell School Bitmask Values*
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-BOTH - Expires upon Spell hit. If (param1) is set will only expire on that spell. If (param2) will only expire on spells of that school. Will repeat every (Param3) and (Param4).
-This Event is commonly used for NPC's who can do special things when you cast a spell (Or specific spell) on them.
-
-------------------
-9 = EVENT_T_RANGE:
-------------------
-Parameter 1: MinDist - This Distance is the Minimum Distance between the NPC and it's target to allow this Event to Expire
-Parameter 2: MaxDist - This Distance is the Maximum Distance between the NPC and it's target to allow this Event to Expire
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-COMBAT ONLY! - Expires when the highest threat target distance is greater than (Param1) and less than (Param2). Will repeat every (Param3) and (Param4).
-This Event is commonly used for NPC's who have Ranged Combat and will Throw/Shoot between a certian distance.
-
----------------------
-10 = EVENT_T_OOC_LOS:
----------------------
-Parameter 1: NoHostile - This Value is to Prevent this Action from Expiring When Caused by a Player/Creature Hostile to them (0 = Prevent Event from Expiring, 1 = Allow Event to Expire)
-Parameter 2: NoFriendly - This Value is to Prevent this Action from Expiring When Caused by a Player/Creature Friendly to them (0 = Prevent Event from Expiring, 1 = Allow Event to Expire)
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-OUT OF COMBAT ONLY! - Expires when a Player moves within visible distance to the NPC. Does not expire for Hostile Players if (Param1) is not 0. Does not expire for Friendly Players if (Param2) is not 0. Will repeat every (Param3) and (Param4). Does not expire for creatures or pets when they are in combat.
-This Event is commonly used for NPC's who Do Something or Say Something when you walk past them Out of Combat.
-
----------------------
-11 = EVENT_T_SPAWNED:
----------------------
-Expires at initial spawn and at creature respawn.
-This Event is commonly used for setting ranged movement type or Summoning a Pet on Spawn
-
------------------------
-12 = EVENT_T_TARGET_HP:
------------------------
-Parameter 1: HPMax% - Maximum HP% That this Event will Expire
-Parameter 2: HPMin% - Minimum HP% That this Event will Expire
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-COMBAT ONLY! - Expires when Current NPC's Target's HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
-This Event is commonly used for NPC's who have a special ability (Like Execute) that only casts when a Player HP is low.
-
-----------------------------
-13 = EVENT_T_TARGET_CASTING:
-----------------------------
-Parameter 1: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 2: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-COMBAT ONLY! - Expires when the current target is casting a spell. Will repeat every (Param1) and (Param2).
-This event is commonly used for NPC's who will cast a counter spell when their target starts to cast a spell.
-
--------------------------
-14 = EVENT_T_FRIENDLY_HP:
--------------------------
-Parameter 1: HPDeficit - This is the Amount of HP Missing from Full HP to trigger this event (You would need to calculate the amount of HP the event happens and subtract that from Full HP Value to get this number)
-Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies (Faction is Friendly To) for the missing amount of HP in Param1.
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-COMBAT ONLY! - Expires when a friendly unit in radius(param2) has at least (param1) hp missing. Will repeat every (Param3) and (Param4).
-This is commonly used when an NPC in Combat will heal a nearby Friendly NPC in Combat with a Heal/Renew Spell.
-
-----------------------------
-15 = EVENT_T_FRIENDLY_IS_CC:
-----------------------------
-Parameter 1: DispelType - Dispel Type to trigger the event - *See Below for Dispel Bitmask Values*
-Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies being Crowd Controlled
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-COMBAT ONLY! - Expires when a friendly unit is Crowd controlled within the given radius (param2). Will repeat every (Param3) and (Param4).
-This is commonly used for NPC's who can come to the resule of other Friendly NPC's if being Crowd Controlled
-
---------------------------
-16 = EVENT_T_MISSING_BUFF:
---------------------------
-Parameter 1: SpellId - This is the SpellID That the Aura Check will look for (If it is missing this Aura)
-Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies (Faction is Friendly To) for the missing Aura.
-Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-BOTH - Expires when a friendly unit is missing aura's given by spell (param1) within radius (param2). Will repeat every (Param3) and (Param4).
-This is commonly used for NPC's who watch friendly units for a debuff to end so they can recast it on them again.
-
----------------------------
-17 = EVENT_T_SUMMONED_UNIT:
----------------------------
-Parameter 1: CreatureId - The CreatureID that the NPC is watching to spawn to trigger this event
-Parameter 2: RepeatMin - Minimum Time used to calculate Random Repeat Expire
-Parameter 3: RepeatMax - Maximum Time used to calculate Random Repeat Expire
-
-BOTH - Expires after creature with entry(Param1) is spawned or for all spawns if param1 = 0. Will repeat every (Param2) and (Param3) .
-This is commonly used for NPC's who will do something special once another NPC is summoned. Usually used is Complex Scripts or Special Events.
-
-
-=========================================
-Action Types
-=========================================
-
------------------
-1 = ACTION_T_SAY:
------------------
-Parameter 1: The ID of the text that the NPC should SAY from the Localized_Texts Table.
-
-This action is pretty straightforward. When activated, the creature will SAY the specified text (Speech Bubble).
-
-------------------
-2 = ACTION_T_YELL:
-------------------
-Parameter 1: The ID of the text that the NPC should YELL from the Localized_Texts Table.
-
-The creature will YELL the specified text (Red Speech Bubble).
-
------------------------
-3 = ACTION_T_TEXTEMOTE:
------------------------
-Parameter 1: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table.
-
-When activated, the creature will do a text emote using the specified text. A text emote is what you would regularly see by using the /em slash command.
-NOTE: The text should be written with the assumption that the creature's name is the first word or words in the sentence (for example, "is doing a text emote" would appear as "XYZ is doing a text emote" for a creature named XYZ).
-
--------------------
-4 = ACTION_T_SOUND:
--------------------
-Parameter 1: The Sound ID to be played. (Sound IDs are contained in the DBC files.)
-
-The creature will play the specified sound.
-This is commonly used for Bosses who Yell and then also have a Voice for the same thing.
-
--------------------
-5 = ACTION_T_EMOTE:
--------------------
-Parameter 1: The Emote ID that the creature should perform. (Emote IDs are also contained in the DBC but they can be found in the mangos source as well).
-
-The creature will perform a visual emote. Unlike a text emote, a visual emote is one where the creature will actually move or perform a gesture.
-This is commonly used for NPC's who may perform a special action (Salute, Roar, ect...). Not all player emotes work for creature models.
-
-------------------------
-6 = ACTION_T_RANDOM_SAY:
-------------------------
-Parameter 1: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #1).
-Parameter 2: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #2).
-Parameter 3: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #3).
-
-Similar to the ACTION_T_SAY action, it will choose at random a text entry to SAY. This action will pick a random entry from the three.
-NOTE: If you want any of the options if selected to do nothing you set that Param to -1 value. Then if selected it will do nothing.
-NOTE: When using Random Select Actions, ALL The Actions using Random in a Single Event (Action 1,2,3) will select the SAME Random Choice when it expires. This can come in handy if you are doing a ACTION_T_RANDOM_YELL for Action1 and then the corresponding sounds for ACTION_T_RANDOM_SOUND in Action2. If it selects Random Choice #2 for the Yell it will Select Random Choice #2 for the sound so you can match them up as required.
-This is commonly used for NPC's who have several different Aggro Say's and you would like one of them selected at random.
-
--------------------------
-7 = ACTION_T_RANDOM_YELL:
--------------------------
-Parameter 1: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #1).
-Parameter 2: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #2).
-Parameter 3: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #3).
-
-Similar to the ACTION_T_YELL action, it will choose at random a text entry to YELL.
-
-------------------------------
-8 = ACTION_T_RANDOM_TEXTEMOTE:
-------------------------------
-Parameter 1: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #1).
-Parameter 2: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #2).
-Parameter 3: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #3).
-
-Similar to the ACTION_T_TEXTEMOTE action, it will choose at random a text entry to TEXTEMOTE.
-
---------------------------
-9 = ACTION_T_RANDOM_SOUND:
---------------------------
-Parameter 1: The Sound ID to be played as Random Choice #1.
-Parameter 2: The Sound ID to be played as Random Choice #2.
-Parameter 3: The Sound ID to be played as Random Choice #3.
-
-Similar to the ACTION_T_SOUND action, it will choose at random a sound to play.
-
----------------------------
-10 = ACTION_T_RANDOM_EMOTE:
----------------------------
-Parameter 1: The Emote ID to be played as Random Choice #1.
-Parameter 2: The Emote ID to be played as Random Choice #2.
-Parameter 3: The Emote ID to be played as Random Choice #3.
-
-Similar to the ACTION_T_EMOTE action, it will choose at random an Emote to Visually Perform.
-
--------------------
-11 = ACTION_T_CAST:
--------------------
-Parameter 1: SpellId - The Spell ID to use for the NPC to cast. The value used in this field needs to be a valid Spell ID.
-Parameter 2: Target - The Target Type defining who the creature should cast the spell at. The value in this field needs to be a valid Target Type as specified in the reference tables below.
-Parameter 3: CastFlags - See Table Below for Cast Flag Bitmask Values. If you are unsure what to set this value at leave it at 0.
-
-The creature will cast a spell specified by a spell ID on a target specified by the target type.
-This is commonly used for NPC's who cast spells.
-
----------------------
-12 = ACTION_T_SUMMON:
----------------------
-Parameter 1: CreatureID - The Creature Template ID to be Summoned. The value here needs to be a valid Creature Template ID.
-Parameter 2: Target - The Target Type defining who the Summoned creature will attack once spawned. The value in this field needs to be a valid Target Type as specified in the reference tables below.
-Parameter 3: Duration - The duration until the summoned creature should be unsummoned AFTER Combat ends. The value in this field is in milliseconds or 0.
-
-The NPC will Summon another creature at the same spot as itself that will attack the specified target.
-NOTE: Almost all Creature Summons have proper Summon Spells that should be used when possible. This Action is a powerful last resort option only to be used if nothing else works.
-NOTE: Using Target Type 0 will cause the Summoned creature to not attack anyone.
-NOTE: If Duration is set at 0, then the summoned creature will not despawn until it has died.
-This is used as a manual way to force an NPC to Summon.
---------------------------------
-13 = ACTION_T_THREAT_SINGLE_PCT:
---------------------------------
-Parameter 1: Threat% - Threat percent that should be modified. The value in this field can range from -100 to +100. If it is negative, threat will be taken away and if positive, threat will be added.
-Parameter 2: Target - The Target Type defining on whom the threat change should occur. The value in this field needs to be a valid target type as specified in the reference tables below.
-
-This action will modify the threat of a target in the creature's threat list by the specified percent.
-This is commonly used to allow an NPC to adjust the Threat to a single player.
-
------------------------------
-14 = ACTION_T_THREAT_ALL_PCT:
------------------------------
-Parameter 1: Threat% - The percent that should be used in modifying everyone's threat in the creature's threat list. The value here can range from -100 to +100.
-
-This action will modify the threat for everyone in the creature's threat list by the specified percent.
-NOTE: Using -100 will cause the creature to reset everyone's threat to 0 so that everyone has the same amount of threat. It will NOT remove anyone from the threat list.
-This is commonly used to allow an NPC to drop threat for all players to zero.
-
---------------------------
-15 = ACTION_T_QUEST_EVENT:
---------------------------
-Parameter 1: QuestID - The Quest Template ID. The value here must be a valid quest template ID. Furthermore, the quest should have SpecialFlags | 2 as it would need to be completed by an external event which is the activation of this action.
-Parameter 2: Target - The Target Type defining whom the quest should be completed for. The value in this field needs to be a valid target type as specified in the reference tables below.
-
-This action will satisfy the external completion requirement for the quest for the specified target defined by the target type.
-NOTE: This action can only be used with player targets so it must be ensured that the target type will point to a player.
-This is commonly used for Quests where only ONE player will gain credit for the quest.
-
------------------------------
-16 = ACTION_T_CASTCREATUREGO:
------------------------------
-Parameter 1: CreatureID - The Creature Template ID to be Summoned. The value here needs to be a valid Creature Template ID.
-Parameter 2: SpellId - The Spell ID to use to simulate the cast. The value used in this field needs to be a valid Spell ID.
-Parameter 3: Target - The Target Type defining whom the quest credit should be given to. The value in this field needs to be a valid target type as specified in the reference tables below.
-
-This action will call CastedCreatureOrGO() function for the player. It can be used to give quest credit for casting a spell on the creature.
-This is commonly used for NPC's who have a special requirement to have a Spell cast on them to complete a quest.
-
------------------------------
-17 = ACTION_T_SET_UNIT_FIELD:
------------------------------
-Parameter 1: Field_Number - The index of the Field Number to be changed. Use (http://wiki.udbforums.org/index.php/Character_data) for a list of indeces and what they control. Creatures only contain the OBJECT_FIELD_* and UNIT_FIELD_* fields. They do not contain the PLAYER_FIELD_* fields.
-Parameter 2: Value - The new value to be put in the field.
-Parameter 3: Target - The Target Type defining for whom the unit field should be changed. The value in this field needs to be a valid target type as specified in the reference tables below.
-
-When activated, this action can change the target's unit field values. More information on the field value indeces can be found at (http://wiki.udbforums.org/index.php/Character_data)
-
-----------------------------
-18 = ACTION_T_SET_UNIT_FLAG:
-----------------------------
-Parameter 1: Flags - The flag(s) to be set. Multiple flags can be set by using bitwise-OR on them (adding them together).
-Parameter 2: Target - The Target Type defining for whom the flags should be changed. The value in this field needs to be a valid Target Type as specified in the reference tables below.
-
-When activated, this action changes the target's flags by adding (turning on) more flags. For example, this action can make the creature unattackable/unselectable if the right flags are used.
-
--------------------------------
-19 = ACTION_T_REMOVE_UNIT_FLAG:
--------------------------------
-Parameter 1: Flags - The flag(s) to be removed. Multiple flags can be set by using bitwise-OR on them (adding them together).
-Parameter 2: Target - The target type defining for whom the flags should be changed. The value in this field needs to be a valid Target Type as specified in the reference tables below.
-
-When activated, this action changes the target's flags by removing (turning off) flags. For example, this action can make the creature normal after it was unattackable/unselectable if the right flags are used.
-
---------------------------
-20 = ACTION_T_AUTO_ATTACK:
---------------------------
-Parameter 1: AllowAutoAttack - If zero, then the creature will stop its melee attacks. If non-zero, then the creature will either continue its melee attacks (the action would then have no effect) or it will start its melee attacks on the target with the top threat if its melee attacks were previously stopped.
-
-This action controls whether or not the creature should stop or start the auto melee attack.
-NOTE: The ACID Dev Team has conformed to using either 0 or 1 for the Param values (0 = Stop Melee, 1 = Start Melee).
-This is commonly used in combination with EVENT_T_RANGE and ACTION_T_COMBAT_MOVEMENT for Ranged Combat for Mages and Spell Casters.
-
-------------------------------
-21 = ACTION_T_COMBAT_MOVEMENT:
-------------------------------
-Parameter 1: If zero, then the creature will stop moving towards its victim (if its victim gets out of melee range) and will be stationary. If non-zero, then the creature will either continue to follow its victim (the action would have no effect) or it will start to follow the target with the top threat if its movement was disabled before.
-
-This action controls whether or not the creature will always move towards its target.
-NOTE: The ACID Dev Team has conformed to using either 0 or 1 for the Param values. (0 = Stop Movement, 1 = Start Movement)
-This is commonly used with EVENT_T_RANGE and ACTION_T_AUTO_ATTACK for NPC's who engage in Ranged Comabt (Either Spells or Ranged Attacks)
-
-------------------------
-22 = ACTION_T_SET_PHASE:
-------------------------
-Parameter 1: The new phase to set the creature in. This number must be an integer between 0 and 31. Numbers outside of that range will result in an error.
-
-When activated, this action sets the creature's event to the specified value.
-NOTE: The creature's current Phase is NOT reset at creature evade. You must manually set the phase back to 0 at EVENT_T_RESET.
-NOTE: The value used for the Param is the actual Phase Number (Not The Event_Inverse_Phase_Mask)
-This is commonly used for complex scripts with several phases and you need to switch to a different phase.
-
-------------------------
-23 = ACTION_T_INC_PHASE:
-------------------------
-Parameter 1: Value - The number of phases to increase or decrease. Use negative values to decrease the current phase.
-
-When activated, this action will increase (or decrease) the current creature's phase.
-NOTE: After increasing or decreasing the phase by this action, the current phase must NOT be lower than 0 or exceed 31.
-This can be used instead of ACTION_T_SET_PHASE to change phases in scripts. Just a user friendly option for changing phases.
-
---------------------
-24 = ACTION_T_EVADE:
---------------------
-When activated, the creature will immediately exit out of combat, clear its threat list, and move back to its spawn point. Basically, this action will reset the whole encounter.
-NOTE: All Param Values Are 0 for this Action.
-
--------------------
-25 = ACTION_T_FLEE:
--------------------
-When activated, the creature will try to flee from combat. Currently this is done by it casting a fear-like spell on itself called "Run Away". A Better Flee system is in Development but will take time before it is implimented.
-NOTE: All Param Values Are 0 for this Action.
-
-------------------------------
-26 = ACTION_T_QUEST_EVENT_ALL:
-------------------------------
-Parameter 1: QuestId - The quest ID to finish for everyone.
-
-This action does the same thing as the ACTION_T_QUEST_EVENT does but it does it for all players in the creature's threat list.
-NOTE: If a player is not in the NPC's threat list for whatever reason, he/she won't get the quest completed.
-
----------------------------------
-27 = ACTION_T_CASTCREATUREGO_ALL:
----------------------------------
-Parameter 1: QuestId - The quest template ID.
-Parameter 2: SpellId - The spell ID used to simulate the cast.
-
-This action does the same thing as the ACTION_T_CASTCREATUREGO does but it does it for all players in the creature's threat list.
-NOTE: If a player is not in its threat list for whatever reason, he/she won't receive the cast emulation.
-
------------------------------------
-28 = ACTION_T_REMOVEAURASFROMSPELL:
------------------------------------
-Parameter 1: Target - The target type defining for whom the unit field should be changed. The value in this field needs to be a valid target type as specified in the reference tables below.
-Parameter 2: SpellId - The spell ID whose auras will be removed.
-
-This action will remove all auras from a specific spell from the target.
-This is commonly used for NPC's who have an OOC Aura that is removed at combat start or a similar idea (Like Stealth or Shape Shift)
-
-------------------------------
-29 = ACTION_T_RANGED_MOVEMENT:
-------------------------------
-Parameter 1: Distance - The distance the mob should keep between it and its target.
-Parameter 2: Angle - The angle the mob should use.
-
-This action changes the movement type generator to ranged type using the specified values for angle and distance.
-NOTE: Specifying zero angle and distance will make it just melee instead.
-This is commonly used for NPC's who always attack at range and you can specify the distance they will maintain from the target.
-
----------------------------
-30 = ACTION_T_RANDOM_PHASE:
----------------------------
-Parameter 1: PhaseId1 - A possible random phase choice.
-Parameter 2: PhaseId2 - A possible random phase choice.
-Parameter 3: PhaseId3 - A possible random phase choice.
-
-Randomly sets the phase to one from the three parameter choices.
-NOTE: Use -1 to specify that if this param is picked to do nothing. Random is constant between actions within an event. So if you have a random Yell and a random Sound they will match up (ex: param2 with param2)
-NOTE 2: PLEASE NOTE THAT EACH OF THE PARAM VALUES ARE ACTUAL PHASE NUMBERS NOT THE INVERSE PHASE MASK VALUE.
-This is commonly used for Spellcasting NPC's who on Aggro may select at random a school of spells to use for the fight. Use this if you have up to 3 phases used, otherwise use Action 31 for more then 3 phases.
-
----------------------------------
-31 = ACTION_T_RANDOM_PHASE_RANGE:
----------------------------------
-Parameter 1: PhaseMin - The minimum of the phase range.
-Parameter 2: PhaseMax - The maximum of the phase range. The number here must be greater than PhaseMin.
-
-Randomly sets the phase between a range of phases controlled by the parameters. Sets the phase to a random id (Phase = PhaseMin + rnd % PhaseMin-PhaseMax).
-NOTE: PhaseMax must be greater than PhaseMin.
-NOTE 2: PLEASE NOTE THAT EACH OF THE PARAM VALUES ARE ACTUAL PHASE NUMBERS NOT THE INVERSE PHASE MASK VALUE.
-This is commonly used for Spellcasting NPC's who on Aggro may select at random a school of spells to use for the fight. Use this if you have MORE then 3 phases used, otherwise use Action 30.
-
----------------------
-32 = ACTION_T_SUMMON:
----------------------
-Parameter 1: CreatureID - The creature template ID to be summoned. The value here needs to be a valid creature template ID.
-Parameter 2: Target - The target type defining who the summoned creature will attack. The value in this field needs to be a valid target type as specified in the reference tables below. NOTE: Using target type 0 will cause the summoned creature to not attack anyone.
-Parameter 3: SummonID - The summon ID from the eventai_summons table controlling the position (and spawntime) where the summoned mob should be spawned at.
-
-Summons creature (param1) to attack target (param2) at location specified by EventAI_Summons (param3).
-NOTE: Param3 Value is the ID Value used for the entry used in EventAI_Summons for this action. You MUST have an EventAI_Summons entry to use this action.
-This is commonly used for NPC's who need to Summon a creature at a specific location. (Normally used for complex events)
-
------------------------------
-33 = ACTION_T_KILLED_MONSTER:
------------------------------
-Parameter 1: CreatureID - The creature template ID. The value here must be a valid creature template ID.
-Parameter 2: Target - The target type defining whom the quest kill count should be given to. The value in this field needs to be a valid target type as specified in the reference tables below.
-
-When activated, this action will call KilledMonster() function for the player. It can be used to give creature credit for killing a creature. In general if the quest is set to be accompished on different creatures (e.g. "Credit" templates).
-NOTE: It can be ANY creature including certain quest specific triggers
-This is commonly used for giving the player Quest Credits for NPC kills (Many NPC's may use the same CreatureID for the Kill Credit)
-
-----------------------------
-34 = ACTION_T_SET_INST_DATA:
-----------------------------
-Parameter 1: Field - The field to change in the instance script. Again, this field needs to be a valid field that has been already defined in the instance's script.
-Parameter 2: Data - The value to put at that field index.
-
-Sets data for the instance. Note that this will only work when the creature is inside an instantiable zone that has a valid script (ScriptedInstance) assigned.
-NOTE: Param1 Value is located in "def_<instance name>.h" SD2 File and Param2 value is generally found in the "sc_instance.h" file in SD2
-This is commonly used to link an ACID script with a SD2 C++ Script. You make make things happen like opening doors on specific events that happen. ACID Just triggers the C++ Script to function.
-
-------------------------------
-35 = ACTION_T_SET_INST_DATA64:
-------------------------------
-Parameter 1: Field - The field to change in the instance script. Again, this field needs to be a valid field that has been already defined in the instance's script.
-Parameter 2: Target - The target type to use to get the GUID that will be stored at the field index. The value in this field needs to be a valid target type as specified in the reference tables below.
-
-Sets GUID (64 bits) data for the instance based on the target. Note that this will only work when the creature is inside an instantiable zone that has a valid script (ScriptedInstance) assigned.
-Calls ScriptedInstance::SetData64 with field (param1) and data (param2) target's GUID.
-
-------------------------------
-36 = ACTION_T_UPDATE_TEMPLATE:
-------------------------------
-Parameter 1: TemplateId - The creature template ID. The value here must be a valid creature template ID.
-Parameter 2: Team - Use model_id from team : Alliance(0) or Horde (1).
-
-This function temporarily changes creature entry to new entry, display is changed, loot is changed, but AI is not changed. At respawn creature will be reverted to original entry.
-Changes the creature to a new creature template of (param1) with team = Alliance if (param2) = false or Horde if (param2) = true
-
-------------------
-37 = ACTION_T_DIE:
-------------------
-Kills the creature
-This is commonly used if you need to Instakill the creature for one reason or another.
-
---------------------------------
-38 = ACTION_T_ZONE_COMBAT_PULSE:
---------------------------------
-Places all players within the instance into combat with the creature. Only works in combat and only works inside of instances.
-
-
-=========================================
-Target Types
-=========================================
-Below is the list of current Target types that EventAI can handle.
-Target types are used by certain actions and may effect actions differently
-
-(# Internal Name Discription)
-0 TARGET_T_SELF Self cast
-1 TARGET_T_HOSTILE Our current target (ie: highest aggro)
-2 TARGET_T_HOSTILE_SECOND_AGGRO Second highest aggro (generaly used for cleaves and some special attacks)
-3 TARGET_T_HOSTILE_LAST_AGGRO Dead last on aggro (no idea what this could be used for)
-4 TARGET_T_HOSTILE_RANDOM Just any random target on our threat list
-5 TARGET_T_HOSTILE_RANDOM_NOT_TOP Any random target except top threat
-6 TARGET_T_ACTION_INVOKER Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP)
-
-=========================================
-Cast Flags
-=========================================
-Below is the list of current Cast Flags that EventAI's spell casting can handle.
-Cast flags are handled bitwise. Bit 0 is Interrupt Previous, bit 1 is triggered, etc.
-So for example the number "3" (11 in Binary, selecting first 2 options) would mean that this cast has both CAST_INTURRUPT_PREVIOUS and CAST_TRIGGERED.
-Another example: the number "5" (101 in Binary, selecting first and third options) would mean that this cast has CAST_INTURRUPT_PREVIOUS and CAST_FORCE_CAST.
-
-(bit# Decimal Internal Name Discription)
-0 1 CAST_INTURRUPT_PREVIOUS Interrupts any previous spell casting (basicaly makes sure that this spell goes off)
-1 2 CAST_TRIGGERED Forces the spell to be instant cast and require no mana/reagents.
-2 4 CAST_FORCE_CAST Forces spell to cast even if the target is possibly out of range or the creature is possibly out of mana
-3 8 CAST_NO_MELEE_IF_OOM Prevents creature from entering melee if out of mana or out of range
-4 16 CAST_FORCE_TARGET_SELF Forces the target to cast this spell on itself
-5 32 CAST_AURA_NOT_PRESENT Only casts the spell on the target if the target does not have the aura from that spell on itself already.
-
-NOTE: You can add the numbers in the decimal column to combine flags.
- For example if you wanted to use CAST_NO_MELEE_IF_OOM(8) and CAST_TRIGGERED(2) you would simply use 10 in the cast flags field (8 + 2 = 10).
-
-=========================================
-Event Flags
-=========================================
-Below is the list of current Event Flags that EventAI can handle. Event flags are handled bitwise.
-
-(bit# Decimal Internal Name Discription)
-0 1 EFLAG_REPEATABLE Event repeats (Does not repeat if this flag is not set)
-1 2 EFLAG_NORMAL Event occurs in Normal instance difficulty (will not occur in Normal if not set)
-2 4 EFLAG_HEROIC Event occurs in Heroic instance difficulty (will not occur in Heroic if not set)
-3 8
-4 16
-5 32
-6 64
-7 128 EFLAG_DEBUG_ONLY Prevents events from occuring on Release builds of ScriptDev2. Useful for testing new features.
-
+=========================================
+Event AI documentation
+=========================================
+
+Scriptdev2 Revision 220 introduces a new database defined AI named EventAI.
+This system allows users to create new creature scripts entierly within the Database.
+ScriptName must still be set to "mob_eventai" within the MaNGOS database creature_template.scriptname field.
+
+=========================================
+Basic Structure of EventAI
+=========================================
+Event AI follows a basic if (Event) then do {Action} format.
+Below is a the list of current fields within the Eventai_scripts table.
+
+(Field_Name Discription)
+id This value is mearly an incrementing counter of the current Event number. Required for sql queries.
+creature_id Creature id which this event should occur on.
+
+event_type Type of event (See Event Types below)
+event_inverse_phase_mask Mask which phases this event should NOT trigger in*
+event_chance Percent chance of this event occuring (1 - 100)
+event_flags Event flags such as if the event is repeatable (see below)
+event_param1 Variable for event (dependant on Event type)
+event_param2
+event_param3
+event_param4
+
+action1_type First Type of Action to take when event occurs (See Action Types below)
+action1_param1 Variables used for Action1 (dependant on Action type)
+action1_param2
+action1_param3
+
+action2_type Second Type of Action to take when event occurs (See Action Types below)
+action2_param1 Variables used for Action2 (dependant on Action type)
+action2_param2
+action2_param3
+
+action3_type Third Type of Action to take when event occurs (See Action Types below)
+action3_param1 Variables used for Action3 (dependant on Action type)
+action3_param2
+action3_param3
+
+All params are signed 32 bit values (+/- 2147483647). If param specifies time then time is in milliseconds. If param specifies percentage then percentages are value/100 (ex: if param = 500 then that means 500%, -50 = -50%)
+
+*Phase mask is a bit mask of which phases this event should not trigger in. Example: Phase mask value of 12 (1100) would mean that this event would trigger 0, 1 and all other phases except for 2 and 3 (0 counts as the first phase).
+
+=========================================
+Event Types
+=========================================
+Below is the list of current Event types that EventAI can handle.
+Each event type has its own specific interpretation of the params that accompany it.
+Params are always read from Param1, then Param2, then Param3.
+Events will not repeat until the creature exits combat unless EFLAG_REPEATABLE is set. Some events such as EVENT_T_AGGRO, EVENT_T_DEATH, EVENT_T_SPAWNED, and EVENT_T_EVADE cannot repeat.
+
+# Internal Name Pamarm usage Description
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+0 EVENT_T_TIMER InitialMin, InitialMax, RepeatMin, RepeatMax Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4). but only in combat.
+1 EVENT_T_TIMER_OOC InitialMin, InitialMax, RepeatMin, RepeatMax Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4). but only out of combat.
+2 EVENT_T_HP HPMax%, HPMin%, RepeatMin, RepeatMax Expires when HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
+3 EVENT_T_MANA ManaMax%,ManaMin% RepeatMin, RepeatMax Expires once Mana% is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
+4 EVENT_T_AGGRO NONE Expires upon initial aggro (does not repeat).
+5 EVENT_T_KILL RepeatMin, RepeatMax Expires upon killing a player. Will repeat between every (Param1) and (Param2).
+6 EVENT_T_DEATH NONE Expires upon Death of the Creature.
+7 EVENT_T_EVADE NONE Expires upon creature EnterEvadeMode().
+8 EVENT_T_SPELLHIT SpellID, School, RepeatMin, RepeatMax Expires upon Spell hit. If (param1) is set will only expire on that spell. If (param2) will only expire on spells of that school (-1 for all). Will repeat every (Param3) and (Param4) .
+9 EVENT_T_RANGE MinDist, MaxDist, RepeatMin, RepeatMax Expires when the highest threat target distance is greater than (Param1) and less than (Param2). Will repeat every (Param3) and (Param4) .
+10 EVENT_T_OOC_LOS NoHostile, NoFriendly, RepeatMin, RepeatMax Expires when a Player moves within visible distance to creature. Does not expire for Hostile Players if (Param1) is not 0. Does not expire for Friendly Players if (Param2) is not 0. Will repeat every (Param3) and (Param4) . Does not expire for creatures or pet or when the creature is in combat.
+11 EVENT_T_SPAWNED NONE Expires at initial spawn and at creature respawn (useful for setting ranged movement type)
+12 EVENT_T_TARGET_HP HPMax%, HPMin%, RepeatMin, RepeatMax Expires when Current Target's HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4) .
+13 EVENT_T_TARGET_CASTING RepeatMin, RepeatatMax Expires when the current target is casting a spell. Will repeat every (Param1) and (Param2) .
+14 EVENT_T_FRIENDLY_HP HPDeficit, Radius, RepeatMin, RepeatMax Expires when a friendly unit in radius has at least (param1) hp missing. Will repeat every (Param3) and (Param4) .
+15 EVENT_T_FRIENDLY_IS_CC DispelType, Radius, RepeatMin, RepeatMax Expires when a friendly unit is Crowd controlled within the given radius (param2). Will repeat every (Param3) and (Param4) .
+16 EVENT_T_MISSING_BUFF SpellId, Radius, RepeatMin, RepeatMax Expires when a friendly unit is missing aura's given by spell (param1) within radius (param2). Will repeat every (Param3) and (Param4) .
+17 EVENT_T_SUMMONED_UNIT CreatureId, RepeatMin, RepeatMax Expires after creature with entry = (param1) is spawned or for all spawns if param1 = 0. Will repeat every (Param2) and (Param3) .
+
+=========================================
+Action Types
+=========================================
+Below is the list of current Action types that EventAI can handle.
+Each event type has its own specific interpretation of the params that accompany it.
+Params are always read from Param1, then Param2, then Param3.
+
+(# Internal Name Param usage Discription)
+0 ACTION_T_NONE No Action Does Nothing
+1 ACTION_T_SAY TextId Says Text
+2 ACTION_T_YELL TextId Yells Text
+3 ACTION_T_TEXTEMOTE TextId Text Emotes Text
+4 ACTION_T_SOUND SoundId Plays Sound
+5 ACTION_T_EMOTE EmoteId Does emote
+6 ACTION_T_RANDOM_SAY TextId1, TextId2, TextId3 Says random text between 3 params*
+7 ACTION_T_RANDOM_YELL TextId1, TextId2, TextId3 Yells random text between 3 params*
+8 ACTION_T_RANDOM_TEXTEMOTE TextId1, TextId2, TextId3 Text Emotes random text between 3 params*
+9 ACTION_T_RANDOM_SOUND SoundId1, SoundId2, SoundId3 Plays random sound between 3 params*
+10 ACTION_T_RANDOM_EMOTE EmoteId1, EmoteId2, EmoteId3 Emotes random emote between 3 params
+11 ACTION_T_CAST SpellId, Target, CastFlags Casts spell (param1) on target type (param2). Uses Cast Flags (specified below target types)
+12 ACTION_T_SUMMON CreatureID, Target, Duration Summons creature (param1) to attack target (param2) for (param3) duration. Spawns on top of current creature.
+13 ACTION_T_THREAT_SINGLE_PCT Threat%, Target Modifies threat by (param1) on target type (param2)
+14 ACTION_T_THREAT_ALL_PCT Threat% Modifies threat by (param1) on all targets (using -100% on all will result in full aggro dump)
+15 ACTION_T_QUEST_EVENT QuestID, Target Calls AreaExploredOrEventHappens with (param1) for target type (Param2)
+16 ACTION_T_QUEST_CASTCREATUREGO CreatureID, SpellId, Target Sends CastCreatureOrGo for CreatureId (param1) with SpellId (param2) for target (param3)
+17 ACTION_T_SET_UNIT_FIELD Field_Number, Value, Target Sets Unit Field (param1) to Value (param2) on target type (param3)
+18 ACTION_T_SET_UNIT_FLAG Flags, Target Sets flag (flags can be binary OR together to modify multiple flags at once) on for Target type (param2)
+19 ACTION_T_REMOVE_UNIT_FLAG Flags, Target Removes flag (flags can be binary OR together to modify multiple flags at once) on for Target type (param2)
+20 ACTION_T_AUTO_ATTACK AllowAutoAttack 0 = stop melee attack, anything else means continue attacking/allow melee attacking
+21 ACTION_T_COMBAT_MOVEMENT AllowCombatMovement 0 = stop combat based movement, anything else continue/allow combat based movement (targeted movement generator)
+22 ACTION_T_SET_PHASE Phase Sets the current phase to (param1)
+23 ACTION_T_INC_PHASE Value Increments the phase by (param1). May be negative to decrement phase but should not be 0.
+24 ACTION_T_EVADE No Params Forces the creature to evade. Wiping all threat and dropping combat.
+25 ACTION_T_FLEE No Params Causes the .creature to flee. Please use this action instead of directly casting this spell so we may change this when a more correct approach is found.
+26 ACTION_T_QUEST_EVENT_ALL QuestId Calls GroupEventHappens with (param1). Only used if it's _expected_ event should complete for all players in current party
+27 ACTION_T_CASTCREATUREGO_ALL QuestId, SpellId Calls CastedCreatureOrGo for all players on the threat list with QuestID(Param1) and SpellId(Param2)
+28 ACTION_T_REMOVEAURASFROMSPELL Target, Spellid Removes all auras on Target caused by Spellid
+29 ACTION_T_RANGED_MOVEMENT Distance, Angle Changes the movement generator type to a ranged type. Note: Default melee type can still be done with this. Specify 0 angle and 0 distance.
+30 ACTION_T_RANDOM_PHASE PhaseId1, PhaseId2, PhaseId3 Sets the phase to the id between 3 params*
+31 ACTION_T_RANDOM_PHASE_RANGE PhaseMin, PhaseMax Sets the phase to a random id (Phase = PhaseMin + rnd % PhaseMin-PhaseMax). PhaseMax must be greater than PhaseMin.
+32 ACTION_T_SUMMON CreatureID, Target, SummonID Summons creature (param1) to attack target (param2) at location specified by EventAI_Summons (param3).
+33 ACTION_T_KILLED_MONSTER CreatureID, Target Calls KilledMonster (param1) for target of type (param2)
+34 ACTION_T_SET_INST_DATA Field, Data Calls ScriptedInstance::SetData with field (param1) and data (param2)
+35 ACTION_T_SET_INST_DATA64 Field, Target Calls ScriptedInstance::SetData64 with field (param1) and data (param2) target's GUID.
+36 ACTION_T_UPDATE_TEMPLATE TemplateId, Team Changes the creature to a new creature template of (param1) with team = Alliance if (param2) = false or Horde if (param2) = true
+37 ACTION_T_DIE No Params Kills the creature
+38 ACTION_T_ZONE_COMBAT_PULSE No Params Places all players within the instance into combat with the creature. Only works in combat and only works inside of instances.
+
+* = Use -1 to specify that if this param is picked to do nothing. Random is constant between actions within an event. So if you have a random Yell and a random Sound they will match up (ex: param2 with param2)
+
+=========================================
+Event Types
+=========================================
+Note:
+COMBAT ONLY - Means that this event will only trigger durring combat.
+OUT OF COMBAT ONLY - Means that this event will only trigger while out of combat.
+BOTH - This event can trigger both in and out of combat.
+
+Events that do not have lables on them are events that are directly involved with the in and out of combat state.
+
+------------------
+0 = EVENT_T_TIMER:
+------------------
+Parameter 1: InitialMin - Minumum Time used to calculate Random Initial Expire
+Parameter 2: InitialMax - Maximum Time used to calculate Random Initial Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4) from then on.
+This is commonly used for spells that repeat cast during combat (Simulate Spell Cooldown).
+
+----------------------
+1 = EVENT_T_TIMER_OOC:
+----------------------
+Parameter 1: InitialMin - Minumum Time used to calculate Random Initial Expire
+Parameter 2: InitialMax - Maximum Time used to calculate Random Initial Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+OUT OF COMBAT ONLY! - Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4) from then on.
+This is commonly used for events that occur and repeat outside of combat.
+
+---------------
+2 = EVENT_T_HP:
+---------------
+Parameter 1: HPMax% - Maximum HP% That this Event will Expire
+Parameter 2: HPMin% - Minimum HP% That this Event will Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+BOTH - Expires when HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
+This is commonly used for events that trigger at a specific HP% (Such as Heal/Enrage Spells or NPC's that Flee).
+
+-----------------
+3 = EVENT_T_MANA:
+-----------------
+Parameter 1: ManaMax% - Maximum Mana% That this Event will Expire
+Parameter 2: ManaMin% - Minimum Mana% That this Event will Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+BOTH - Expires once Mana% is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
+This is commonly used for events where an NPC low on Mana will do something (Such as stop casting spells and switch to melee).
+
+------------------
+4 = EVENT_T_AGGRO:
+------------------
+This Event Expires upon initial aggro (does not repeat).
+
+-----------------
+5 = EVENT_T_KILL:
+-----------------
+Parameter 1: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 2: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires upon killing a player. Will repeat every (Param1) and (Param2).
+This Event Expires upon killing a player. It is commonly used for NPC's who yell or do something after killing a player.
+
+------------------
+6 = EVENT_T_DEATH:
+------------------
+This Event Expires upon Death of the Scripted NPC.
+This is commonly used for NPC's who have a yell on death or cast some kind if summon spell when they die.
+
+------------------
+7 = EVENT_T_EVADE:
+------------------
+This Event Expires upon the creature EnterEvadeMode().
+This is commonly used for NPC's who use phases, allows you to reset their phase to 0 upon evade to prevent possible strange behavior.
+
+---------------------
+8 = EVENT_T_SPELLHIT:
+---------------------
+Parameter 1: SpellID - The Spell ID that will trigger the event to occur (NOTE: If you use Spell School as the trigger set this value to 0)
+Parameter 2: School - Spell School to trigger the event (NOTE: If you use a SpellID then set this value to -1) - *See Below for Spell School Bitmask Values*
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+BOTH - Expires upon Spell hit. If (param1) is set will only expire on that spell. If (param2) will only expire on spells of that school. Will repeat every (Param3) and (Param4).
+This Event is commonly used for NPC's who can do special things when you cast a spell (Or specific spell) on them.
+
+------------------
+9 = EVENT_T_RANGE:
+------------------
+Parameter 1: MinDist - This Distance is the Minimum Distance between the NPC and it's target to allow this Event to Expire
+Parameter 2: MaxDist - This Distance is the Maximum Distance between the NPC and it's target to allow this Event to Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires when the highest threat target distance is greater than (Param1) and less than (Param2). Will repeat every (Param3) and (Param4).
+This Event is commonly used for NPC's who have Ranged Combat and will Throw/Shoot between a certian distance.
+
+---------------------
+10 = EVENT_T_OOC_LOS:
+---------------------
+Parameter 1: NoHostile - This Value is to Prevent this Action from Expiring When Caused by a Player/Creature Hostile to them (0 = Prevent Event from Expiring, 1 = Allow Event to Expire)
+Parameter 2: NoFriendly - This Value is to Prevent this Action from Expiring When Caused by a Player/Creature Friendly to them (0 = Prevent Event from Expiring, 1 = Allow Event to Expire)
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+OUT OF COMBAT ONLY! - Expires when a Player moves within visible distance to the NPC. Does not expire for Hostile Players if (Param1) is not 0. Does not expire for Friendly Players if (Param2) is not 0. Will repeat every (Param3) and (Param4). Does not expire for creatures or pets when they are in combat.
+This Event is commonly used for NPC's who Do Something or Say Something when you walk past them Out of Combat.
+
+---------------------
+11 = EVENT_T_SPAWNED:
+---------------------
+Expires at initial spawn and at creature respawn.
+This Event is commonly used for setting ranged movement type or Summoning a Pet on Spawn
+
+-----------------------
+12 = EVENT_T_TARGET_HP:
+-----------------------
+Parameter 1: HPMax% - Maximum HP% That this Event will Expire
+Parameter 2: HPMin% - Minimum HP% That this Event will Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires when Current NPC's Target's HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
+This Event is commonly used for NPC's who have a special ability (Like Execute) that only casts when a Player HP is low.
+
+----------------------------
+13 = EVENT_T_TARGET_CASTING:
+----------------------------
+Parameter 1: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 2: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires when the current target is casting a spell. Will repeat every (Param1) and (Param2).
+This event is commonly used for NPC's who will cast a counter spell when their target starts to cast a spell.
+
+-------------------------
+14 = EVENT_T_FRIENDLY_HP:
+-------------------------
+Parameter 1: HPDeficit - This is the Amount of HP Missing from Full HP to trigger this event (You would need to calculate the amount of HP the event happens and subtract that from Full HP Value to get this number)
+Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies (Faction is Friendly To) for the missing amount of HP in Param1.
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires when a friendly unit in radius(param2) has at least (param1) hp missing. Will repeat every (Param3) and (Param4).
+This is commonly used when an NPC in Combat will heal a nearby Friendly NPC in Combat with a Heal/Renew Spell.
+
+----------------------------
+15 = EVENT_T_FRIENDLY_IS_CC:
+----------------------------
+Parameter 1: DispelType - Dispel Type to trigger the event - *See Below for Dispel Bitmask Values*
+Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies being Crowd Controlled
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires when a friendly unit is Crowd controlled within the given radius (param2). Will repeat every (Param3) and (Param4).
+This is commonly used for NPC's who can come to the resule of other Friendly NPC's if being Crowd Controlled
+
+--------------------------
+16 = EVENT_T_MISSING_BUFF:
+--------------------------
+Parameter 1: SpellId - This is the SpellID That the Aura Check will look for (If it is missing this Aura)
+Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies (Faction is Friendly To) for the missing Aura.
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+BOTH - Expires when a friendly unit is missing aura's given by spell (param1) within radius (param2). Will repeat every (Param3) and (Param4).
+This is commonly used for NPC's who watch friendly units for a debuff to end so they can recast it on them again.
+
+---------------------------
+17 = EVENT_T_SUMMONED_UNIT:
+---------------------------
+Parameter 1: CreatureId - The CreatureID that the NPC is watching to spawn to trigger this event
+Parameter 2: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 3: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+BOTH - Expires after creature with entry(Param1) is spawned or for all spawns if param1 = 0. Will repeat every (Param2) and (Param3) .
+This is commonly used for NPC's who will do something special once another NPC is summoned. Usually used is Complex Scripts or Special Events.
+
+
+=========================================
+Action Types
+=========================================
+
+-----------------
+1 = ACTION_T_SAY:
+-----------------
+Parameter 1: The ID of the text that the NPC should SAY from the Localized_Texts Table.
+
+This action is pretty straightforward. When activated, the creature will SAY the specified text (Speech Bubble).
+
+------------------
+2 = ACTION_T_YELL:
+------------------
+Parameter 1: The ID of the text that the NPC should YELL from the Localized_Texts Table.
+
+The creature will YELL the specified text (Red Speech Bubble).
+
+-----------------------
+3 = ACTION_T_TEXTEMOTE:
+-----------------------
+Parameter 1: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table.
+
+When activated, the creature will do a text emote using the specified text. A text emote is what you would regularly see by using the /em slash command.
+NOTE: The text should be written with the assumption that the creature's name is the first word or words in the sentence (for example, "is doing a text emote" would appear as "XYZ is doing a text emote" for a creature named XYZ).
+
+-------------------
+4 = ACTION_T_SOUND:
+-------------------
+Parameter 1: The Sound ID to be played. (Sound IDs are contained in the DBC files.)
+
+The creature will play the specified sound.
+This is commonly used for Bosses who Yell and then also have a Voice for the same thing.
+
+-------------------
+5 = ACTION_T_EMOTE:
+-------------------
+Parameter 1: The Emote ID that the creature should perform. (Emote IDs are also contained in the DBC but they can be found in the mangos source as well).
+
+The creature will perform a visual emote. Unlike a text emote, a visual emote is one where the creature will actually move or perform a gesture.
+This is commonly used for NPC's who may perform a special action (Salute, Roar, ect...). Not all player emotes work for creature models.
+
+------------------------
+6 = ACTION_T_RANDOM_SAY:
+------------------------
+Parameter 1: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #1).
+Parameter 2: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #2).
+Parameter 3: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #3).
+
+Similar to the ACTION_T_SAY action, it will choose at random a text entry to SAY. This action will pick a random entry from the three.
+NOTE: If you want any of the options if selected to do nothing you set that Param to -1 value. Then if selected it will do nothing.
+NOTE: When using Random Select Actions, ALL The Actions using Random in a Single Event (Action 1,2,3) will select the SAME Random Choice when it expires. This can come in handy if you are doing a ACTION_T_RANDOM_YELL for Action1 and then the corresponding sounds for ACTION_T_RANDOM_SOUND in Action2. If it selects Random Choice #2 for the Yell it will Select Random Choice #2 for the sound so you can match them up as required.
+This is commonly used for NPC's who have several different Aggro Say's and you would like one of them selected at random.
+
+-------------------------
+7 = ACTION_T_RANDOM_YELL:
+-------------------------
+Parameter 1: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #1).
+Parameter 2: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #2).
+Parameter 3: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #3).
+
+Similar to the ACTION_T_YELL action, it will choose at random a text entry to YELL.
+
+------------------------------
+8 = ACTION_T_RANDOM_TEXTEMOTE:
+------------------------------
+Parameter 1: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #1).
+Parameter 2: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #2).
+Parameter 3: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #3).
+
+Similar to the ACTION_T_TEXTEMOTE action, it will choose at random a text entry to TEXTEMOTE.
+
+--------------------------
+9 = ACTION_T_RANDOM_SOUND:
+--------------------------
+Parameter 1: The Sound ID to be played as Random Choice #1.
+Parameter 2: The Sound ID to be played as Random Choice #2.
+Parameter 3: The Sound ID to be played as Random Choice #3.
+
+Similar to the ACTION_T_SOUND action, it will choose at random a sound to play.
+
+---------------------------
+10 = ACTION_T_RANDOM_EMOTE:
+---------------------------
+Parameter 1: The Emote ID to be played as Random Choice #1.
+Parameter 2: The Emote ID to be played as Random Choice #2.
+Parameter 3: The Emote ID to be played as Random Choice #3.
+
+Similar to the ACTION_T_EMOTE action, it will choose at random an Emote to Visually Perform.
+
+-------------------
+11 = ACTION_T_CAST:
+-------------------
+Parameter 1: SpellId - The Spell ID to use for the NPC to cast. The value used in this field needs to be a valid Spell ID.
+Parameter 2: Target - The Target Type defining who the creature should cast the spell at. The value in this field needs to be a valid Target Type as specified in the reference tables below.
+Parameter 3: CastFlags - See Table Below for Cast Flag Bitmask Values. If you are unsure what to set this value at leave it at 0.
+
+The creature will cast a spell specified by a spell ID on a target specified by the target type.
+This is commonly used for NPC's who cast spells.
+
+---------------------
+12 = ACTION_T_SUMMON:
+---------------------
+Parameter 1: CreatureID - The Creature Template ID to be Summoned. The value here needs to be a valid Creature Template ID.
+Parameter 2: Target - The Target Type defining who the Summoned creature will attack once spawned. The value in this field needs to be a valid Target Type as specified in the reference tables below.
+Parameter 3: Duration - The duration until the summoned creature should be unsummoned AFTER Combat ends. The value in this field is in milliseconds or 0.
+
+The NPC will Summon another creature at the same spot as itself that will attack the specified target.
+NOTE: Almost all Creature Summons have proper Summon Spells that should be used when possible. This Action is a powerful last resort option only to be used if nothing else works.
+NOTE: Using Target Type 0 will cause the Summoned creature to not attack anyone.
+NOTE: If Duration is set at 0, then the summoned creature will not despawn until it has died.
+This is used as a manual way to force an NPC to Summon.
+--------------------------------
+13 = ACTION_T_THREAT_SINGLE_PCT:
+--------------------------------
+Parameter 1: Threat% - Threat percent that should be modified. The value in this field can range from -100 to +100. If it is negative, threat will be taken away and if positive, threat will be added.
+Parameter 2: Target - The Target Type defining on whom the threat change should occur. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+This action will modify the threat of a target in the creature's threat list by the specified percent.
+This is commonly used to allow an NPC to adjust the Threat to a single player.
+
+-----------------------------
+14 = ACTION_T_THREAT_ALL_PCT:
+-----------------------------
+Parameter 1: Threat% - The percent that should be used in modifying everyone's threat in the creature's threat list. The value here can range from -100 to +100.
+
+This action will modify the threat for everyone in the creature's threat list by the specified percent.
+NOTE: Using -100 will cause the creature to reset everyone's threat to 0 so that everyone has the same amount of threat. It will NOT remove anyone from the threat list.
+This is commonly used to allow an NPC to drop threat for all players to zero.
+
+--------------------------
+15 = ACTION_T_QUEST_EVENT:
+--------------------------
+Parameter 1: QuestID - The Quest Template ID. The value here must be a valid quest template ID. Furthermore, the quest should have SpecialFlags | 2 as it would need to be completed by an external event which is the activation of this action.
+Parameter 2: Target - The Target Type defining whom the quest should be completed for. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+This action will satisfy the external completion requirement for the quest for the specified target defined by the target type.
+NOTE: This action can only be used with player targets so it must be ensured that the target type will point to a player.
+This is commonly used for Quests where only ONE player will gain credit for the quest.
+
+-----------------------------
+16 = ACTION_T_CASTCREATUREGO:
+-----------------------------
+Parameter 1: CreatureID - The Creature Template ID to be Summoned. The value here needs to be a valid Creature Template ID.
+Parameter 2: SpellId - The Spell ID to use to simulate the cast. The value used in this field needs to be a valid Spell ID.
+Parameter 3: Target - The Target Type defining whom the quest credit should be given to. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+This action will call CastedCreatureOrGO() function for the player. It can be used to give quest credit for casting a spell on the creature.
+This is commonly used for NPC's who have a special requirement to have a Spell cast on them to complete a quest.
+
+-----------------------------
+17 = ACTION_T_SET_UNIT_FIELD:
+-----------------------------
+Parameter 1: Field_Number - The index of the Field Number to be changed. Use (http://wiki.udbforums.org/index.php/Character_data) for a list of indeces and what they control. Creatures only contain the OBJECT_FIELD_* and UNIT_FIELD_* fields. They do not contain the PLAYER_FIELD_* fields.
+Parameter 2: Value - The new value to be put in the field.
+Parameter 3: Target - The Target Type defining for whom the unit field should be changed. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+When activated, this action can change the target's unit field values. More information on the field value indeces can be found at (http://wiki.udbforums.org/index.php/Character_data)
+
+----------------------------
+18 = ACTION_T_SET_UNIT_FLAG:
+----------------------------
+Parameter 1: Flags - The flag(s) to be set. Multiple flags can be set by using bitwise-OR on them (adding them together).
+Parameter 2: Target - The Target Type defining for whom the flags should be changed. The value in this field needs to be a valid Target Type as specified in the reference tables below.
+
+When activated, this action changes the target's flags by adding (turning on) more flags. For example, this action can make the creature unattackable/unselectable if the right flags are used.
+
+-------------------------------
+19 = ACTION_T_REMOVE_UNIT_FLAG:
+-------------------------------
+Parameter 1: Flags - The flag(s) to be removed. Multiple flags can be set by using bitwise-OR on them (adding them together).
+Parameter 2: Target - The target type defining for whom the flags should be changed. The value in this field needs to be a valid Target Type as specified in the reference tables below.
+
+When activated, this action changes the target's flags by removing (turning off) flags. For example, this action can make the creature normal after it was unattackable/unselectable if the right flags are used.
+
+--------------------------
+20 = ACTION_T_AUTO_ATTACK:
+--------------------------
+Parameter 1: AllowAutoAttack - If zero, then the creature will stop its melee attacks. If non-zero, then the creature will either continue its melee attacks (the action would then have no effect) or it will start its melee attacks on the target with the top threat if its melee attacks were previously stopped.
+
+This action controls whether or not the creature should stop or start the auto melee attack.
+NOTE: The ACID Dev Team has conformed to using either 0 or 1 for the Param values (0 = Stop Melee, 1 = Start Melee).
+This is commonly used in combination with EVENT_T_RANGE and ACTION_T_COMBAT_MOVEMENT for Ranged Combat for Mages and Spell Casters.
+
+------------------------------
+21 = ACTION_T_COMBAT_MOVEMENT:
+------------------------------
+Parameter 1: If zero, then the creature will stop moving towards its victim (if its victim gets out of melee range) and will be stationary. If non-zero, then the creature will either continue to follow its victim (the action would have no effect) or it will start to follow the target with the top threat if its movement was disabled before.
+
+This action controls whether or not the creature will always move towards its target.
+NOTE: The ACID Dev Team has conformed to using either 0 or 1 for the Param values. (0 = Stop Movement, 1 = Start Movement)
+This is commonly used with EVENT_T_RANGE and ACTION_T_AUTO_ATTACK for NPC's who engage in Ranged Comabt (Either Spells or Ranged Attacks)
+
+------------------------
+22 = ACTION_T_SET_PHASE:
+------------------------
+Parameter 1: The new phase to set the creature in. This number must be an integer between 0 and 31. Numbers outside of that range will result in an error.
+
+When activated, this action sets the creature's event to the specified value.
+NOTE: The creature's current Phase is NOT reset at creature evade. You must manually set the phase back to 0 at EVENT_T_RESET.
+NOTE: The value used for the Param is the actual Phase Number (Not The Event_Inverse_Phase_Mask)
+This is commonly used for complex scripts with several phases and you need to switch to a different phase.
+
+------------------------
+23 = ACTION_T_INC_PHASE:
+------------------------
+Parameter 1: Value - The number of phases to increase or decrease. Use negative values to decrease the current phase.
+
+When activated, this action will increase (or decrease) the current creature's phase.
+NOTE: After increasing or decreasing the phase by this action, the current phase must NOT be lower than 0 or exceed 31.
+This can be used instead of ACTION_T_SET_PHASE to change phases in scripts. Just a user friendly option for changing phases.
+
+--------------------
+24 = ACTION_T_EVADE:
+--------------------
+When activated, the creature will immediately exit out of combat, clear its threat list, and move back to its spawn point. Basically, this action will reset the whole encounter.
+NOTE: All Param Values Are 0 for this Action.
+
+-------------------
+25 = ACTION_T_FLEE:
+-------------------
+When activated, the creature will try to flee from combat. Currently this is done by it casting a fear-like spell on itself called "Run Away". A Better Flee system is in Development but will take time before it is implimented.
+NOTE: All Param Values Are 0 for this Action.
+
+------------------------------
+26 = ACTION_T_QUEST_EVENT_ALL:
+------------------------------
+Parameter 1: QuestId - The quest ID to finish for everyone.
+
+This action does the same thing as the ACTION_T_QUEST_EVENT does but it does it for all players in the creature's threat list.
+NOTE: If a player is not in the NPC's threat list for whatever reason, he/she won't get the quest completed.
+
+---------------------------------
+27 = ACTION_T_CASTCREATUREGO_ALL:
+---------------------------------
+Parameter 1: QuestId - The quest template ID.
+Parameter 2: SpellId - The spell ID used to simulate the cast.
+
+This action does the same thing as the ACTION_T_CASTCREATUREGO does but it does it for all players in the creature's threat list.
+NOTE: If a player is not in its threat list for whatever reason, he/she won't receive the cast emulation.
+
+-----------------------------------
+28 = ACTION_T_REMOVEAURASFROMSPELL:
+-----------------------------------
+Parameter 1: Target - The target type defining for whom the unit field should be changed. The value in this field needs to be a valid target type as specified in the reference tables below.
+Parameter 2: SpellId - The spell ID whose auras will be removed.
+
+This action will remove all auras from a specific spell from the target.
+This is commonly used for NPC's who have an OOC Aura that is removed at combat start or a similar idea (Like Stealth or Shape Shift)
+
+------------------------------
+29 = ACTION_T_RANGED_MOVEMENT:
+------------------------------
+Parameter 1: Distance - The distance the mob should keep between it and its target.
+Parameter 2: Angle - The angle the mob should use.
+
+This action changes the movement type generator to ranged type using the specified values for angle and distance.
+NOTE: Specifying zero angle and distance will make it just melee instead.
+This is commonly used for NPC's who always attack at range and you can specify the distance they will maintain from the target.
+
+---------------------------
+30 = ACTION_T_RANDOM_PHASE:
+---------------------------
+Parameter 1: PhaseId1 - A possible random phase choice.
+Parameter 2: PhaseId2 - A possible random phase choice.
+Parameter 3: PhaseId3 - A possible random phase choice.
+
+Randomly sets the phase to one from the three parameter choices.
+NOTE: Use -1 to specify that if this param is picked to do nothing. Random is constant between actions within an event. So if you have a random Yell and a random Sound they will match up (ex: param2 with param2)
+NOTE 2: PLEASE NOTE THAT EACH OF THE PARAM VALUES ARE ACTUAL PHASE NUMBERS NOT THE INVERSE PHASE MASK VALUE.
+This is commonly used for Spellcasting NPC's who on Aggro may select at random a school of spells to use for the fight. Use this if you have up to 3 phases used, otherwise use Action 31 for more then 3 phases.
+
+---------------------------------
+31 = ACTION_T_RANDOM_PHASE_RANGE:
+---------------------------------
+Parameter 1: PhaseMin - The minimum of the phase range.
+Parameter 2: PhaseMax - The maximum of the phase range. The number here must be greater than PhaseMin.
+
+Randomly sets the phase between a range of phases controlled by the parameters. Sets the phase to a random id (Phase = PhaseMin + rnd % PhaseMin-PhaseMax).
+NOTE: PhaseMax must be greater than PhaseMin.
+NOTE 2: PLEASE NOTE THAT EACH OF THE PARAM VALUES ARE ACTUAL PHASE NUMBERS NOT THE INVERSE PHASE MASK VALUE.
+This is commonly used for Spellcasting NPC's who on Aggro may select at random a school of spells to use for the fight. Use this if you have MORE then 3 phases used, otherwise use Action 30.
+
+---------------------
+32 = ACTION_T_SUMMON:
+---------------------
+Parameter 1: CreatureID - The creature template ID to be summoned. The value here needs to be a valid creature template ID.
+Parameter 2: Target - The target type defining who the summoned creature will attack. The value in this field needs to be a valid target type as specified in the reference tables below. NOTE: Using target type 0 will cause the summoned creature to not attack anyone.
+Parameter 3: SummonID - The summon ID from the eventai_summons table controlling the position (and spawntime) where the summoned mob should be spawned at.
+
+Summons creature (param1) to attack target (param2) at location specified by EventAI_Summons (param3).
+NOTE: Param3 Value is the ID Value used for the entry used in EventAI_Summons for this action. You MUST have an EventAI_Summons entry to use this action.
+This is commonly used for NPC's who need to Summon a creature at a specific location. (Normally used for complex events)
+
+-----------------------------
+33 = ACTION_T_KILLED_MONSTER:
+-----------------------------
+Parameter 1: CreatureID - The creature template ID. The value here must be a valid creature template ID.
+Parameter 2: Target - The target type defining whom the quest kill count should be given to. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+When activated, this action will call KilledMonster() function for the player. It can be used to give creature credit for killing a creature. In general if the quest is set to be accompished on different creatures (e.g. "Credit" templates).
+NOTE: It can be ANY creature including certain quest specific triggers
+This is commonly used for giving the player Quest Credits for NPC kills (Many NPC's may use the same CreatureID for the Kill Credit)
+
+----------------------------
+34 = ACTION_T_SET_INST_DATA:
+----------------------------
+Parameter 1: Field - The field to change in the instance script. Again, this field needs to be a valid field that has been already defined in the instance's script.
+Parameter 2: Data - The value to put at that field index.
+
+Sets data for the instance. Note that this will only work when the creature is inside an instantiable zone that has a valid script (ScriptedInstance) assigned.
+NOTE: Param1 Value is located in "def_<instance name>.h" SD2 File and Param2 value is generally found in the "sc_instance.h" file in SD2
+This is commonly used to link an ACID script with a SD2 C++ Script. You make make things happen like opening doors on specific events that happen. ACID Just triggers the C++ Script to function.
+
+------------------------------
+35 = ACTION_T_SET_INST_DATA64:
+------------------------------
+Parameter 1: Field - The field to change in the instance script. Again, this field needs to be a valid field that has been already defined in the instance's script.
+Parameter 2: Target - The target type to use to get the GUID that will be stored at the field index. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+Sets GUID (64 bits) data for the instance based on the target. Note that this will only work when the creature is inside an instantiable zone that has a valid script (ScriptedInstance) assigned.
+Calls ScriptedInstance::SetData64 with field (param1) and data (param2) target's GUID.
+
+------------------------------
+36 = ACTION_T_UPDATE_TEMPLATE:
+------------------------------
+Parameter 1: TemplateId - The creature template ID. The value here must be a valid creature template ID.
+Parameter 2: Team - Use model_id from team : Alliance(0) or Horde (1).
+
+This function temporarily changes creature entry to new entry, display is changed, loot is changed, but AI is not changed. At respawn creature will be reverted to original entry.
+Changes the creature to a new creature template of (param1) with team = Alliance if (param2) = false or Horde if (param2) = true
+
+------------------
+37 = ACTION_T_DIE:
+------------------
+Kills the creature
+This is commonly used if you need to Instakill the creature for one reason or another.
+
+--------------------------------
+38 = ACTION_T_ZONE_COMBAT_PULSE:
+--------------------------------
+Places all players within the instance into combat with the creature. Only works in combat and only works inside of instances.
+
+
+=========================================
+Target Types
+=========================================
+Below is the list of current Target types that EventAI can handle.
+Target types are used by certain actions and may effect actions differently
+
+(# Internal Name Discription)
+0 TARGET_T_SELF Self cast
+1 TARGET_T_HOSTILE Our current target (ie: highest aggro)
+2 TARGET_T_HOSTILE_SECOND_AGGRO Second highest aggro (generaly used for cleaves and some special attacks)
+3 TARGET_T_HOSTILE_LAST_AGGRO Dead last on aggro (no idea what this could be used for)
+4 TARGET_T_HOSTILE_RANDOM Just any random target on our threat list
+5 TARGET_T_HOSTILE_RANDOM_NOT_TOP Any random target except top threat
+6 TARGET_T_ACTION_INVOKER Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP)
+
+=========================================
+Cast Flags
+=========================================
+Below is the list of current Cast Flags that EventAI's spell casting can handle.
+Cast flags are handled bitwise. Bit 0 is Interrupt Previous, bit 1 is triggered, etc.
+So for example the number "3" (11 in Binary, selecting first 2 options) would mean that this cast has both CAST_INTURRUPT_PREVIOUS and CAST_TRIGGERED.
+Another example: the number "5" (101 in Binary, selecting first and third options) would mean that this cast has CAST_INTURRUPT_PREVIOUS and CAST_FORCE_CAST.
+
+(bit# Decimal Internal Name Discription)
+0 1 CAST_INTURRUPT_PREVIOUS Interrupts any previous spell casting (basicaly makes sure that this spell goes off)
+1 2 CAST_TRIGGERED Forces the spell to be instant cast and require no mana/reagents.
+2 4 CAST_FORCE_CAST Forces spell to cast even if the target is possibly out of range or the creature is possibly out of mana
+3 8 CAST_NO_MELEE_IF_OOM Prevents creature from entering melee if out of mana or out of range
+4 16 CAST_FORCE_TARGET_SELF Forces the target to cast this spell on itself
+5 32 CAST_AURA_NOT_PRESENT Only casts the spell on the target if the target does not have the aura from that spell on itself already.
+
+NOTE: You can add the numbers in the decimal column to combine flags.
+ For example if you wanted to use CAST_NO_MELEE_IF_OOM(8) and CAST_TRIGGERED(2) you would simply use 10 in the cast flags field (8 + 2 = 10).
+
+=========================================
+Event Flags
+=========================================
+Below is the list of current Event Flags that EventAI can handle. Event flags are handled bitwise.
+
+(bit# Decimal Internal Name Discription)
+0 1 EFLAG_REPEATABLE Event repeats (Does not repeat if this flag is not set)
+1 2 EFLAG_NORMAL Event occurs in Normal instance difficulty (will not occur in Normal if not set)
+2 4 EFLAG_HEROIC Event occurs in Heroic instance difficulty (will not occur in Heroic if not set)
+3 8
+4 16
+5 32
+6 64
+7 128 EFLAG_DEBUG_ONLY Prevents events from occuring on Release builds of ScriptDev2. Useful for testing new features.
+
NOTE: You can add the numbers in the decimal column to combine flags. \ No newline at end of file
diff --git a/src/bindings/scripts/include/sc_creature.cpp b/src/bindings/scripts/include/sc_creature.cpp
index 8e18b0fc29e..80271821ade 100644
--- a/src/bindings/scripts/include/sc_creature.cpp
+++ b/src/bindings/scripts/include/sc_creature.cpp
@@ -1,608 +1,608 @@
-/* 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 "Item.h"
-#include "Spell.h"
-
-// Spell summary for ScriptedAI::SelectSpell
-struct TSpellSummary {
- uint8 Targets; // set of enum SelectTarget
- uint8 Effects; // set of enum SelectEffect
-} *SpellSummary;
-
-bool ScriptedAI::IsVisible(Unit* who) const
-{
- if (!who)
- return false;
-
- return (m_creature->GetDistance(who) < VISIBLE_RANGE) && who->isVisibleForOrDetect(m_creature,true);
-}
-
-void ScriptedAI::MoveInLineOfSight(Unit *who)
-{
- if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) )
- {
- if (!m_creature->canFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
- return;
-
- float attackRadius = m_creature->GetAttackDistance(who);
- if( m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who) )
- {
- DoStartAttackAndMovement(who);
- who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
-
- if (!InCombat)
- {
- InCombat = true;
- Aggro(who);
- }
- }
- }
-}
-
-void ScriptedAI::AttackStart(Unit* who)
-{
- if (!who)
- return;
-
- if (who->isTargetableForAttack())
- {
- //Begin attack
- DoStartAttackAndMovement(who);
-
- if (!InCombat)
- {
- InCombat = true;
- Aggro(who);
- }
- }
-}
-
-void ScriptedAI::UpdateAI(const uint32 diff)
-{
- //Check if we have a current target
- if( m_creature->isAlive() && m_creature->SelectHostilTarget() && m_creature->getVictim())
- {
- if( m_creature->isAttackReady() )
- {
- //If we are within range melee the target
- if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE))
- {
- m_creature->AttackerStateUpdate(m_creature->getVictim());
- m_creature->resetAttackTimer();
- }
- }
- }
-}
-
-void ScriptedAI::EnterEvadeMode()
-{
- m_creature->InterruptNonMeleeSpells(true);
- m_creature->RemoveAllAuras();
- m_creature->DeleteThreatList();
- m_creature->CombatStop();
- m_creature->LoadCreaturesAddon();
-
- if( m_creature->isAlive() )
- m_creature->GetMotionMaster()->MoveTargetedHome();
-
- m_creature->SetLootRecipient(NULL);
-
- InCombat = false;
- Reset();
-}
-
-void ScriptedAI::JustRespawned()
-{
- InCombat = false;
- Reset();
-}
-
-void ScriptedAI::DoStartAttackAndMovement(Unit* victim, float distance, float angle)
-{
- if (!victim)
- return;
-
- if ( m_creature->Attack(victim, true) )
- {
- m_creature->GetMotionMaster()->MoveChase(victim, distance, angle);
- m_creature->AddThreat(victim, 0.0f);
- }
-}
-
-void ScriptedAI::DoStartAttackNoMovement(Unit* victim)
-{
- if (!victim)
- return;
-
- if ( m_creature->Attack(victim, true) )
- {
- m_creature->AddThreat(victim, 0.0f);
- }
-}
-
-
-void ScriptedAI::DoMeleeAttackIfReady()
-{
- //Make sure our attack is ready and we aren't currently casting before checking distance
- if( m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false))
- {
- //If we are within range melee the target
- if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE))
- {
- m_creature->AttackerStateUpdate(m_creature->getVictim());
- m_creature->resetAttackTimer();
- }
- }
-}
-
-void ScriptedAI::DoStopAttack()
-{
- if( m_creature->getVictim() != NULL )
- {
- m_creature->AttackStop();
- }
-}
-
-void ScriptedAI::DoCast(Unit* victim, uint32 spellId, bool triggered)
-{
- if (!victim || m_creature->IsNonMeleeSpellCasted(false))
- return;
-
- m_creature->StopMoving();
- m_creature->CastSpell(victim, spellId, triggered);
-}
-
-void ScriptedAI::DoCastSpell(Unit* who,SpellEntry const *spellInfo, bool triggered)
-{
- if (!who || m_creature->IsNonMeleeSpellCasted(false))
- return;
-
- m_creature->StopMoving();
- m_creature->CastSpell(who, spellInfo, triggered);
-}
-
-void ScriptedAI::DoSay(const char* text, uint32 language, Unit* target)
-{
- if (target)m_creature->Say(text, language, target->GetGUID());
- else m_creature->Say(text, language, 0);
-}
-
-void ScriptedAI::DoYell(const char* text, uint32 language, Unit* target)
-{
- if (target)m_creature->Yell(text, language, target->GetGUID());
- else m_creature->Yell(text, language, 0);
-}
-
-void ScriptedAI::DoTextEmote(const char* text, Unit* target, bool IsBossEmote)
-{
- if (target)m_creature->TextEmote(text, target->GetGUID(), IsBossEmote);
- else m_creature->TextEmote(text, 0, IsBossEmote);
-}
-
-void ScriptedAI::DoWhisper(const char* text, Unit* reciever, bool IsBossWhisper)
-{
- if (!reciever || reciever->GetTypeId() != TYPEID_PLAYER)
- return;
-
- m_creature->Whisper(text, reciever->GetGUID(), IsBossWhisper);
-}
-
-void ScriptedAI::DoPlaySoundToSet(Unit* unit, uint32 sound)
-{
- if (!unit)
- return;
-
- if (!GetSoundEntriesStore()->LookupEntry(sound))
- {
- error_log("SD2: Invalid soundId %u used in DoPlaySoundToSet (by unit TypeId %u, guid %u)", sound, unit->GetTypeId(), unit->GetGUID());
- return;
- }
-
- unit->SendPlaySound(sound, false);
-}
-
-Creature* ScriptedAI::DoSpawnCreature(uint32 id, float x, float y, float z, float angle, uint32 type, uint32 despawntime)
-{
- return m_creature->SummonCreature(id,m_creature->GetPositionX() + x,m_creature->GetPositionY() + y,m_creature->GetPositionZ() + z, angle, (TempSummonType)type, despawntime);
-}
-
-Unit* ScriptedAI::SelectUnit(SelectAggroTarget target, uint32 position)
-{
- //ThreatList m_threatlist;
- std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
- std::list<HostilReference*>::iterator i = m_threatlist.begin();
- std::list<HostilReference*>::reverse_iterator r = m_threatlist.rbegin();
-
- if (position >= m_threatlist.size() || !m_threatlist.size())
- return NULL;
-
- switch (target)
- {
- case SELECT_TARGET_RANDOM:
- advance ( i , position + (rand() % (m_threatlist.size() - position ) ));
- return Unit::GetUnit((*m_creature),(*i)->getUnitGuid());
- break;
-
- case SELECT_TARGET_TOPAGGRO:
- advance ( i , position);
- return Unit::GetUnit((*m_creature),(*i)->getUnitGuid());
- break;
-
- case SELECT_TARGET_BOTTOMAGGRO:
- advance ( r , position);
- return Unit::GetUnit((*m_creature),(*r)->getUnitGuid());
- break;
- }
-
- return NULL;
-}
-
-SpellEntry const* ScriptedAI::SelectSpell(Unit* Target, int32 School, int32 Mechanic, SelectTarget Targets, uint32 PowerCostMin, uint32 PowerCostMax, float RangeMin, float RangeMax, SelectEffect Effects)
-{
- //No target so we can't cast
- if (!Target)
- return false;
-
- //Silenced so we can't cast
- if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
- return false;
-
- //Using the extended script system we first create a list of viable spells
- SpellEntry const* Spell[4];
- Spell[0] = 0;
- Spell[1] = 0;
- Spell[2] = 0;
- Spell[3] = 0;
-
- uint32 SpellCount = 0;
-
- SpellEntry const* TempSpell;
- SpellRangeEntry const* TempRange;
-
- //Check if each spell is viable(set it to null if not)
- for (uint32 i = 0; i < 4; i++)
- {
- TempSpell = GetSpellStore()->LookupEntry(m_creature->m_spells[i]);
-
- //This spell doesn't exist
- if (!TempSpell)
- continue;
-
- // Targets and Effects checked first as most used restrictions
- //Check the spell targets if specified
- if ( Targets && !(SpellSummary[m_creature->m_spells[i]].Targets & (1 << (Targets-1))) )
- continue;
-
- //Check the type of spell if we are looking for a specific spell type
- if ( Effects && !(SpellSummary[m_creature->m_spells[i]].Effects & (1 << (Effects-1))) )
- continue;
-
- //Check for school if specified
- if (School >= 0 && TempSpell->SchoolMask & School)
- continue;
-
- //Check for spell mechanic if specified
- if (Mechanic >= 0 && TempSpell->Mechanic != Mechanic)
- continue;
-
- //Make sure that the spell uses the requested amount of power
- if (PowerCostMin && TempSpell->manaCost < PowerCostMin)
- continue;
-
- if (PowerCostMax && TempSpell->manaCost > PowerCostMax)
- continue;
-
- //Continue if we don't have the mana to actually cast this spell
- if (TempSpell->manaCost > m_creature->GetPower((Powers)TempSpell->powerType))
- continue;
-
- //Get the Range
- TempRange = GetSpellRangeStore()->LookupEntry(TempSpell->rangeIndex);
-
- //Spell has invalid range store so we can't use it
- if (!TempRange)
- continue;
-
- //Check if the spell meets our range requirements
- if (RangeMin && TempRange->maxRange < RangeMin)
- continue;
- if (RangeMax && TempRange->maxRange > RangeMax)
- continue;
-
- //Check if our target is in range
- if (m_creature->IsWithinDistInMap(Target, TempRange->minRange) || !m_creature->IsWithinDistInMap(Target, TempRange->maxRange))
- continue;
-
- //All good so lets add it to the spell list
- Spell[SpellCount] = TempSpell;
- SpellCount++;
- }
-
- //We got our usable spells so now lets randomly pick one
- if (!SpellCount)
- return NULL;
-
- return Spell[rand()%SpellCount];
-}
-
-bool ScriptedAI::CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered)
-{
- //No target so we can't cast
- if (!Target || !Spell)
- return false;
-
- //Silenced so we can't cast
- if (!Triggered && m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
- return false;
-
- //Check for power
- if (!Triggered && m_creature->GetPower((Powers)Spell->powerType) < Spell->manaCost)
- return false;
-
- SpellRangeEntry const *TempRange = NULL;
-
- TempRange = GetSpellRangeStore()->LookupEntry(Spell->rangeIndex);
-
- //Spell has invalid range store so we can't use it
- if (!TempRange)
- return false;
-
- //Unit is out of range of this spell
- if (m_creature->GetDistance(Target) > TempRange->maxRange || m_creature->GetDistance(Target) < TempRange->minRange)
- return false;
-
- return true;
-}
-
-void FillSpellSummary()
-{
- SpellSummary = new TSpellSummary[GetSpellStore()->GetNumRows()];
-
- SpellEntry const* TempSpell;
-
- for (int i=0; i < GetSpellStore()->GetNumRows(); i++ )
- {
- SpellSummary[i].Effects = 0;
- SpellSummary[i].Targets = 0;
-
- TempSpell = GetSpellStore()->LookupEntry(i);
- //This spell doesn't exist
- if (!TempSpell)
- continue;
-
- for (int j=0; j<3; j++)
- {
- //Spell targets self
- if ( TempSpell->EffectImplicitTargetA[j] == TARGET_SELF )
- SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SELF-1);
-
- //Spell targets a single enemy
- if ( TempSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_ENEMY_COORDINATES )
- SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SINGLE_ENEMY-1);
-
- //Spell targets AoE at enemy
- if ( TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED )
- SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_ENEMY-1);
-
- //Spell targets an enemy
- if ( TempSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_ENEMY_COORDINATES ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED )
- SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_ENEMY-1);
-
- //Spell targets a single friend(or self)
- if ( TempSpell->EffectImplicitTargetA[j] == TARGET_SELF ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY )
- SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SINGLE_FRIEND-1);
-
- //Spell targets aoe friends
- if ( TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER)
- SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_FRIEND-1);
-
- //Spell targets any friend(or self)
- if ( TempSpell->EffectImplicitTargetA[j] == TARGET_SELF ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY ||
- TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER)
- SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_FRIEND-1);
-
- //Make sure that this spell includes a damage effect
- if ( TempSpell->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE ||
- TempSpell->Effect[j] == SPELL_EFFECT_INSTAKILL ||
- TempSpell->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE ||
- TempSpell->Effect[j] == SPELL_EFFECT_HEALTH_LEECH )
- SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_DAMAGE-1);
-
- //Make sure that this spell includes a healing effect (or an apply aura with a periodic heal)
- if ( TempSpell->Effect[j] == SPELL_EFFECT_HEAL ||
- TempSpell->Effect[j] == SPELL_EFFECT_HEAL_MAX_HEALTH ||
- TempSpell->Effect[j] == SPELL_EFFECT_HEAL_MECHANICAL ||
- (TempSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA && TempSpell->EffectApplyAuraName[j]== 8 ))
- SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_HEALING-1);
-
- //Make sure that this spell applies an aura
- if ( TempSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA )
- SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_AURA-1);
- }
- }
-}
-
-void ScriptedAI::DoZoneInCombat(Unit* pUnit)
-{
- if (!pUnit)
- pUnit = m_creature;
-
- Map *map = pUnit->GetMap();
-
- if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated
- {
- error_log("SD2: DoZoneInCombat call for map that isn't an instance (pUnit entry = %d)", pUnit->GetTypeId() == TYPEID_UNIT ? ((Creature*)pUnit)->GetEntry() : 0);
- return;
- }
-
- if (!pUnit->CanHaveThreatList() || pUnit->getThreatManager().isThreatListEmpty())
- {
- error_log("SD2: DoZoneInCombat called for creature that either cannot have threat list or has empty threat list (pUnit entry = %d)", pUnit->GetTypeId() == TYPEID_UNIT ? ((Creature*)pUnit)->GetEntry() : 0);
-
- return;
- }
-
- InstanceMap::PlayerList const &PlayerList = ((InstanceMap*)map)->GetPlayers();
- InstanceMap::PlayerList::const_iterator i;
- for (i = PlayerList.begin(); i != PlayerList.end(); ++i)
- {
- if(!(*i)->isGameMaster())
- pUnit->AddThreat(*i, 0.0f);
- }
-}
-
-void ScriptedAI::DoResetThreat()
-{
- if (!m_creature->CanHaveThreatList() || m_creature->getThreatManager().isThreatListEmpty())
- {
- error_log("SD2: DoResetThreat called for creature that either cannot have threat list or has empty threat list (m_creature entry = %d)", m_creature->GetEntry());
-
- return;
- }
-
- std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
- std::list<HostilReference*>::iterator itr;
-
- for(itr = m_threatlist.begin(); itr != m_threatlist.end(); ++itr)
- {
- Unit* pUnit = NULL;
- pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid());
- if(pUnit && m_creature->getThreatManager().getThreat(pUnit))
- m_creature->getThreatManager().modifyThreatPercent(pUnit, -100);
- }
-}
-
-void ScriptedAI::DoTeleportPlayer(Unit* pUnit, float x, float y, float z, float o)
-{
- if(!pUnit || pUnit->GetTypeId() != TYPEID_PLAYER)
- {
- if(pUnit)
- error_log("SD2: Creature %u (Entry: %u) Tried to teleport non-player unit (Type: %u GUID: %u) to x: %f y:%f z: %f o: %f. Aborted.", m_creature->GetGUID(), m_creature->GetEntry(), pUnit->GetTypeId(), pUnit->GetGUID(), x, y, z, o);
- return;
- }
-
- ((Player*)pUnit)->TeleportTo(pUnit->GetMapId(), x, y, z, o, TELE_TO_NOT_LEAVE_COMBAT);
-}
-
-Unit* ScriptedAI::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff)
-{
- CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- Unit* pUnit = NULL;
-
- MaNGOS::MostHPMissingInRange u_check(m_creature, range, MinHPDiff);
- MaNGOS::UnitLastSearcher<MaNGOS::MostHPMissingInRange> searcher(pUnit, u_check);
-
- /*
- typedef TYPELIST_4(GameObject, Creature*except pets*, DynamicObject, Corpse*Bones*) AllGridObjectTypes;
- This means that if we only search grid then we cannot possibly return pets or players so this is safe
- */
- TypeContainerVisitor<MaNGOS::UnitLastSearcher<MaNGOS::MostHPMissingInRange>, GridTypeMapContainer > grid_unit_searcher(searcher);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, grid_unit_searcher, *(m_creature->GetMap()));
- return pUnit;
-}
-
-std::list<Creature*> ScriptedAI::DoFindFriendlyCC(float range)
-{
- CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- std::list<Creature*> pList;
-
- MaNGOS::FriendlyCCedInRange u_check(m_creature, range);
- MaNGOS::CreatureListSearcher<MaNGOS::FriendlyCCedInRange> searcher(pList, u_check);
-
- TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::FriendlyCCedInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, grid_creature_searcher, *(m_creature->GetMap()));
-
- return pList;
-}
-
-std::list<Creature*> ScriptedAI::DoFindFriendlyMissingBuff(float range, uint32 spellid)
-{
- CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- std::list<Creature*> pList;
-
- MaNGOS::FriendlyMissingBuffInRange u_check(m_creature, range, spellid);
- MaNGOS::CreatureListSearcher<MaNGOS::FriendlyMissingBuffInRange> searcher(pList, u_check);
-
- TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::FriendlyMissingBuffInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, grid_creature_searcher, *(m_creature->GetMap()));
-
- return pList;
-}
-
-void Scripted_NoMovementAI::MoveInLineOfSight(Unit *who)
-{
- if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) )
- {
- if (!m_creature->canFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
- return;
-
- float attackRadius = m_creature->GetAttackDistance(who);
- if( m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who) )
- {
- DoStartAttackNoMovement(who);
- who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
-
- if (!InCombat)
- {
- InCombat = true;
- Aggro(who);
- }
- }
- }
-}
-
-void Scripted_NoMovementAI::AttackStart(Unit* who)
-{
- if (!who)
- return;
-
- if (who->isTargetableForAttack())
- {
- //Begin attack
- DoStartAttackNoMovement(who);
-
- if (!InCombat)
- {
- InCombat = true;
- Aggro(who);
- }
- }
-}
+/* 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 "Item.h"
+#include "Spell.h"
+
+// Spell summary for ScriptedAI::SelectSpell
+struct TSpellSummary {
+ uint8 Targets; // set of enum SelectTarget
+ uint8 Effects; // set of enum SelectEffect
+} *SpellSummary;
+
+bool ScriptedAI::IsVisible(Unit* who) const
+{
+ if (!who)
+ return false;
+
+ return (m_creature->GetDistance(who) < VISIBLE_RANGE) && who->isVisibleForOrDetect(m_creature,true);
+}
+
+void ScriptedAI::MoveInLineOfSight(Unit *who)
+{
+ if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) )
+ {
+ if (!m_creature->canFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
+ return;
+
+ float attackRadius = m_creature->GetAttackDistance(who);
+ if( m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who) )
+ {
+ DoStartAttackAndMovement(who);
+ who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+
+ if (!InCombat)
+ {
+ InCombat = true;
+ Aggro(who);
+ }
+ }
+ }
+}
+
+void ScriptedAI::AttackStart(Unit* who)
+{
+ if (!who)
+ return;
+
+ if (who->isTargetableForAttack())
+ {
+ //Begin attack
+ DoStartAttackAndMovement(who);
+
+ if (!InCombat)
+ {
+ InCombat = true;
+ Aggro(who);
+ }
+ }
+}
+
+void ScriptedAI::UpdateAI(const uint32 diff)
+{
+ //Check if we have a current target
+ if( m_creature->isAlive() && m_creature->SelectHostilTarget() && m_creature->getVictim())
+ {
+ if( m_creature->isAttackReady() )
+ {
+ //If we are within range melee the target
+ if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE))
+ {
+ m_creature->AttackerStateUpdate(m_creature->getVictim());
+ m_creature->resetAttackTimer();
+ }
+ }
+ }
+}
+
+void ScriptedAI::EnterEvadeMode()
+{
+ m_creature->InterruptNonMeleeSpells(true);
+ m_creature->RemoveAllAuras();
+ m_creature->DeleteThreatList();
+ m_creature->CombatStop();
+ m_creature->LoadCreaturesAddon();
+
+ if( m_creature->isAlive() )
+ m_creature->GetMotionMaster()->MoveTargetedHome();
+
+ m_creature->SetLootRecipient(NULL);
+
+ InCombat = false;
+ Reset();
+}
+
+void ScriptedAI::JustRespawned()
+{
+ InCombat = false;
+ Reset();
+}
+
+void ScriptedAI::DoStartAttackAndMovement(Unit* victim, float distance, float angle)
+{
+ if (!victim)
+ return;
+
+ if ( m_creature->Attack(victim, true) )
+ {
+ m_creature->GetMotionMaster()->MoveChase(victim, distance, angle);
+ m_creature->AddThreat(victim, 0.0f);
+ }
+}
+
+void ScriptedAI::DoStartAttackNoMovement(Unit* victim)
+{
+ if (!victim)
+ return;
+
+ if ( m_creature->Attack(victim, true) )
+ {
+ m_creature->AddThreat(victim, 0.0f);
+ }
+}
+
+
+void ScriptedAI::DoMeleeAttackIfReady()
+{
+ //Make sure our attack is ready and we aren't currently casting before checking distance
+ if( m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false))
+ {
+ //If we are within range melee the target
+ if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE))
+ {
+ m_creature->AttackerStateUpdate(m_creature->getVictim());
+ m_creature->resetAttackTimer();
+ }
+ }
+}
+
+void ScriptedAI::DoStopAttack()
+{
+ if( m_creature->getVictim() != NULL )
+ {
+ m_creature->AttackStop();
+ }
+}
+
+void ScriptedAI::DoCast(Unit* victim, uint32 spellId, bool triggered)
+{
+ if (!victim || m_creature->IsNonMeleeSpellCasted(false))
+ return;
+
+ m_creature->StopMoving();
+ m_creature->CastSpell(victim, spellId, triggered);
+}
+
+void ScriptedAI::DoCastSpell(Unit* who,SpellEntry const *spellInfo, bool triggered)
+{
+ if (!who || m_creature->IsNonMeleeSpellCasted(false))
+ return;
+
+ m_creature->StopMoving();
+ m_creature->CastSpell(who, spellInfo, triggered);
+}
+
+void ScriptedAI::DoSay(const char* text, uint32 language, Unit* target)
+{
+ if (target)m_creature->Say(text, language, target->GetGUID());
+ else m_creature->Say(text, language, 0);
+}
+
+void ScriptedAI::DoYell(const char* text, uint32 language, Unit* target)
+{
+ if (target)m_creature->Yell(text, language, target->GetGUID());
+ else m_creature->Yell(text, language, 0);
+}
+
+void ScriptedAI::DoTextEmote(const char* text, Unit* target, bool IsBossEmote)
+{
+ if (target)m_creature->TextEmote(text, target->GetGUID(), IsBossEmote);
+ else m_creature->TextEmote(text, 0, IsBossEmote);
+}
+
+void ScriptedAI::DoWhisper(const char* text, Unit* reciever, bool IsBossWhisper)
+{
+ if (!reciever || reciever->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ m_creature->Whisper(text, reciever->GetGUID(), IsBossWhisper);
+}
+
+void ScriptedAI::DoPlaySoundToSet(Unit* unit, uint32 sound)
+{
+ if (!unit)
+ return;
+
+ if (!GetSoundEntriesStore()->LookupEntry(sound))
+ {
+ error_log("SD2: Invalid soundId %u used in DoPlaySoundToSet (by unit TypeId %u, guid %u)", sound, unit->GetTypeId(), unit->GetGUID());
+ return;
+ }
+
+ unit->SendPlaySound(sound, false);
+}
+
+Creature* ScriptedAI::DoSpawnCreature(uint32 id, float x, float y, float z, float angle, uint32 type, uint32 despawntime)
+{
+ return m_creature->SummonCreature(id,m_creature->GetPositionX() + x,m_creature->GetPositionY() + y,m_creature->GetPositionZ() + z, angle, (TempSummonType)type, despawntime);
+}
+
+Unit* ScriptedAI::SelectUnit(SelectAggroTarget target, uint32 position)
+{
+ //ThreatList m_threatlist;
+ std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
+ std::list<HostilReference*>::iterator i = m_threatlist.begin();
+ std::list<HostilReference*>::reverse_iterator r = m_threatlist.rbegin();
+
+ if (position >= m_threatlist.size() || !m_threatlist.size())
+ return NULL;
+
+ switch (target)
+ {
+ case SELECT_TARGET_RANDOM:
+ advance ( i , position + (rand() % (m_threatlist.size() - position ) ));
+ return Unit::GetUnit((*m_creature),(*i)->getUnitGuid());
+ break;
+
+ case SELECT_TARGET_TOPAGGRO:
+ advance ( i , position);
+ return Unit::GetUnit((*m_creature),(*i)->getUnitGuid());
+ break;
+
+ case SELECT_TARGET_BOTTOMAGGRO:
+ advance ( r , position);
+ return Unit::GetUnit((*m_creature),(*r)->getUnitGuid());
+ break;
+ }
+
+ return NULL;
+}
+
+SpellEntry const* ScriptedAI::SelectSpell(Unit* Target, int32 School, int32 Mechanic, SelectTarget Targets, uint32 PowerCostMin, uint32 PowerCostMax, float RangeMin, float RangeMax, SelectEffect Effects)
+{
+ //No target so we can't cast
+ if (!Target)
+ return false;
+
+ //Silenced so we can't cast
+ if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
+ return false;
+
+ //Using the extended script system we first create a list of viable spells
+ SpellEntry const* Spell[4];
+ Spell[0] = 0;
+ Spell[1] = 0;
+ Spell[2] = 0;
+ Spell[3] = 0;
+
+ uint32 SpellCount = 0;
+
+ SpellEntry const* TempSpell;
+ SpellRangeEntry const* TempRange;
+
+ //Check if each spell is viable(set it to null if not)
+ for (uint32 i = 0; i < 4; i++)
+ {
+ TempSpell = GetSpellStore()->LookupEntry(m_creature->m_spells[i]);
+
+ //This spell doesn't exist
+ if (!TempSpell)
+ continue;
+
+ // Targets and Effects checked first as most used restrictions
+ //Check the spell targets if specified
+ if ( Targets && !(SpellSummary[m_creature->m_spells[i]].Targets & (1 << (Targets-1))) )
+ continue;
+
+ //Check the type of spell if we are looking for a specific spell type
+ if ( Effects && !(SpellSummary[m_creature->m_spells[i]].Effects & (1 << (Effects-1))) )
+ continue;
+
+ //Check for school if specified
+ if (School >= 0 && TempSpell->SchoolMask & School)
+ continue;
+
+ //Check for spell mechanic if specified
+ if (Mechanic >= 0 && TempSpell->Mechanic != Mechanic)
+ continue;
+
+ //Make sure that the spell uses the requested amount of power
+ if (PowerCostMin && TempSpell->manaCost < PowerCostMin)
+ continue;
+
+ if (PowerCostMax && TempSpell->manaCost > PowerCostMax)
+ continue;
+
+ //Continue if we don't have the mana to actually cast this spell
+ if (TempSpell->manaCost > m_creature->GetPower((Powers)TempSpell->powerType))
+ continue;
+
+ //Get the Range
+ TempRange = GetSpellRangeStore()->LookupEntry(TempSpell->rangeIndex);
+
+ //Spell has invalid range store so we can't use it
+ if (!TempRange)
+ continue;
+
+ //Check if the spell meets our range requirements
+ if (RangeMin && TempRange->maxRange < RangeMin)
+ continue;
+ if (RangeMax && TempRange->maxRange > RangeMax)
+ continue;
+
+ //Check if our target is in range
+ if (m_creature->IsWithinDistInMap(Target, TempRange->minRange) || !m_creature->IsWithinDistInMap(Target, TempRange->maxRange))
+ continue;
+
+ //All good so lets add it to the spell list
+ Spell[SpellCount] = TempSpell;
+ SpellCount++;
+ }
+
+ //We got our usable spells so now lets randomly pick one
+ if (!SpellCount)
+ return NULL;
+
+ return Spell[rand()%SpellCount];
+}
+
+bool ScriptedAI::CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered)
+{
+ //No target so we can't cast
+ if (!Target || !Spell)
+ return false;
+
+ //Silenced so we can't cast
+ if (!Triggered && m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
+ return false;
+
+ //Check for power
+ if (!Triggered && m_creature->GetPower((Powers)Spell->powerType) < Spell->manaCost)
+ return false;
+
+ SpellRangeEntry const *TempRange = NULL;
+
+ TempRange = GetSpellRangeStore()->LookupEntry(Spell->rangeIndex);
+
+ //Spell has invalid range store so we can't use it
+ if (!TempRange)
+ return false;
+
+ //Unit is out of range of this spell
+ if (m_creature->GetDistance(Target) > TempRange->maxRange || m_creature->GetDistance(Target) < TempRange->minRange)
+ return false;
+
+ return true;
+}
+
+void FillSpellSummary()
+{
+ SpellSummary = new TSpellSummary[GetSpellStore()->GetNumRows()];
+
+ SpellEntry const* TempSpell;
+
+ for (int i=0; i < GetSpellStore()->GetNumRows(); i++ )
+ {
+ SpellSummary[i].Effects = 0;
+ SpellSummary[i].Targets = 0;
+
+ TempSpell = GetSpellStore()->LookupEntry(i);
+ //This spell doesn't exist
+ if (!TempSpell)
+ continue;
+
+ for (int j=0; j<3; j++)
+ {
+ //Spell targets self
+ if ( TempSpell->EffectImplicitTargetA[j] == TARGET_SELF )
+ SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SELF-1);
+
+ //Spell targets a single enemy
+ if ( TempSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_ENEMY_COORDINATES )
+ SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SINGLE_ENEMY-1);
+
+ //Spell targets AoE at enemy
+ if ( TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED )
+ SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_ENEMY-1);
+
+ //Spell targets an enemy
+ if ( TempSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_ENEMY_COORDINATES ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED )
+ SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_ENEMY-1);
+
+ //Spell targets a single friend(or self)
+ if ( TempSpell->EffectImplicitTargetA[j] == TARGET_SELF ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY )
+ SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SINGLE_FRIEND-1);
+
+ //Spell targets aoe friends
+ if ( TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER)
+ SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_FRIEND-1);
+
+ //Spell targets any friend(or self)
+ if ( TempSpell->EffectImplicitTargetA[j] == TARGET_SELF ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY ||
+ TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_AROUND_CASTER)
+ SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_FRIEND-1);
+
+ //Make sure that this spell includes a damage effect
+ if ( TempSpell->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE ||
+ TempSpell->Effect[j] == SPELL_EFFECT_INSTAKILL ||
+ TempSpell->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE ||
+ TempSpell->Effect[j] == SPELL_EFFECT_HEALTH_LEECH )
+ SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_DAMAGE-1);
+
+ //Make sure that this spell includes a healing effect (or an apply aura with a periodic heal)
+ if ( TempSpell->Effect[j] == SPELL_EFFECT_HEAL ||
+ TempSpell->Effect[j] == SPELL_EFFECT_HEAL_MAX_HEALTH ||
+ TempSpell->Effect[j] == SPELL_EFFECT_HEAL_MECHANICAL ||
+ (TempSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA && TempSpell->EffectApplyAuraName[j]== 8 ))
+ SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_HEALING-1);
+
+ //Make sure that this spell applies an aura
+ if ( TempSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA )
+ SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_AURA-1);
+ }
+ }
+}
+
+void ScriptedAI::DoZoneInCombat(Unit* pUnit)
+{
+ if (!pUnit)
+ pUnit = m_creature;
+
+ Map *map = pUnit->GetMap();
+
+ if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated
+ {
+ error_log("SD2: DoZoneInCombat call for map that isn't an instance (pUnit entry = %d)", pUnit->GetTypeId() == TYPEID_UNIT ? ((Creature*)pUnit)->GetEntry() : 0);
+ return;
+ }
+
+ if (!pUnit->CanHaveThreatList() || pUnit->getThreatManager().isThreatListEmpty())
+ {
+ error_log("SD2: DoZoneInCombat called for creature that either cannot have threat list or has empty threat list (pUnit entry = %d)", pUnit->GetTypeId() == TYPEID_UNIT ? ((Creature*)pUnit)->GetEntry() : 0);
+
+ return;
+ }
+
+ InstanceMap::PlayerList const &PlayerList = ((InstanceMap*)map)->GetPlayers();
+ InstanceMap::PlayerList::const_iterator i;
+ for (i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ {
+ if(!(*i)->isGameMaster())
+ pUnit->AddThreat(*i, 0.0f);
+ }
+}
+
+void ScriptedAI::DoResetThreat()
+{
+ if (!m_creature->CanHaveThreatList() || m_creature->getThreatManager().isThreatListEmpty())
+ {
+ error_log("SD2: DoResetThreat called for creature that either cannot have threat list or has empty threat list (m_creature entry = %d)", m_creature->GetEntry());
+
+ return;
+ }
+
+ std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
+ std::list<HostilReference*>::iterator itr;
+
+ for(itr = m_threatlist.begin(); itr != m_threatlist.end(); ++itr)
+ {
+ Unit* pUnit = NULL;
+ pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid());
+ if(pUnit && m_creature->getThreatManager().getThreat(pUnit))
+ m_creature->getThreatManager().modifyThreatPercent(pUnit, -100);
+ }
+}
+
+void ScriptedAI::DoTeleportPlayer(Unit* pUnit, float x, float y, float z, float o)
+{
+ if(!pUnit || pUnit->GetTypeId() != TYPEID_PLAYER)
+ {
+ if(pUnit)
+ error_log("SD2: Creature %u (Entry: %u) Tried to teleport non-player unit (Type: %u GUID: %u) to x: %f y:%f z: %f o: %f. Aborted.", m_creature->GetGUID(), m_creature->GetEntry(), pUnit->GetTypeId(), pUnit->GetGUID(), x, y, z, o);
+ return;
+ }
+
+ ((Player*)pUnit)->TeleportTo(pUnit->GetMapId(), x, y, z, o, TELE_TO_NOT_LEAVE_COMBAT);
+}
+
+Unit* ScriptedAI::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff)
+{
+ CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ Unit* pUnit = NULL;
+
+ MaNGOS::MostHPMissingInRange u_check(m_creature, range, MinHPDiff);
+ MaNGOS::UnitLastSearcher<MaNGOS::MostHPMissingInRange> searcher(pUnit, u_check);
+
+ /*
+ typedef TYPELIST_4(GameObject, Creature*except pets*, DynamicObject, Corpse*Bones*) AllGridObjectTypes;
+ This means that if we only search grid then we cannot possibly return pets or players so this is safe
+ */
+ TypeContainerVisitor<MaNGOS::UnitLastSearcher<MaNGOS::MostHPMissingInRange>, GridTypeMapContainer > grid_unit_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *(m_creature->GetMap()));
+ return pUnit;
+}
+
+std::list<Creature*> ScriptedAI::DoFindFriendlyCC(float range)
+{
+ CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ std::list<Creature*> pList;
+
+ MaNGOS::FriendlyCCedInRange u_check(m_creature, range);
+ MaNGOS::CreatureListSearcher<MaNGOS::FriendlyCCedInRange> searcher(pList, u_check);
+
+ TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::FriendlyCCedInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_creature_searcher, *(m_creature->GetMap()));
+
+ return pList;
+}
+
+std::list<Creature*> ScriptedAI::DoFindFriendlyMissingBuff(float range, uint32 spellid)
+{
+ CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ std::list<Creature*> pList;
+
+ MaNGOS::FriendlyMissingBuffInRange u_check(m_creature, range, spellid);
+ MaNGOS::CreatureListSearcher<MaNGOS::FriendlyMissingBuffInRange> searcher(pList, u_check);
+
+ TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::FriendlyMissingBuffInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_creature_searcher, *(m_creature->GetMap()));
+
+ return pList;
+}
+
+void Scripted_NoMovementAI::MoveInLineOfSight(Unit *who)
+{
+ if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) )
+ {
+ if (!m_creature->canFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
+ return;
+
+ float attackRadius = m_creature->GetAttackDistance(who);
+ if( m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who) )
+ {
+ DoStartAttackNoMovement(who);
+ who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+
+ if (!InCombat)
+ {
+ InCombat = true;
+ Aggro(who);
+ }
+ }
+ }
+}
+
+void Scripted_NoMovementAI::AttackStart(Unit* who)
+{
+ if (!who)
+ return;
+
+ if (who->isTargetableForAttack())
+ {
+ //Begin attack
+ DoStartAttackNoMovement(who);
+
+ if (!InCombat)
+ {
+ InCombat = true;
+ Aggro(who);
+ }
+ }
+}
diff --git a/src/bindings/scripts/include/sc_gossip.h b/src/bindings/scripts/include/sc_gossip.h
index 48d9786a4ed..9857661ae9a 100644
--- a/src/bindings/scripts/include/sc_gossip.h
+++ b/src/bindings/scripts/include/sc_gossip.h
@@ -1,183 +1,183 @@
-/* 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 SC_PLAYER_H
-#define SC_PLAYER_H
-
-#include "Player.h"
-#include "GossipDef.h"
-#include "QuestDef.h"
-
-// Gossip Item Text
-#define GOSSIP_TEXT_BROWSE_GOODS "I'd like to browse your goods."
-#define GOSSIP_TEXT_TRAIN "Train me!"
-
-#define GOSSIP_TEXT_BANK "The Bank"
-#define GOSSIP_TEXT_WINDRIDER "Wind rider master"
-#define GOSSIP_TEXT_GRYPHON "Gryphon Master"
-#define GOSSIP_TEXT_BATHANDLER "Bat Handler"
-#define GOSSIP_TEXT_HIPPOGRYPH "Hippogryph Master"
-#define GOSSIP_TEXT_FLIGHTMASTER "Flight Master"
-#define GOSSIP_TEXT_AUCTIONHOUSE "Auction House"
-#define GOSSIP_TEXT_GUILDMASTER "Guild Master"
-#define GOSSIP_TEXT_INN "The Inn"
-#define GOSSIP_TEXT_MAILBOX "Mailbox"
-#define GOSSIP_TEXT_STABLEMASTER "Stable Master"
-#define GOSSIP_TEXT_WEAPONMASTER "Weapons Trainer"
-#define GOSSIP_TEXT_BATTLEMASTER "Battlemaster"
-#define GOSSIP_TEXT_CLASSTRAINER "Class Trainer"
-#define GOSSIP_TEXT_PROFTRAINER "Profession Trainer"
-#define GOSSIP_TEXT_OFFICERS "The officers` lounge"
-
-#define GOSSIP_TEXT_ALTERACVALLEY "Alterac Valley"
-#define GOSSIP_TEXT_ARATHIBASIN "Arathi Basin"
-#define GOSSIP_TEXT_WARSONGULCH "Warsong Gulch"
-#define GOSSIP_TEXT_ARENA "Arena"
-#define GOSSIP_TEXT_EYEOFTHESTORM "Eye of The Storm"
-
-#define GOSSIP_TEXT_DRUID "Druid"
-#define GOSSIP_TEXT_HUNTER "Hunter"
-#define GOSSIP_TEXT_PRIEST "Priest"
-#define GOSSIP_TEXT_ROGUE "Rogue"
-#define GOSSIP_TEXT_WARRIOR "Warrior"
-#define GOSSIP_TEXT_PALADIN "Paladin"
-#define GOSSIP_TEXT_SHAMAN "Shaman"
-#define GOSSIP_TEXT_MAGE "Mage"
-#define GOSSIP_TEXT_WARLOCK "Warlock"
-
-#define GOSSIP_TEXT_ALCHEMY "Alchemy"
-#define GOSSIP_TEXT_BLACKSMITHING "Blacksmithing"
-#define GOSSIP_TEXT_COOKING "Cooking"
-#define GOSSIP_TEXT_ENCHANTING "Enchanting"
-#define GOSSIP_TEXT_ENGINEERING "Engineering"
-#define GOSSIP_TEXT_FIRSTAID "First Aid"
-#define GOSSIP_TEXT_HERBALISM "Herbalism"
-#define GOSSIP_TEXT_LEATHERWORKING "Leatherworking"
-#define GOSSIP_TEXT_POISONS "Poisons"
-#define GOSSIP_TEXT_TAILORING "Tailoring"
-#define GOSSIP_TEXT_MINING "Mining"
-#define GOSSIP_TEXT_FISHING "Fishing"
-#define GOSSIP_TEXT_SKINNING "Skinning"
-#define GOSSIP_TEXT_JEWELCRAFTING "Jewelcrafting"
-
-#define GOSSIP_TEXT_IRONFORGE_BANK "Bank of Ironforge"
-#define GOSSIP_TEXT_STORMWIND_BANK "Bank of Stormwind"
-#define GOSSIP_TEXT_DEEPRUNTRAM "Deeprun Tram"
-#define GOSSIP_TEXT_ZEPPLINMASTER "Zeppelin master"
-#define GOSSIP_TEXT_FERRY "Rut'theran Ferry"
-
-// Skill defines
-
-#define TRADESKILL_ALCHEMY 1
-#define TRADESKILL_BLACKSMITHING 2
-#define TRADESKILL_COOKING 3
-#define TRADESKILL_ENCHANTING 4
-#define TRADESKILL_ENGINEERING 5
-#define TRADESKILL_FIRSTAID 6
-#define TRADESKILL_HERBALISM 7
-#define TRADESKILL_LEATHERWORKING 8
-#define TRADESKILL_POISONS 9
-#define TRADESKILL_TAILORING 10
-#define TRADESKILL_MINING 11
-#define TRADESKILL_FISHING 12
-#define TRADESKILL_SKINNING 13
-#define TRADESKILL_JEWLCRAFTING 14
-
-#define TRADESKILL_LEVEL_NONE 0
-#define TRADESKILL_LEVEL_APPRENTICE 1
-#define TRADESKILL_LEVEL_JOURNEYMAN 2
-#define TRADESKILL_LEVEL_EXPERT 3
-#define TRADESKILL_LEVEL_ARTISAN 4
-#define TRADESKILL_LEVEL_MASTER 5
-
-// Gossip defines
-
-#define GOSSIP_ACTION_TRADE 1
-#define GOSSIP_ACTION_TRAIN 2
-#define GOSSIP_ACTION_TAXI 3
-#define GOSSIP_ACTION_GUILD 4
-#define GOSSIP_ACTION_BATTLE 5
-#define GOSSIP_ACTION_BANK 6
-#define GOSSIP_ACTION_INN 7
-#define GOSSIP_ACTION_HEAL 8
-#define GOSSIP_ACTION_TABARD 9
-#define GOSSIP_ACTION_AUCTION 10
-#define GOSSIP_ACTION_INN_INFO 11
-#define GOSSIP_ACTION_UNLEARN 12
-#define GOSSIP_ACTION_INFO_DEF 1000
-
-#define GOSSIP_SENDER_MAIN 1
-#define GOSSIP_SENDER_INN_INFO 2
-#define GOSSIP_SENDER_INFO 3
-#define GOSSIP_SENDER_SEC_PROFTRAIN 4
-#define GOSSIP_SENDER_SEC_CLASSTRAIN 5
-#define GOSSIP_SENDER_SEC_BATTLEINFO 6
-#define GOSSIP_SENDER_SEC_BANK 7
-#define GOSSIP_SENDER_SEC_INN 8
-#define GOSSIP_SENDER_SEC_MAILBOX 9
-#define GOSSIP_SENDER_SEC_STABLEMASTER 10
-
-#define DEFAULT_GOSSIP_MESSAGE 0xffffff
-
-extern uint32 GetSkillLevel(Player *player,uint32 skill);
-
-// Defined fuctions to use with player.
-
-// This fuction add's a menu item,
-// a - Icon Id
-// b - Text
-// c - Sender(this is to identify the current Menu with this item)
-// d - Action (identifys this Menu Item)
-// e - Text to be displayed in pop up box
-// f - Money value in pop up box
-#define ADD_GOSSIP_ITEM(a,b,c,d) PlayerTalkClass->GetGossipMenu().AddMenuItem(a,b,c,d,"",0)
-#define ADD_GOSSIP_ITEM_EXTENDED(a,b,c,d,e,f,g) PlayerTalkClass->GetGossipMenu().AddMenuItem(a,b,c,d,e,f,g)
-
-// This fuction Sends the current menu to show to client, a - NPCTEXTID(uint32) , b - npc guid(uint64)
-#define SEND_GOSSIP_MENU(a,b) PlayerTalkClass->SendGossipMenu(a,b)
-
-// This fuction shows POI(point of interest) to client.
-// a - position X
-// b - position Y
-// c - Icon Id
-// d - Flags
-// e - Data
-// f - Location Name
-#define SEND_POI(a,b,c,d,e,f) PlayerTalkClass->SendPointOfInterest(a,b,c,d,e,f)
-
-// Closes the Menu
-#define CLOSE_GOSSIP_MENU() PlayerTalkClass->CloseGossip()
-
-// Fuction to tell to client the details
-// a - quest object
-// b - npc guid(uint64)
-// c - Activate accept(bool)
-#define SEND_QUEST_DETAILS(a,b,c) PlayerTalkClass->SendQuestDetails(a,b,c)
-
-// Fuction to tell to client the requested items to complete quest
-// a - quest object
-// b - npc guid(uint64)
-// c - Iscompletable(bool)
-// d - close at cancel(bool) - in case single incomplite ques
-#define SEND_REQUESTEDITEMS(a,b,c,d) PlayerTalkClass->SendRequestedItems(a,b,c,d)
-
-// Fuctions to send NPC lists, a - is always the npc guid(uint64)
-#define SEND_VENDORLIST(a) GetSession()->SendListInventory(a)
-#define SEND_TRAINERLIST(a) GetSession()->SendTrainerList(a)
-#define SEND_BANKERLIST(a) GetSession()->SendShowBank(a)
-#define SEND_TABARDLIST(a) GetSession()->SendTabardVendorActivate(a)
-#define SEND_AUCTIONLIST(a) GetSession()->SendAuctionHello(a)
-#define SEND_TAXILIST(a) GetSession()->SendTaxiStatus(a)
-
-// Ressurect's the player if is dead.
-#define SEND_SPRESURRECT() GetSession()->SendSpiritResurrect()
-
-// Get the player's honor rank.
-#define GET_HONORRANK() GetHonorRank()
-// -----------------------------------
-
-// defined fuctions to use with Creature
-
-#define QUEST_DIALOG_STATUS(a,b,c) GetSession()->getDialogStatus(a,b,c)
-#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 SC_PLAYER_H
+#define SC_PLAYER_H
+
+#include "Player.h"
+#include "GossipDef.h"
+#include "QuestDef.h"
+
+// Gossip Item Text
+#define GOSSIP_TEXT_BROWSE_GOODS "I'd like to browse your goods."
+#define GOSSIP_TEXT_TRAIN "Train me!"
+
+#define GOSSIP_TEXT_BANK "The Bank"
+#define GOSSIP_TEXT_WINDRIDER "Wind rider master"
+#define GOSSIP_TEXT_GRYPHON "Gryphon Master"
+#define GOSSIP_TEXT_BATHANDLER "Bat Handler"
+#define GOSSIP_TEXT_HIPPOGRYPH "Hippogryph Master"
+#define GOSSIP_TEXT_FLIGHTMASTER "Flight Master"
+#define GOSSIP_TEXT_AUCTIONHOUSE "Auction House"
+#define GOSSIP_TEXT_GUILDMASTER "Guild Master"
+#define GOSSIP_TEXT_INN "The Inn"
+#define GOSSIP_TEXT_MAILBOX "Mailbox"
+#define GOSSIP_TEXT_STABLEMASTER "Stable Master"
+#define GOSSIP_TEXT_WEAPONMASTER "Weapons Trainer"
+#define GOSSIP_TEXT_BATTLEMASTER "Battlemaster"
+#define GOSSIP_TEXT_CLASSTRAINER "Class Trainer"
+#define GOSSIP_TEXT_PROFTRAINER "Profession Trainer"
+#define GOSSIP_TEXT_OFFICERS "The officers` lounge"
+
+#define GOSSIP_TEXT_ALTERACVALLEY "Alterac Valley"
+#define GOSSIP_TEXT_ARATHIBASIN "Arathi Basin"
+#define GOSSIP_TEXT_WARSONGULCH "Warsong Gulch"
+#define GOSSIP_TEXT_ARENA "Arena"
+#define GOSSIP_TEXT_EYEOFTHESTORM "Eye of The Storm"
+
+#define GOSSIP_TEXT_DRUID "Druid"
+#define GOSSIP_TEXT_HUNTER "Hunter"
+#define GOSSIP_TEXT_PRIEST "Priest"
+#define GOSSIP_TEXT_ROGUE "Rogue"
+#define GOSSIP_TEXT_WARRIOR "Warrior"
+#define GOSSIP_TEXT_PALADIN "Paladin"
+#define GOSSIP_TEXT_SHAMAN "Shaman"
+#define GOSSIP_TEXT_MAGE "Mage"
+#define GOSSIP_TEXT_WARLOCK "Warlock"
+
+#define GOSSIP_TEXT_ALCHEMY "Alchemy"
+#define GOSSIP_TEXT_BLACKSMITHING "Blacksmithing"
+#define GOSSIP_TEXT_COOKING "Cooking"
+#define GOSSIP_TEXT_ENCHANTING "Enchanting"
+#define GOSSIP_TEXT_ENGINEERING "Engineering"
+#define GOSSIP_TEXT_FIRSTAID "First Aid"
+#define GOSSIP_TEXT_HERBALISM "Herbalism"
+#define GOSSIP_TEXT_LEATHERWORKING "Leatherworking"
+#define GOSSIP_TEXT_POISONS "Poisons"
+#define GOSSIP_TEXT_TAILORING "Tailoring"
+#define GOSSIP_TEXT_MINING "Mining"
+#define GOSSIP_TEXT_FISHING "Fishing"
+#define GOSSIP_TEXT_SKINNING "Skinning"
+#define GOSSIP_TEXT_JEWELCRAFTING "Jewelcrafting"
+
+#define GOSSIP_TEXT_IRONFORGE_BANK "Bank of Ironforge"
+#define GOSSIP_TEXT_STORMWIND_BANK "Bank of Stormwind"
+#define GOSSIP_TEXT_DEEPRUNTRAM "Deeprun Tram"
+#define GOSSIP_TEXT_ZEPPLINMASTER "Zeppelin master"
+#define GOSSIP_TEXT_FERRY "Rut'theran Ferry"
+
+// Skill defines
+
+#define TRADESKILL_ALCHEMY 1
+#define TRADESKILL_BLACKSMITHING 2
+#define TRADESKILL_COOKING 3
+#define TRADESKILL_ENCHANTING 4
+#define TRADESKILL_ENGINEERING 5
+#define TRADESKILL_FIRSTAID 6
+#define TRADESKILL_HERBALISM 7
+#define TRADESKILL_LEATHERWORKING 8
+#define TRADESKILL_POISONS 9
+#define TRADESKILL_TAILORING 10
+#define TRADESKILL_MINING 11
+#define TRADESKILL_FISHING 12
+#define TRADESKILL_SKINNING 13
+#define TRADESKILL_JEWLCRAFTING 14
+
+#define TRADESKILL_LEVEL_NONE 0
+#define TRADESKILL_LEVEL_APPRENTICE 1
+#define TRADESKILL_LEVEL_JOURNEYMAN 2
+#define TRADESKILL_LEVEL_EXPERT 3
+#define TRADESKILL_LEVEL_ARTISAN 4
+#define TRADESKILL_LEVEL_MASTER 5
+
+// Gossip defines
+
+#define GOSSIP_ACTION_TRADE 1
+#define GOSSIP_ACTION_TRAIN 2
+#define GOSSIP_ACTION_TAXI 3
+#define GOSSIP_ACTION_GUILD 4
+#define GOSSIP_ACTION_BATTLE 5
+#define GOSSIP_ACTION_BANK 6
+#define GOSSIP_ACTION_INN 7
+#define GOSSIP_ACTION_HEAL 8
+#define GOSSIP_ACTION_TABARD 9
+#define GOSSIP_ACTION_AUCTION 10
+#define GOSSIP_ACTION_INN_INFO 11
+#define GOSSIP_ACTION_UNLEARN 12
+#define GOSSIP_ACTION_INFO_DEF 1000
+
+#define GOSSIP_SENDER_MAIN 1
+#define GOSSIP_SENDER_INN_INFO 2
+#define GOSSIP_SENDER_INFO 3
+#define GOSSIP_SENDER_SEC_PROFTRAIN 4
+#define GOSSIP_SENDER_SEC_CLASSTRAIN 5
+#define GOSSIP_SENDER_SEC_BATTLEINFO 6
+#define GOSSIP_SENDER_SEC_BANK 7
+#define GOSSIP_SENDER_SEC_INN 8
+#define GOSSIP_SENDER_SEC_MAILBOX 9
+#define GOSSIP_SENDER_SEC_STABLEMASTER 10
+
+#define DEFAULT_GOSSIP_MESSAGE 0xffffff
+
+extern uint32 GetSkillLevel(Player *player,uint32 skill);
+
+// Defined fuctions to use with player.
+
+// This fuction add's a menu item,
+// a - Icon Id
+// b - Text
+// c - Sender(this is to identify the current Menu with this item)
+// d - Action (identifys this Menu Item)
+// e - Text to be displayed in pop up box
+// f - Money value in pop up box
+#define ADD_GOSSIP_ITEM(a,b,c,d) PlayerTalkClass->GetGossipMenu().AddMenuItem(a,b,c,d,"",0)
+#define ADD_GOSSIP_ITEM_EXTENDED(a,b,c,d,e,f,g) PlayerTalkClass->GetGossipMenu().AddMenuItem(a,b,c,d,e,f,g)
+
+// This fuction Sends the current menu to show to client, a - NPCTEXTID(uint32) , b - npc guid(uint64)
+#define SEND_GOSSIP_MENU(a,b) PlayerTalkClass->SendGossipMenu(a,b)
+
+// This fuction shows POI(point of interest) to client.
+// a - position X
+// b - position Y
+// c - Icon Id
+// d - Flags
+// e - Data
+// f - Location Name
+#define SEND_POI(a,b,c,d,e,f) PlayerTalkClass->SendPointOfInterest(a,b,c,d,e,f)
+
+// Closes the Menu
+#define CLOSE_GOSSIP_MENU() PlayerTalkClass->CloseGossip()
+
+// Fuction to tell to client the details
+// a - quest object
+// b - npc guid(uint64)
+// c - Activate accept(bool)
+#define SEND_QUEST_DETAILS(a,b,c) PlayerTalkClass->SendQuestDetails(a,b,c)
+
+// Fuction to tell to client the requested items to complete quest
+// a - quest object
+// b - npc guid(uint64)
+// c - Iscompletable(bool)
+// d - close at cancel(bool) - in case single incomplite ques
+#define SEND_REQUESTEDITEMS(a,b,c,d) PlayerTalkClass->SendRequestedItems(a,b,c,d)
+
+// Fuctions to send NPC lists, a - is always the npc guid(uint64)
+#define SEND_VENDORLIST(a) GetSession()->SendListInventory(a)
+#define SEND_TRAINERLIST(a) GetSession()->SendTrainerList(a)
+#define SEND_BANKERLIST(a) GetSession()->SendShowBank(a)
+#define SEND_TABARDLIST(a) GetSession()->SendTabardVendorActivate(a)
+#define SEND_AUCTIONLIST(a) GetSession()->SendAuctionHello(a)
+#define SEND_TAXILIST(a) GetSession()->SendTaxiStatus(a)
+
+// Ressurect's the player if is dead.
+#define SEND_SPRESURRECT() GetSession()->SendSpiritResurrect()
+
+// Get the player's honor rank.
+#define GET_HONORRANK() GetHonorRank()
+// -----------------------------------
+
+// defined fuctions to use with Creature
+
+#define QUEST_DIALOG_STATUS(a,b,c) GetSession()->getDialogStatus(a,b,c)
+#endif
diff --git a/src/bindings/scripts/sql/create_database.sql b/src/bindings/scripts/sql/create_database.sql
index 39da79b6fde..2673b90555a 100644
--- a/src/bindings/scripts/sql/create_database.sql
+++ b/src/bindings/scripts/sql/create_database.sql
@@ -1,3 +1,3 @@
-CREATE DATABASE `trinityscript` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
-
-GRANT ALL PRIVILEGES ON `trinityscript` . * TO 'trinity'@'localhost' WITH GRANT OPTION;
+CREATE DATABASE `trinityscript` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
+
+GRANT ALL PRIVILEGES ON `trinityscript` . * TO 'trinity'@'localhost' WITH GRANT OPTION;
diff --git a/src/bindings/scripts/sql/scriptdev2_structure.sql b/src/bindings/scripts/sql/scriptdev2_structure.sql
index 122deb53357..db46c42c948 100644
--- a/src/bindings/scripts/sql/scriptdev2_structure.sql
+++ b/src/bindings/scripts/sql/scriptdev2_structure.sql
@@ -1,115 +1,115 @@
-DROP TABLE IF EXISTS `eventai_scripts`;
-CREATE TABLE `eventai_scripts` (
-`id` int(11) unsigned NOT NULL COMMENT 'Identifier' AUTO_INCREMENT,
-`creature_id` int(11) unsigned NOT NULL default '0' COMMENT 'Creature Template Identifier',
-
-`event_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Event Type',
-`event_inverse_phase_mask` int(11) signed NOT NULL default '0' COMMENT 'Mask which phases this event will not trigger in',
-`event_chance` int(3) unsigned NOT NULL default '100',
-`event_flags` int(3) unsigned NOT NULL default '0',
-`event_param1` int(11) signed NOT NULL default '0',
-`event_param2` int(11) signed NOT NULL default '0',
-`event_param3` int(11) signed NOT NULL default '0',
-`event_param4` int(11) signed NOT NULL default '0',
-
-`action1_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
-`action1_param1` int(11) signed NOT NULL default '0',
-`action1_param2` int(11) signed NOT NULL default '0',
-`action1_param3` int(11) signed NOT NULL default '0',
-
-`action2_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
-`action2_param1` int(11) signed NOT NULL default '0',
-`action2_param2` int(11) signed NOT NULL default '0',
-`action2_param3` int(11) signed NOT NULL default '0',
-
-`action3_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
-`action3_param1` int(11) signed NOT NULL default '0',
-`action3_param2` int(11) signed NOT NULL default '0',
-`action3_param3` int(11) signed NOT NULL default '0',
-
-`comment` varchar(255) NOT NULL default '' COMMENT 'Event Comment',
-
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='EventAI Scripts';
-
-DROP TABLE IF EXISTS `eventai_texts`;
-CREATE TABLE `eventai_texts` (
-
-`id` int(11) unsigned NOT NULL default '0' COMMENT 'Identifier',
-`text` varchar(255) character set utf8 NOT NULL default '',
-`comment` varchar(255) character set utf8 NOT NULL default '' COMMENT 'Text Comment',
-
-PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Texts used in EventAI';
-
-DROP TABLE IF EXISTS `eventai_localized_texts`;
-CREATE TABLE `eventai_localized_texts` (
-
-`id` int(11) unsigned NOT NULL COMMENT 'Identifier' AUTO_INCREMENT,
-`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='EventAI Localized Text';
-
-DROP TABLE IF EXISTS `eventai_summons`;
-CREATE TABLE `eventai_summons` (
-`id` int(11) unsigned NOT NULL COMMENT 'Location Identifier' AUTO_INCREMENT,
-`position_x` float NOT NULL default '0',
-`position_y` float NOT NULL default '0',
-`position_z` float NOT NULL default '0',
-`orientation` float NOT NULL default '0',
-`spawntimesecs` int(11) unsigned NOT NULL default '120',
-`comment` varchar(255) NOT NULL default '' COMMENT 'Summon Comment',
-PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='EventAI Summoning Locations';
-
-DROP TABLE IF EXISTS `script_texts`;
-CREATE TABLE `script_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='Script Texts';
-
-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` (
-`version` varchar(255) NOT NULL default '' COMMENT 'Script Database version string'
+DROP TABLE IF EXISTS `eventai_scripts`;
+CREATE TABLE `eventai_scripts` (
+`id` int(11) unsigned NOT NULL COMMENT 'Identifier' AUTO_INCREMENT,
+`creature_id` int(11) unsigned NOT NULL default '0' COMMENT 'Creature Template Identifier',
+
+`event_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Event Type',
+`event_inverse_phase_mask` int(11) signed NOT NULL default '0' COMMENT 'Mask which phases this event will not trigger in',
+`event_chance` int(3) unsigned NOT NULL default '100',
+`event_flags` int(3) unsigned NOT NULL default '0',
+`event_param1` int(11) signed NOT NULL default '0',
+`event_param2` int(11) signed NOT NULL default '0',
+`event_param3` int(11) signed NOT NULL default '0',
+`event_param4` int(11) signed NOT NULL default '0',
+
+`action1_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
+`action1_param1` int(11) signed NOT NULL default '0',
+`action1_param2` int(11) signed NOT NULL default '0',
+`action1_param3` int(11) signed NOT NULL default '0',
+
+`action2_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
+`action2_param1` int(11) signed NOT NULL default '0',
+`action2_param2` int(11) signed NOT NULL default '0',
+`action2_param3` int(11) signed NOT NULL default '0',
+
+`action3_type` tinyint(5) unsigned NOT NULL default '0' COMMENT 'Action Type',
+`action3_param1` int(11) signed NOT NULL default '0',
+`action3_param2` int(11) signed NOT NULL default '0',
+`action3_param3` int(11) signed NOT NULL default '0',
+
+`comment` varchar(255) NOT NULL default '' COMMENT 'Event Comment',
+
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='EventAI Scripts';
+
+DROP TABLE IF EXISTS `eventai_texts`;
+CREATE TABLE `eventai_texts` (
+
+`id` int(11) unsigned NOT NULL default '0' COMMENT 'Identifier',
+`text` varchar(255) character set utf8 NOT NULL default '',
+`comment` varchar(255) character set utf8 NOT NULL default '' COMMENT 'Text Comment',
+
+PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Texts used in EventAI';
+
+DROP TABLE IF EXISTS `eventai_localized_texts`;
+CREATE TABLE `eventai_localized_texts` (
+
+`id` int(11) unsigned NOT NULL COMMENT 'Identifier' AUTO_INCREMENT,
+`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='EventAI Localized Text';
+
+DROP TABLE IF EXISTS `eventai_summons`;
+CREATE TABLE `eventai_summons` (
+`id` int(11) unsigned NOT NULL COMMENT 'Location Identifier' AUTO_INCREMENT,
+`position_x` float NOT NULL default '0',
+`position_y` float NOT NULL default '0',
+`position_z` float NOT NULL default '0',
+`orientation` float NOT NULL default '0',
+`spawntimesecs` int(11) unsigned NOT NULL default '120',
+`comment` varchar(255) NOT NULL default '' COMMENT 'Summon Comment',
+PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='EventAI Summoning Locations';
+
+DROP TABLE IF EXISTS `script_texts`;
+CREATE TABLE `script_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='Script Texts';
+
+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` (
+`version` varchar(255) NOT NULL default '' COMMENT 'Script Database version string'
) ENGINE=MyISAM DEFAULT CHARSET=utf8; \ No newline at end of file
diff --git a/src/bindings/scripts/trinityscript.conf.dist b/src/bindings/scripts/trinityscript.conf.dist
index 8a91eda5deb..b391945c6a2 100644
--- a/src/bindings/scripts/trinityscript.conf.dist
+++ b/src/bindings/scripts/trinityscript.conf.dist
@@ -1,20 +1,20 @@
-# TrinityScript Configuration file
-# This file must be placed within the directory which holds TrinityCore.conf and TrinityRealm.conf
-ConfVersion=2008100201
-
-# Database connection settings for the world server.
-# Default: hostname;port;username;password;database
-# .;somenumber;username;password;database - use named pipes at Windows
-# Named pipes: mySQL required adding "enable-named-pipe" to [mysqld] section my.ini
-# .;/path/to/unix_socket;username;password;database - use Unix sockets at Unix/Linux
-# Unix sockets: experimental, not tested
-TScriptDatabaseInfo = "127.0.0.1;3306;trinity;trinity;trinityscript"
-
-# Setting for current locale to use
-Locale = 0
-
-# EventAI Error reporting
-# 0 - Only startup (Default)
-# 1 - Startup errors and Runtime event errors
-# 2 - Startup errors, Runtime event errors, and Creation errors
-EAIErrorLevel = 1
+# TrinityScript Configuration file
+# This file must be placed within the directory which holds TrinityCore.conf and TrinityRealm.conf
+ConfVersion=2008100201
+
+# Database connection settings for the world server.
+# Default: hostname;port;username;password;database
+# .;somenumber;username;password;database - use named pipes at Windows
+# Named pipes: mySQL required adding "enable-named-pipe" to [mysqld] section my.ini
+# .;/path/to/unix_socket;username;password;database - use Unix sockets at Unix/Linux
+# Unix sockets: experimental, not tested
+TScriptDatabaseInfo = "127.0.0.1;3306;trinity;trinity;trinityscript"
+
+# Setting for current locale to use
+Locale = 0
+
+# EventAI Error reporting
+# 0 - Only startup (Default)
+# 1 - Startup errors and Runtime event errors
+# 2 - Startup errors, Runtime event errors, and Creation errors
+EAIErrorLevel = 1
diff --git a/src/game/AggressorAI.cpp b/src/game/AggressorAI.cpp
index 58604183660..6cdbddeb3ed 100644
--- a/src/game/AggressorAI.cpp
+++ b/src/game/AggressorAI.cpp
@@ -1,157 +1,157 @@
-/*
- * 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 "AggressorAI.h"
-#include "Errors.h"
-#include "Creature.h"
-#include "Player.h"
-#include "ObjectAccessor.h"
-#include "VMapFactory.h"
-#include "World.h"
-
-#include <list>
-
-int
-AggressorAI::Permissible(const Creature *creature)
-{
- // have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight
- if( !creature->isCivilian() && !creature->IsNeutralToAll() )
- return PERMIT_BASE_PROACTIVE;
-
- return PERMIT_BASE_NO;
-}
-
-AggressorAI::AggressorAI(Creature &c) : i_creature(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
-{
-}
-
-void
-AggressorAI::MoveInLineOfSight(Unit *u)
-{
- // Ignore Z for flying creatures
- if( !i_creature.canFly() && i_creature.GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE )
- return;
-
- 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) )
- {
- float attackRadius = i_creature.GetAttackDistance(u);
- if(i_creature.IsWithinDistInMap(u, attackRadius) && i_creature.IsWithinLOSInMap(u) )
- {
- AttackStart(u);
- u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
- }
- }
-}
-
-void AggressorAI::EnterEvadeMode()
-{
- if( !i_creature.isAlive() )
- {
- DEBUG_LOG("Creature stopped attacking cuz his dead [guid=%u]", i_creature.GetGUIDLow());
- i_victimGuid = 0;
- i_creature.CombatStop();
- i_creature.DeleteThreatList();
- return;
- }
-
- Unit* victim = ObjectAccessor::GetUnit(i_creature, i_victimGuid );
-
- if( !victim )
- {
- DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow());
- }
- else if( !victim->isAlive() )
- {
- DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_creature.GetGUIDLow());
- }
- else if( victim->HasStealthAura() )
- {
- DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_creature.GetGUIDLow());
- }
- else if( victim->isInFlight() )
- {
- DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_creature.GetGUIDLow());
- }
- else
- {
- DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_creature.GetGUIDLow());
- //i_state = STATE_LOOK_AT_VICTIM;
- //i_tracker.Reset(TIME_INTERVAL_LOOK);
- }
-
- if(!i_creature.isCharmed())
- {
- i_creature.RemoveAllAuras();
-
- // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
- if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE )
- i_creature.GetMotionMaster()->MoveTargetedHome();
- }
-
- i_creature.DeleteThreatList();
- i_victimGuid = 0;
- i_creature.CombatStop();
- i_creature.SetLootRecipient(NULL);
-}
-
-void
-AggressorAI::UpdateAI(const uint32 /*diff*/)
-{
- // update i_victimGuid if i_creature.getVictim() !=0 and changed
- if(!i_creature.SelectHostilTarget() || !i_creature.getVictim())
- return;
-
- i_victimGuid = i_creature.getVictim()->GetGUID();
-
- if( i_creature.isAttackReady() )
- {
- if( i_creature.IsWithinDistInMap(i_creature.getVictim(), ATTACK_DISTANCE))
- {
- i_creature.AttackerStateUpdate(i_creature.getVictim());
- i_creature.resetAttackTimer();
- }
- }
-}
-
-bool
-AggressorAI::IsVisible(Unit *pl) const
-{
- return i_creature.GetDistance(pl) < sWorld.getConfig(CONFIG_SIGHT_MONSTER)
- && pl->isVisibleForOrDetect(&i_creature,true);
-}
-
-void
-AggressorAI::AttackStart(Unit *u)
-{
- if( !u )
- return;
-
- if(i_creature.Attack(u,true))
- {
- i_creature.SetInCombatWith(u);
- u->SetInCombatWith(&i_creature);
-
- i_creature.AddThreat(u, 0.0f);
- // DEBUG_LOG("Creature %s tagged a victim to kill [guid=%u]", i_creature.GetName(), u->GetGUIDLow());
- i_victimGuid = u->GetGUID();
-
- i_creature.GetMotionMaster()->MoveChase(u);
- }
-}
+/*
+ * 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 "AggressorAI.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "Player.h"
+#include "ObjectAccessor.h"
+#include "VMapFactory.h"
+#include "World.h"
+
+#include <list>
+
+int
+AggressorAI::Permissible(const Creature *creature)
+{
+ // have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight
+ if( !creature->isCivilian() && !creature->IsNeutralToAll() )
+ return PERMIT_BASE_PROACTIVE;
+
+ return PERMIT_BASE_NO;
+}
+
+AggressorAI::AggressorAI(Creature &c) : i_creature(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
+{
+}
+
+void
+AggressorAI::MoveInLineOfSight(Unit *u)
+{
+ // Ignore Z for flying creatures
+ if( !i_creature.canFly() && i_creature.GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE )
+ return;
+
+ 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) )
+ {
+ float attackRadius = i_creature.GetAttackDistance(u);
+ if(i_creature.IsWithinDistInMap(u, attackRadius) && i_creature.IsWithinLOSInMap(u) )
+ {
+ AttackStart(u);
+ u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ }
+ }
+}
+
+void AggressorAI::EnterEvadeMode()
+{
+ if( !i_creature.isAlive() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his dead [guid=%u]", i_creature.GetGUIDLow());
+ i_victimGuid = 0;
+ i_creature.CombatStop();
+ i_creature.DeleteThreatList();
+ return;
+ }
+
+ Unit* victim = ObjectAccessor::GetUnit(i_creature, i_victimGuid );
+
+ if( !victim )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( !victim->isAlive() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( victim->HasStealthAura() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( victim->isInFlight() )
+ {
+ DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else
+ {
+ DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_creature.GetGUIDLow());
+ //i_state = STATE_LOOK_AT_VICTIM;
+ //i_tracker.Reset(TIME_INTERVAL_LOOK);
+ }
+
+ if(!i_creature.isCharmed())
+ {
+ i_creature.RemoveAllAuras();
+
+ // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
+ if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE )
+ i_creature.GetMotionMaster()->MoveTargetedHome();
+ }
+
+ i_creature.DeleteThreatList();
+ i_victimGuid = 0;
+ i_creature.CombatStop();
+ i_creature.SetLootRecipient(NULL);
+}
+
+void
+AggressorAI::UpdateAI(const uint32 /*diff*/)
+{
+ // update i_victimGuid if i_creature.getVictim() !=0 and changed
+ if(!i_creature.SelectHostilTarget() || !i_creature.getVictim())
+ return;
+
+ i_victimGuid = i_creature.getVictim()->GetGUID();
+
+ if( i_creature.isAttackReady() )
+ {
+ if( i_creature.IsWithinDistInMap(i_creature.getVictim(), ATTACK_DISTANCE))
+ {
+ i_creature.AttackerStateUpdate(i_creature.getVictim());
+ i_creature.resetAttackTimer();
+ }
+ }
+}
+
+bool
+AggressorAI::IsVisible(Unit *pl) const
+{
+ return i_creature.GetDistance(pl) < sWorld.getConfig(CONFIG_SIGHT_MONSTER)
+ && pl->isVisibleForOrDetect(&i_creature,true);
+}
+
+void
+AggressorAI::AttackStart(Unit *u)
+{
+ if( !u )
+ return;
+
+ if(i_creature.Attack(u,true))
+ {
+ i_creature.SetInCombatWith(u);
+ u->SetInCombatWith(&i_creature);
+
+ i_creature.AddThreat(u, 0.0f);
+ // DEBUG_LOG("Creature %s tagged a victim to kill [guid=%u]", i_creature.GetName(), u->GetGUIDLow());
+ i_victimGuid = u->GetGUID();
+
+ i_creature.GetMotionMaster()->MoveChase(u);
+ }
+}
diff --git a/src/game/ArenaTeamHandler.cpp b/src/game/ArenaTeamHandler.cpp
index 58293cb96cb..96f3a3ccee2 100644
--- a/src/game/ArenaTeamHandler.cpp
+++ b/src/game/ArenaTeamHandler.cpp
@@ -1,463 +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"
-#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"
-*/
+/*
+ * 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 eb9a593decf..8de2cd23426 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(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);
-}
+/*
+ * 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 036de1e7399..1797b4cc7a1 100644
--- a/src/game/CharacterHandler.cpp
+++ b/src/game/CharacterHandler.cpp
@@ -1,1124 +1,1124 @@
-/*
- * 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(sWorld.getConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when logging in
- pCurrChar->UpdateSkillsToMaxSkillsForLevel();
-
- //ImpConfig - Check if player has logged in before
- QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM has_logged_in_before WHERE guid = %u",pCurrChar->GetGUIDLow());
- if(!result)
- {
- sLog.outBasic("Character '%s' logging in for first time, applying skills and stuff",pCurrChar->GetName());
- CharacterDatabase.PExecute("INSERT INTO has_logged_in_before VALUES (%u)",pCurrChar->GetGUIDLow());
-
- //Reputations if "StartAllReputation" is enabled, -- TODO: Fix this in a better way
- if(sWorld.getConfig(CONFIG_START_ALL_REP))
- {
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(942),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(935),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(936),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1011),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(970),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(967),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(989),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(932),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(934),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1038),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1077),42999);
-
- // Factions depending on team, like cities and some more stuff
- switch(pCurrChar->GetTeam())
- {
- case ALLIANCE:
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(72),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(47),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(69),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(930),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(730),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(978),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(54),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(946),42999);
- break;
- case HORDE:
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(76),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(68),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(81),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(911),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(729),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(941),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(530),42999);
- pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(947),42999);
- break;
- }
- }
- }
- else
- sLog.outBasic("Character '%s' has logged in before",pCurrChar->GetName());
-
- if(sWorld.getConfig(CONFIG_START_ALL_TAXI))
- pCurrChar->SetTaxiCheater(true);
-
-
- 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);
-}
+/*
+ * 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(sWorld.getConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when logging in
+ pCurrChar->UpdateSkillsToMaxSkillsForLevel();
+
+ //ImpConfig - Check if player has logged in before
+ QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM has_logged_in_before WHERE guid = %u",pCurrChar->GetGUIDLow());
+ if(!result)
+ {
+ sLog.outBasic("Character '%s' logging in for first time, applying skills and stuff",pCurrChar->GetName());
+ CharacterDatabase.PExecute("INSERT INTO has_logged_in_before VALUES (%u)",pCurrChar->GetGUIDLow());
+
+ //Reputations if "StartAllReputation" is enabled, -- TODO: Fix this in a better way
+ if(sWorld.getConfig(CONFIG_START_ALL_REP))
+ {
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(942),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(935),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(936),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1011),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(970),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(967),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(989),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(932),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(934),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1038),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(1077),42999);
+
+ // Factions depending on team, like cities and some more stuff
+ switch(pCurrChar->GetTeam())
+ {
+ case ALLIANCE:
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(72),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(47),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(69),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(930),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(730),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(978),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(54),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(946),42999);
+ break;
+ case HORDE:
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(76),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(68),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(81),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(911),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(729),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(941),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(530),42999);
+ pCurrChar->SetFactionReputation(sFactionStore.LookupEntry(947),42999);
+ break;
+ }
+ }
+ }
+ else
+ sLog.outBasic("Character '%s' has logged in before",pCurrChar->GetName());
+
+ if(sWorld.getConfig(CONFIG_START_ALL_TAXI))
+ pCurrChar->SetTaxiCheater(true);
+
+
+ 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 e8867c516f4..b8e12a5a8b1 100644
--- a/src/game/Chat.cpp
+++ b/src/game/Chat.cpp
@@ -1,1084 +1,1084 @@
-/*
- * 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 "WorldSession.h"
-#include "Opcodes.h"
-#include "Log.h"
-#include "World.h"
-#include "ObjectMgr.h"
-#include "Player.h"
-#include "UpdateMask.h"
-#include "Chat.h"
-#include "MapManager.h"
-#include "GridNotifiersImpl.h"
-#include "CellImpl.h"
-
-bool ChatHandler::load_command_table = true;
-
-ChatCommand * ChatHandler::getCommandTable()
-{
- static ChatCommand serverCommandTable[] =
- {
- { "idlerestart", SEC_ADMINISTRATOR, &ChatHandler::HandleIdleRestartCommand, "", NULL },
- { "idleshutdown", SEC_ADMINISTRATOR, &ChatHandler::HandleIdleShutDownCommand, "", NULL },
- { "info", SEC_PLAYER, &ChatHandler::HandleInfoCommand, "", NULL },
- { "restart", SEC_ADMINISTRATOR, &ChatHandler::HandleRestartCommand, "", NULL },
- { "shutdown", SEC_ADMINISTRATOR, &ChatHandler::HandleShutDownCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand modifyCommandTable[] =
- {
- { "hp", SEC_MODERATOR, &ChatHandler::HandleModifyHPCommand, "", NULL },
- { "mana", SEC_MODERATOR, &ChatHandler::HandleModifyManaCommand, "", NULL },
- { "rage", SEC_MODERATOR, &ChatHandler::HandleModifyRageCommand, "", NULL },
- { "energy", SEC_MODERATOR, &ChatHandler::HandleModifyEnergyCommand, "", NULL },
- { "money", SEC_MODERATOR, &ChatHandler::HandleModifyMoneyCommand, "", NULL },
- { "speed", SEC_MODERATOR, &ChatHandler::HandleModifySpeedCommand, "", NULL },
- { "swim", SEC_MODERATOR, &ChatHandler::HandleModifySwimCommand, "", NULL },
- { "scale", SEC_MODERATOR, &ChatHandler::HandleModifyScaleCommand, "", NULL },
- { "bit", SEC_MODERATOR, &ChatHandler::HandleModifyBitCommand, "", NULL },
- { "bwalk", SEC_MODERATOR, &ChatHandler::HandleModifyBWalkCommand, "", NULL },
- { "fly", SEC_MODERATOR, &ChatHandler::HandleModifyFlyCommand, "", NULL },
- { "aspeed", SEC_MODERATOR, &ChatHandler::HandleModifyASpeedCommand, "", NULL },
- { "faction", SEC_MODERATOR, &ChatHandler::HandleModifyFactionCommand, "", NULL },
- { "spell", SEC_MODERATOR, &ChatHandler::HandleModifySpellCommand, "", NULL },
- { "tp", SEC_MODERATOR, &ChatHandler::HandleModifyTalentCommand, "", NULL },
- { "titles", SEC_MODERATOR, &ChatHandler::HandleModifyKnownTitlesCommand, "", NULL },
- { "mount", SEC_MODERATOR, &ChatHandler::HandleModifyMountCommand, "", NULL },
- { "honor", SEC_MODERATOR, &ChatHandler::HandleModifyHonorCommand, "", NULL },
- { "rep", SEC_MODERATOR, &ChatHandler::HandleModifyRepCommand, "", NULL },
- { "arena", SEC_MODERATOR, &ChatHandler::HandleModifyArenaCommand, "", NULL },
- { "drunk", SEC_MODERATOR, &ChatHandler::HandleDrunkCommand, "", NULL },
- { "standstate", SEC_GAMEMASTER, &ChatHandler::HandleStandStateCommand, "", NULL },
- { "morph", SEC_GAMEMASTER, &ChatHandler::HandleMorphCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand wpCommandTable[] =
- {
- { "show", SEC_GAMEMASTER, &ChatHandler::HandleWpShowCommand, "", NULL },
- { "add", SEC_GAMEMASTER, &ChatHandler::HandleWpAddCommand, "", NULL },
- { "modify", SEC_GAMEMASTER, &ChatHandler::HandleWpModifyCommand, "", NULL },
- { "export", SEC_ADMINISTRATOR, &ChatHandler::HandleWpExportCommand, "", NULL },
- { "import", SEC_ADMINISTRATOR, &ChatHandler::HandleWpImportCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand debugCommandTable[] =
- {
- { "inarc", SEC_ADMINISTRATOR, &ChatHandler::HandleDebugInArcCommand, "", NULL },
- { "spellfail", SEC_ADMINISTRATOR, &ChatHandler::HandleDebugSpellFailCommand, "", NULL },
- { "setpoi", SEC_ADMINISTRATOR, &ChatHandler::HandleSetPoiCommand, "", NULL },
- { "qpartymsg", SEC_ADMINISTRATOR, &ChatHandler::HandleSendQuestPartyMsgCommand, "", NULL },
- { "qinvalidmsg", SEC_ADMINISTRATOR, &ChatHandler::HandleSendQuestInvalidMsgCommand, "", NULL },
- { "equiperr", SEC_ADMINISTRATOR, &ChatHandler::HandleEquipErrorCommand, "", NULL },
- { "sellerr", SEC_ADMINISTRATOR, &ChatHandler::HandleSellErrorCommand, "", NULL },
- { "buyerr", SEC_ADMINISTRATOR, &ChatHandler::HandleBuyErrorCommand, "", NULL },
- { "sendopcode", SEC_ADMINISTRATOR, &ChatHandler::HandleSendOpcodeCommand, "", NULL },
- { "uws", SEC_ADMINISTRATOR, &ChatHandler::HandleUpdateWorldStateCommand, "", NULL },
- { "ps", SEC_ADMINISTRATOR, &ChatHandler::HandlePlaySound2Command, "", NULL },
- { "scn", SEC_ADMINISTRATOR, &ChatHandler::HandleSendChannelNotifyCommand, "", NULL },
- { "scm", SEC_ADMINISTRATOR, &ChatHandler::HandleSendChatMsgCommand, "", NULL },
- { "getitemstate", SEC_ADMINISTRATOR, &ChatHandler::HandleGetItemState, "", NULL },
- { "playsound", SEC_MODERATOR, &ChatHandler::HandlePlaySoundCommand, "", NULL },
- { "update", SEC_ADMINISTRATOR, &ChatHandler::HandleUpdate, "", NULL },
- { "setvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleSetValue, "", NULL },
- { "getvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleGetValue, "", NULL },
- { "Mod32Value", SEC_ADMINISTRATOR, &ChatHandler::HandleMod32Value, "", NULL },
- { "anim", SEC_GAMEMASTER, &ChatHandler::HandleAnimCommand, "", NULL },
- { "lootrecipient", SEC_GAMEMASTER, &ChatHandler::HandleGetLootRecipient, "", NULL },
- { "arena", SEC_ADMINISTRATOR, &ChatHandler::HandleDebugArenaCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand eventCommandTable[] =
- {
- { "activelist", SEC_GAMEMASTER, &ChatHandler::HandleEventActiveListCommand, "", NULL },
- { "start", SEC_GAMEMASTER, &ChatHandler::HandleEventStartCommand, "", NULL },
- { "stop", SEC_GAMEMASTER, &ChatHandler::HandleEventStopCommand, "", NULL },
- { "", SEC_GAMEMASTER, &ChatHandler::HandleEventInfoCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand learnCommandTable[] =
- {
- { "all", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllCommand, "", NULL },
- { "all_gm", SEC_GAMEMASTER, &ChatHandler::HandleLearnAllGMCommand, "", NULL },
- { "all_crafts", SEC_GAMEMASTER, &ChatHandler::HandleLearnAllCraftsCommand, "", NULL },
- { "all_default", SEC_MODERATOR, &ChatHandler::HandleLearnAllDefaultCommand, "", NULL },
- { "all_lang", SEC_MODERATOR, &ChatHandler::HandleLearnAllLangCommand, "", NULL },
- { "all_myclass", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllMyClassCommand, "", NULL },
- { "all_myspells", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllMySpellsCommand, "", NULL },
- { "all_mytalents", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllMyTalentsCommand, "", NULL },
- { "all_recipes", SEC_GAMEMASTER, &ChatHandler::HandleLearnAllRecipesCommand, "", NULL },
- { "", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand reloadCommandTable[] =
- {
- { "all", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllCommand, "", NULL },
- { "all_loot", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllLootCommand, "", NULL },
- { "all_npc", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllNpcCommand, "", NULL },
- { "all_quest", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllQuestCommand, "", NULL },
- { "all_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllScriptsCommand, "", NULL },
- { "all_spell", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllSpellCommand, "", NULL },
- { "all_item", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllItemCommand, "", NULL },
-
- { "config", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadConfigCommand, "", NULL },
-
- { "areatrigger_tavern", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAreaTriggerTavernCommand, "", NULL },
- { "areatrigger_teleport", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAreaTriggerTeleportCommand, "", NULL },
- { "areatrigger_involvedrelation",SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestAreaTriggersCommand, "", NULL },
- { "event_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadEventScriptsCommand, "", NULL },
- { "command", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCommandCommand, "", NULL },
- { "creature_involvedrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCreatureQuestInvRelationsCommand,"",NULL },
- { "creature_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesCreatureCommand, "", NULL },
- { "creature_questrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCreatureQuestRelationsCommand, "", NULL },
- { "disenchant_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesDisenchantCommand, "", NULL },
- { "fishing_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesFishingCommand, "", NULL },
- { "game_graveyard_zone", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGameGraveyardZoneCommand, "", NULL },
- { "game_tele", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGameTeleCommand, "", NULL },
- { "gameobject_involvedrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGOQuestInvRelationsCommand, "", NULL },
- { "gameobject_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesGameobjectCommand, "", NULL },
- { "gameobject_questrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGOQuestRelationsCommand, "", NULL },
- { "gameobject_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGameObjectScriptsCommand, "", NULL },
- { "item_enchantment_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadItemEnchantementsCommand, "", NULL },
- { "item_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesItemCommand, "", NULL },
- { "mangos_string", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadMangosStringCommand, "", NULL },
- { "npc_gossip", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadNpcGossipCommand, "", NULL },
- { "npc_trainer", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadNpcTrainerCommand, "", NULL },
- { "npc_vendor", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadNpcVendorCommand, "", NULL },
- { "page_text", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadPageTextsCommand, "", NULL },
- { "pickpocketing_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesPickpocketingCommand,"",NULL},
- { "prospecting_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesProspectingCommand,"", NULL },
- { "quest_mail_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesQuestMailCommand, "", NULL },
- { "quest_end_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestEndScriptsCommand, "", NULL },
- { "quest_start_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestStartScriptsCommand, "", NULL },
- { "quest_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestTemplateCommand, "", NULL },
- { "reference_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesReferenceCommand, "", NULL },
- { "reserved_name", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadReservedNameCommand, "", NULL },
- { "skill_discovery_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSkillDiscoveryTemplateCommand, "", NULL },
- { "skill_extra_item_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSkillExtraItemTemplateCommand, "", NULL },
- { "skill_fishing_base_level", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSkillFishingBaseLevelCommand, "", NULL },
- { "skinning_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesSkinningCommand, "", NULL },
- { "spell_affect", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellAffectCommand, "", NULL },
- { "spell_chain", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellChainCommand, "", NULL },
- { "spell_elixir", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellElixirCommand, "", NULL },
- { "spell_learn_spell", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellLearnSpellCommand, "", NULL },
- { "spell_pet_auras", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellPetAurasCommand, "", NULL },
- { "spell_proc_event", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellProcEventCommand, "", NULL },
- { "spell_script_target", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellScriptTargetCommand, "", NULL },
- { "spell_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellScriptsCommand, "", NULL },
- { "spell_target_position", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellTargetPositionCommand, "", NULL },
- { "spell_threats", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellThreatsCommand, "", NULL },
- { "", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand honorCommandTable[] =
- {
- { "add", SEC_GAMEMASTER, &ChatHandler::HandleAddHonorCommand, "", NULL },
- { "addkill", SEC_GAMEMASTER, &ChatHandler::HandleHonorAddKillCommand, "", NULL },
- { "update", SEC_GAMEMASTER, &ChatHandler::HandleUpdateHonorFieldsCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand guildCommandTable[] =
- {
- { "create", SEC_GAMEMASTER, &ChatHandler::HandleGuildCreateCommand, "", NULL },
- { "delete", SEC_GAMEMASTER, &ChatHandler::HandleGuildDeleteCommand, "", NULL },
- { "invite", SEC_GAMEMASTER, &ChatHandler::HandleGuildInviteCommand, "", NULL },
- { "uninvite", SEC_GAMEMASTER, &ChatHandler::HandleGuildUninviteCommand, "", NULL },
- { "rank", SEC_GAMEMASTER, &ChatHandler::HandleGuildRankCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand lookupPlayerCommandTable[] =
- {
- { "ip", SEC_GAMEMASTER, &ChatHandler::HandleLookupPlayerIpCommand, "", NULL },
- { "account", SEC_GAMEMASTER, &ChatHandler::HandleLookupPlayerAccountCommand, "", NULL },
- { "email", SEC_GAMEMASTER, &ChatHandler::HandleLookupPlayerEmailCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand lookupCommandTable[] =
- {
- { "area", SEC_MODERATOR, &ChatHandler::HandleLookupAreaCommand, "", NULL },
- { "creature", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupCreatureCommand, "", NULL },
- { "event", SEC_GAMEMASTER, &ChatHandler::HandleLookupEventCommand, "", NULL },
- { "faction", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupFactionCommand, "", NULL },
- { "item", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupItemCommand, "", NULL },
- { "itemset", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupItemSetCommand, "", NULL },
- { "object", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupObjectCommand, "", NULL },
- { "quest", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupQuestCommand, "", NULL },
- { "player", SEC_GAMEMASTER, NULL, "", lookupPlayerCommandTable },
- { "skill", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupSkillCommand, "", NULL },
- { "spell", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupSpellCommand, "", NULL },
- { "tele", SEC_MODERATOR, &ChatHandler::HandleLookupTeleCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand resetCommandTable[] =
- {
- { "honor", SEC_ADMINISTRATOR, &ChatHandler::HandleResetHonorCommand, "", NULL },
- { "level", SEC_ADMINISTRATOR, &ChatHandler::HandleResetLevelCommand, "", NULL },
- { "spells", SEC_ADMINISTRATOR, &ChatHandler::HandleResetSpellsCommand, "", NULL },
- { "stats", SEC_ADMINISTRATOR, &ChatHandler::HandleResetStatsCommand, "", NULL },
- { "talents", SEC_ADMINISTRATOR, &ChatHandler::HandleResetTalentsCommand, "", NULL },
- { "all", SEC_ADMINISTRATOR, &ChatHandler::HandleResetAllCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand castCommandTable[] =
- {
- { "back", SEC_ADMINISTRATOR, &ChatHandler::HandleCastBackCommand, "", NULL },
- { "dist", SEC_ADMINISTRATOR, &ChatHandler::HandleCastDistCommand, "", NULL },
- { "self", SEC_ADMINISTRATOR, &ChatHandler::HandleCastSelfCommand, "", NULL },
- { "target", SEC_ADMINISTRATOR, &ChatHandler::HandleCastTargetCommand, "", NULL },
- { "", SEC_ADMINISTRATOR, &ChatHandler::HandleCastCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand pdumpCommandTable[] =
- {
- { "load", SEC_ADMINISTRATOR, &ChatHandler::HandleLoadPDumpCommand, "", NULL },
- { "write", SEC_ADMINISTRATOR, &ChatHandler::HandleWritePDumpCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand listCommandTable[] =
- {
- { "creature", SEC_ADMINISTRATOR, &ChatHandler::HandleListCreatureCommand, "", NULL },
- { "item", SEC_ADMINISTRATOR, &ChatHandler::HandleListItemCommand, "", NULL },
- { "object", SEC_ADMINISTRATOR, &ChatHandler::HandleListObjectCommand, "", NULL },
- { "auras", SEC_ADMINISTRATOR, &ChatHandler::HandleListAurasCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand teleCommandTable[] =
- {
- { "add", SEC_ADMINISTRATOR, &ChatHandler::HandleAddTeleCommand, "", NULL },
- { "del", SEC_ADMINISTRATOR, &ChatHandler::HandleDelTeleCommand, "", NULL },
- { "name", SEC_MODERATOR, &ChatHandler::HandleNameTeleCommand, "", NULL },
- { "group", SEC_MODERATOR, &ChatHandler::HandleGroupTeleCommand, "", NULL },
- { "", SEC_MODERATOR, &ChatHandler::HandleTeleCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand npcCommandTable[] =
- {
- { "say", SEC_MODERATOR, &ChatHandler::HandleSayCommand, "", NULL },
- { "whisper", SEC_MODERATOR, &ChatHandler::HandleNpcWhisperCommand, "", NULL },
- { "yell", SEC_MODERATOR, &ChatHandler::HandleYellCommand, "", NULL },
- { "textemote", SEC_MODERATOR, &ChatHandler::HandleTextEmoteCommand, "", NULL },
- { "add", SEC_GAMEMASTER, &ChatHandler::HandleAddSpwCommand, "", NULL },
- { "delete", SEC_GAMEMASTER, &ChatHandler::HandleDelCreatureCommand, "", NULL },
- { "spawndist", SEC_GAMEMASTER, &ChatHandler::HandleSpawnDistCommand, "", NULL },
- { "spawntime", SEC_GAMEMASTER, &ChatHandler::HandleSpawnTimeCommand, "", NULL },
- { "factionid", SEC_GAMEMASTER, &ChatHandler::HandleFactionIdCommand, "", NULL },
- { "addmove", SEC_GAMEMASTER, &ChatHandler::HandleAddMoveCommand, "", NULL },
- { "setmovetype", SEC_GAMEMASTER, &ChatHandler::HandleSetMoveTypeCommand, "", NULL },
- { "move", SEC_GAMEMASTER, &ChatHandler::HandleMoveCreatureCommand, "", NULL },
- { "changelevel", SEC_GAMEMASTER, &ChatHandler::HandleChangeLevelCommand, "", NULL },
- { "setmodel", SEC_GAMEMASTER, &ChatHandler::HandleSetModelCommand, "", NULL },
- { "additem", SEC_GAMEMASTER, &ChatHandler::HandleAddVendorItemCommand, "", NULL },
- { "delitem", SEC_GAMEMASTER, &ChatHandler::HandleDelVendorItemCommand, "", NULL },
- { "flag", SEC_GAMEMASTER, &ChatHandler::HandleNPCFlagCommand, "", NULL },
- { "changeentry", SEC_ADMINISTRATOR, &ChatHandler::HandleChangeEntryCommand, "", NULL },
- { "info", SEC_ADMINISTRATOR, &ChatHandler::HandleNpcInfoCommand, "", NULL },
- { "playemote", SEC_ADMINISTRATOR, &ChatHandler::HandlePlayEmoteCommand, "", NULL },
-
- //{ TODO: fix or remove this commands
- { "name", SEC_GAMEMASTER, &ChatHandler::HandleNameCommand, "", NULL },
- { "subname", SEC_GAMEMASTER, &ChatHandler::HandleSubNameCommand, "", NULL },
- { "addweapon", SEC_ADMINISTRATOR, &ChatHandler::HandleAddWeaponCommand, "", NULL },
- //}
-
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand goCommandTable[] =
- {
- { "grid", SEC_MODERATOR, &ChatHandler::HandleGoGridCommand, "", NULL },
- { "creature", SEC_GAMEMASTER, &ChatHandler::HandleGoCreatureCommand, "", NULL },
- { "object", SEC_GAMEMASTER, &ChatHandler::HandleGoObjectCommand, "", NULL },
- { "trigger", SEC_GAMEMASTER, &ChatHandler::HandleGoTriggerCommand, "", NULL },
- { "graveyard", SEC_GAMEMASTER, &ChatHandler::HandleGoGraveyardCommand, "", NULL },
- { "zonexy", SEC_MODERATOR, &ChatHandler::HandleGoZoneXYCommand, "", NULL },
- { "xy", SEC_MODERATOR, &ChatHandler::HandleGoXYCommand, "", NULL },
- { "xyz", SEC_MODERATOR, &ChatHandler::HandleGoXYZCommand, "", NULL },
- { "", SEC_MODERATOR, &ChatHandler::HandleGoXYZCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand gobjectCommandTable[] =
- {
- { "add", SEC_GAMEMASTER, &ChatHandler::HandleGameObjectCommand, "", NULL },
- { "delete", SEC_GAMEMASTER, &ChatHandler::HandleDelObjectCommand, "", NULL },
- { "target", SEC_GAMEMASTER, &ChatHandler::HandleTargetObjectCommand, "", NULL },
- { "turn", SEC_GAMEMASTER, &ChatHandler::HandleTurnObjectCommand, "", NULL },
- { "move", SEC_GAMEMASTER, &ChatHandler::HandleMoveObjectCommand, "", NULL },
- { "near", SEC_ADMINISTRATOR, &ChatHandler::HandleNearObjectCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand questCommandTable[] =
- {
- { "add", SEC_ADMINISTRATOR, &ChatHandler::HandleAddQuest, "", NULL },
- { "complete", SEC_ADMINISTRATOR, &ChatHandler::HandleCompleteQuest, "", NULL },
- { "remove", SEC_ADMINISTRATOR, &ChatHandler::HandleRemoveQuest, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- 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 },
- { "", SEC_MODERATOR, &ChatHandler::HandleGMmodeCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand instanceCommandTable[] =
- {
- { "listbinds", SEC_MODERATOR, &ChatHandler::HandleInstanceListBindsCommand, "", NULL },
- { "unbind", SEC_MODERATOR, &ChatHandler::HandleInstanceUnbindCommand, "", NULL },
- { "stats", SEC_MODERATOR, &ChatHandler::HandleInstanceStatsCommand, "", NULL },
- { "savedata", SEC_MODERATOR, &ChatHandler::HandleInstanceSaveDataCommand, "", NULL },
- { NULL, 0, NULL, "", NULL }
- };
-
- static ChatCommand commandTable[] =
- {
- { "gm", SEC_MODERATOR, NULL, "", gmCommandTable },
- { "npc", SEC_MODERATOR, NULL, "", npcCommandTable },
- { "go", SEC_MODERATOR, NULL, "", goCommandTable },
- { "learn", SEC_MODERATOR, NULL, "", learnCommandTable },
- { "modify", SEC_MODERATOR, NULL, "", modifyCommandTable },
- { "debug", SEC_MODERATOR, NULL, "", debugCommandTable },
- { "tele", SEC_MODERATOR, NULL, "", teleCommandTable },
- { "event", SEC_GAMEMASTER, NULL, "", eventCommandTable },
- { "gobject", SEC_GAMEMASTER, NULL, "", gobjectCommandTable },
- { "honor", SEC_GAMEMASTER, NULL, "", honorCommandTable },
- { "wp", SEC_GAMEMASTER, NULL, "", wpCommandTable },
- { "quest", SEC_ADMINISTRATOR, NULL, "", questCommandTable },
- { "reload", SEC_ADMINISTRATOR, NULL, "", reloadCommandTable },
- { "list", SEC_ADMINISTRATOR, NULL, "", listCommandTable },
- { "lookup", SEC_ADMINISTRATOR, NULL, "", lookupCommandTable },
- { "pdump", SEC_ADMINISTRATOR, NULL, "", pdumpCommandTable },
- { "guild", SEC_ADMINISTRATOR, NULL, "", guildCommandTable },
- { "cast", SEC_ADMINISTRATOR, NULL, "", castCommandTable },
- { "reset", SEC_ADMINISTRATOR, NULL, "", resetCommandTable },
- { "instance", SEC_ADMINISTRATOR, NULL, "", instanceCommandTable },
- { "server", SEC_ADMINISTRATOR, NULL, "", serverCommandTable },
-
- { "aura", SEC_ADMINISTRATOR, &ChatHandler::HandleAuraCommand, "", NULL },
- { "unaura", SEC_ADMINISTRATOR, &ChatHandler::HandleUnAuraCommand, "", NULL },
- { "acct", SEC_PLAYER, &ChatHandler::HandleAcctCommand, "", NULL },
- { "announce", SEC_MODERATOR, &ChatHandler::HandleAnnounceCommand, "", NULL },
- { "notify", SEC_MODERATOR, &ChatHandler::HandleNotifyCommand, "", NULL },
- { "goname", SEC_MODERATOR, &ChatHandler::HandleGonameCommand, "", NULL },
- { "namego", SEC_MODERATOR, &ChatHandler::HandleNamegoCommand, "", NULL },
- { "groupgo", SEC_MODERATOR, &ChatHandler::HandleGroupgoCommand, "", NULL },
- { "commands", SEC_PLAYER, &ChatHandler::HandleCommandsCommand, "", NULL },
- { "demorph", SEC_GAMEMASTER, &ChatHandler::HandleDeMorphCommand, "", NULL },
- { "die", SEC_ADMINISTRATOR, &ChatHandler::HandleDieCommand, "", NULL },
- { "revive", SEC_ADMINISTRATOR, &ChatHandler::HandleReviveCommand, "", NULL },
- { "dismount", SEC_PLAYER, &ChatHandler::HandleDismountCommand, "", NULL },
- { "gps", SEC_MODERATOR, &ChatHandler::HandleGPSCommand, "", NULL },
- { "guid", SEC_GAMEMASTER, &ChatHandler::HandleGUIDCommand, "", NULL },
- { "help", SEC_PLAYER, &ChatHandler::HandleHelpCommand, "", NULL },
- { "itemmove", SEC_GAMEMASTER, &ChatHandler::HandleItemMoveCommand, "", NULL },
- { "cooldown", SEC_ADMINISTRATOR, &ChatHandler::HandleCooldownCommand, "", NULL },
- { "unlearn", SEC_ADMINISTRATOR, &ChatHandler::HandleUnLearnCommand, "", NULL },
- { "distance", SEC_ADMINISTRATOR, &ChatHandler::HandleGetDistanceCommand, "", NULL },
- { "recall", SEC_MODERATOR, &ChatHandler::HandleRecallCommand, "", NULL },
- { "save", SEC_PLAYER, &ChatHandler::HandleSaveCommand, "", NULL },
- { "saveall", SEC_MODERATOR, &ChatHandler::HandleSaveAllCommand, "", NULL },
- { "kick", SEC_GAMEMASTER, &ChatHandler::HandleKickPlayerCommand, "", NULL },
- { "security", SEC_ADMINISTRATOR, &ChatHandler::HandleSecurityCommand, "", NULL },
- { "ban", SEC_ADMINISTRATOR, &ChatHandler::HandleBanCommand, "", NULL },
- { "unban", SEC_ADMINISTRATOR, &ChatHandler::HandleUnBanCommand, "", NULL },
- { "baninfo", SEC_ADMINISTRATOR, &ChatHandler::HandleBanInfoCommand, "", NULL },
- { "banlist", SEC_ADMINISTRATOR, &ChatHandler::HandleBanListCommand, "", NULL },
- { "plimit", SEC_ADMINISTRATOR, &ChatHandler::HandlePLimitCommand, "", NULL },
- { "start", SEC_PLAYER, &ChatHandler::HandleStartCommand, "", NULL },
- { "taxicheat", SEC_MODERATOR, &ChatHandler::HandleTaxiCheatCommand, "", NULL },
- { "allowmove", SEC_ADMINISTRATOR, &ChatHandler::HandleAllowMovementCommand, "", NULL },
- { "linkgrave", SEC_ADMINISTRATOR, &ChatHandler::HandleLinkGraveCommand, "", NULL },
- { "neargrave", SEC_ADMINISTRATOR, &ChatHandler::HandleNearGraveCommand, "", NULL },
- { "transport", SEC_ADMINISTRATOR, &ChatHandler::HandleSpawnTransportCommand, "", NULL },
- { "explorecheat", SEC_ADMINISTRATOR, &ChatHandler::HandleExploreCheatCommand, "", NULL },
- { "hover", SEC_ADMINISTRATOR, &ChatHandler::HandleHoverCommand, "", NULL },
- { "levelup", SEC_ADMINISTRATOR, &ChatHandler::HandleLevelUpCommand, "", NULL },
- { "showarea", SEC_ADMINISTRATOR, &ChatHandler::HandleShowAreaCommand, "", NULL },
- { "hidearea", SEC_ADMINISTRATOR, &ChatHandler::HandleHideAreaCommand, "", NULL },
- { "additem", SEC_ADMINISTRATOR, &ChatHandler::HandleAddItemCommand, "", NULL },
- { "additemset", SEC_ADMINISTRATOR, &ChatHandler::HandleAddItemSetCommand, "", NULL },
- { "bank", SEC_ADMINISTRATOR, &ChatHandler::HandleBankCommand, "", NULL },
- { "wchange", SEC_ADMINISTRATOR, &ChatHandler::HandleChangeWeather, "", NULL },
- { "ticket", SEC_GAMEMASTER, &ChatHandler::HandleTicketCommand, "", NULL },
- { "delticket", SEC_GAMEMASTER, &ChatHandler::HandleDelTicketCommand, "", NULL },
- { "maxskill", SEC_ADMINISTRATOR, &ChatHandler::HandleMaxSkillCommand, "", NULL },
- { "setskill", SEC_ADMINISTRATOR, &ChatHandler::HandleSetSkillCommand, "", NULL },
- { "whispers", SEC_MODERATOR, &ChatHandler::HandleWhispersCommand, "", NULL },
- { "pinfo", SEC_GAMEMASTER, &ChatHandler::HandlePInfoCommand, "", NULL },
- { "password", SEC_PLAYER, &ChatHandler::HandlePasswordCommand, "", NULL },
- { "lockaccount", SEC_PLAYER, &ChatHandler::HandleLockAccountCommand, "", NULL },
- { "respawn", SEC_ADMINISTRATOR, &ChatHandler::HandleRespawnCommand, "", NULL },
- { "sendmail", SEC_MODERATOR, &ChatHandler::HandleSendMailCommand, "", NULL },
- { "rename", SEC_GAMEMASTER, &ChatHandler::HandleRenameCommand, "", NULL },
- { "loadscripts", SEC_ADMINISTRATOR, &ChatHandler::HandleLoadScriptsCommand, "", NULL },
- { "mute", SEC_GAMEMASTER, &ChatHandler::HandleMuteCommand, "", NULL },
- { "unmute", SEC_GAMEMASTER, &ChatHandler::HandleUnmuteCommand, "", NULL },
- { "movegens", SEC_ADMINISTRATOR, &ChatHandler::HandleMovegensCommand, "", NULL },
- { "cometome", SEC_ADMINISTRATOR, &ChatHandler::HandleComeToMeCommand, "", NULL },
- { "damage", SEC_ADMINISTRATOR, &ChatHandler::HandleDamageCommand, "", NULL },
- { "combatstop", SEC_GAMEMASTER, &ChatHandler::HandleCombatStopCommand, "", NULL },
- { "flusharenapoints", SEC_ADMINISTRATOR, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL },
-
- { NULL, 0, NULL, "", NULL }
- };
-
- if(load_command_table)
- {
- load_command_table = false;
-
- QueryResult *result = WorldDatabase.Query("SELECT name,security,help FROM command");
- if (result)
- {
- do
- {
- Field *fields = result->Fetch();
- std::string name = fields[0].GetCppString();
- for(uint32 i = 0; commandTable[i].Name != NULL; i++)
- {
- if (name == commandTable[i].Name)
- {
- commandTable[i].SecurityLevel = (uint16)fields[1].GetUInt16();
- commandTable[i].Help = fields[2].GetCppString();
- }
- if(commandTable[i].ChildCommands != NULL)
- {
- ChatCommand *ptable = commandTable[i].ChildCommands;
- for(uint32 j = 0; ptable[j].Name != NULL; j++)
- {
- // first case for "" named subcommand
- if (ptable[j].Name[0]=='\0' && name == commandTable[i].Name ||
- name == fmtstring("%s %s", commandTable[i].Name, ptable[j].Name) )
- {
- ptable[j].SecurityLevel = (uint16)fields[1].GetUInt16();
- ptable[j].Help = fields[2].GetCppString();
- }
- }
- }
- }
- } while(result->NextRow());
- delete result;
- }
- }
-
- return commandTable;
-}
-
-const char *ChatHandler::GetMangosString(int32 entry)
-{
- return m_session->GetMangosString(entry);
-}
-
-bool ChatHandler::hasStringAbbr(const char* name, const char* part)
-{
- // non "" command
- if( *name )
- {
- // "" part from non-"" command
- if( !*part )
- return false;
-
- 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)
-{
- WorldPacket data;
-
- // need copy to prevent corruption by strtok call in LineFromMessage original string
- char* buf = strdup(str);
- char* pos = buf;
-
- while(char* line = LineFromMessage(pos))
- {
- FillSystemMessageData(&data, line);
- m_session->SendPacket(&data);
- }
-
- free(buf);
-}
-
-void ChatHandler::SendGlobalSysMessage(const char *str)
-{
- WorldPacket data;
-
- // need copy to prevent corruption by strtok call in LineFromMessage original string
- char* buf = strdup(str);
- char* pos = buf;
-
- while(char* line = LineFromMessage(pos))
- {
- FillSystemMessageData(&data, line);
- sWorld.SendGlobalMessage(&data);
- }
-
- free(buf);
-}
-
-void ChatHandler::SendSysMessage(int32 entry)
-{
- SendSysMessage(GetMangosString(entry));
-}
-
-void ChatHandler::PSendSysMessage(int32 entry, ...)
-{
- const char *format = GetMangosString(entry);
- va_list ap;
- char str [1024];
- va_start(ap, entry);
- vsnprintf(str,1024,format, ap );
- va_end(ap);
- SendSysMessage(str);
-}
-
-void ChatHandler::PSendSysMessage(const char *format, ...)
-{
- va_list ap;
- char str [1024];
- va_start(ap, format);
- vsnprintf(str,1024,format, ap );
- va_end(ap);
- SendSysMessage(str);
-}
-
-bool ChatHandler::ExecuteCommandInTable(ChatCommand *table, const char* text, std::string fullcmd)
-{
- char const* oldtext = text;
- std::string cmd = "";
-
- while (*text != ' ' && *text != '\0')
- {
- cmd += *text;
- ++text;
- }
-
- while (*text == ' ') ++text;
-
- for(uint32 i = 0; table[i].Name != NULL; i++)
- {
- if( !hasStringAbbr(table[i].Name, cmd.c_str()) )
- continue;
-
- // select subcommand from child commands list
- if(table[i].ChildCommands != NULL)
- {
- if(!ExecuteCommandInTable(table[i].ChildCommands, text, fullcmd))
- {
- if(text && text[0] != '\0')
- SendSysMessage(LANG_NO_SUBCMD);
- else
- SendSysMessage(LANG_CMD_SYNTAX);
-
- ShowHelpForCommand(table[i].ChildCommands,text);
- }
-
- return true;
- }
-
- // check security level only for simple command (without child commands)
- if(m_session->GetSecurity() < table[i].SecurityLevel)
- continue;
-
- SetSentErrorMessage(false);
- // table[i].Name == "" is special case: send original command to handler
- if((this->*(table[i].Handler))(strlen(table[i].Name)!=0 ? text : oldtext))
- {
- if(table[i].SecurityLevel > SEC_PLAYER)
- {
- Player* p = m_session->GetPlayer();
- uint64 sel_guid = p->GetSelection();
- sLog.outCommand("Command: %s [Player: %s (Account: %u) X: %f Y: %f Z: %f Map: %u Selected: %s (GUID: %u)]",
- fullcmd.c_str(),p->GetName(),m_session->GetAccountId(),p->GetPositionX(),p->GetPositionY(),p->GetPositionZ(),p->GetMapId(),
- GetLogNameForGuid(sel_guid),GUID_LOPART(sel_guid));
- }
- }
- // some commands have custom error messages. Don't send the default one in these cases.
- else if(!sentErrorMessage)
- {
- if(!table[i].Help.empty())
- SendSysMessage(table[i].Help.c_str());
- else
- SendSysMessage(LANG_CMD_SYNTAX);
- }
-
- return true;
- }
-
- return false;
-}
-
-int ChatHandler::ParseCommands(const char* text)
-{
- ASSERT(text);
- ASSERT(*text);
-
- //if(m_session->GetSecurity() == 0)
- // return 0;
-
- if(text[0] != '!' && text[0] != '.')
- return 0;
-
- // ignore single . and ! in line
- if(strlen(text) < 2)
- return 0;
-
- // ignore messages staring from many dots.
- if(text[0] == '.' && text[1] == '.' || text[0] == '!' && text[1] == '!')
- return 0;
-
- ++text;
-
- std::string fullcmd = text; // original `text` can't be used. It content destroyed in command code processing.
-
- if(!ExecuteCommandInTable(getCommandTable(), text, fullcmd))
- SendSysMessage(LANG_NO_CMD);
-
- return 1;
-}
-
-bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd)
-{
- std::string list;
- for(uint32 i = 0; table[i].Name != NULL; ++i)
- {
- if(m_session->GetSecurity() < table[i].SecurityLevel)
- continue;
-
- if( !hasStringAbbr(table[i].Name, subcmd) )
-
- (list += "\n ") += table[i].Name;
- }
-
- if(list.empty())
- return false;
-
- if(table==getCommandTable())
- {
- SendSysMessage(LANG_AVIABLE_CMD);
- PSendSysMessage("%s",list.c_str());
- }
- else
- PSendSysMessage(LANG_SUBCMDS_LIST,cmd,list.c_str());
-
- return true;
-}
-
-bool ChatHandler::ShowHelpForCommand(ChatCommand *table, const char* cmd)
-{
- if(*cmd)
- {
- for(uint32 i = 0; table[i].Name != NULL; ++i)
- {
- if(m_session->GetSecurity() < table[i].SecurityLevel)
- continue;
-
- if( !hasStringAbbr(table[i].Name, cmd) )
- continue;
-
- // have subcommand
- char const* subcmd = (*cmd) ? strtok(NULL, " ") : "";
-
- if(table[i].ChildCommands && subcmd && *subcmd)
- {
- if(ShowHelpForCommand(table[i].ChildCommands, subcmd))
- return true;
- }
-
- if(!table[i].Help.empty())
- SendSysMessage(table[i].Help.c_str());
-
- if(table[i].ChildCommands)
- if(ShowHelpForSubCommands(table[i].ChildCommands,table[i].Name,subcmd ? subcmd : ""))
- return true;
-
- return !table[i].Help.empty();
- }
- }
- else
- {
- for(uint32 i = 0; table[i].Name != NULL; ++i)
- {
- if(m_session->GetSecurity() < table[i].SecurityLevel)
- continue;
-
- if(strlen(table[i].Name))
- continue;
-
- if(!table[i].Help.empty())
- SendSysMessage(table[i].Help.c_str());
-
- if(table[i].ChildCommands)
- if(ShowHelpForSubCommands(table[i].ChildCommands,"",""))
- return true;
-
- return !table[i].Help.empty();
- }
- }
-
- return ShowHelpForSubCommands(table,"",cmd);
-}
-
-//Note: target_guid used only in CHAT_MSG_WHISPER_INFORM mode (in this case channelName ignored)
-void ChatHandler::FillMessageData( WorldPacket *data, WorldSession* session, uint8 type, uint32 language, const char *channelName, uint64 target_guid, const char *message, Unit *speaker)
-{
- uint32 messageLength = (message ? strlen(message) : 0) + 1;
-
- data->Initialize(SMSG_MESSAGECHAT, 100); // guess size
- *data << uint8(type);
- if ((type != CHAT_MSG_CHANNEL && type != CHAT_MSG_WHISPER) || language == LANG_ADDON)
- *data << uint32(language);
- else
- *data << uint32(LANG_UNIVERSAL);
-
- switch(type)
- {
- case CHAT_MSG_SAY:
- case CHAT_MSG_PARTY:
- case CHAT_MSG_RAID:
- case CHAT_MSG_GUILD:
- case CHAT_MSG_OFFICER:
- case CHAT_MSG_YELL:
- case CHAT_MSG_WHISPER:
- case CHAT_MSG_CHANNEL:
- case CHAT_MSG_RAID_LEADER:
- case CHAT_MSG_RAID_WARNING:
- case CHAT_MSG_BG_SYSTEM_NEUTRAL:
- case CHAT_MSG_BG_SYSTEM_ALLIANCE:
- case CHAT_MSG_BG_SYSTEM_HORDE:
- case CHAT_MSG_BATTLEGROUND:
- case CHAT_MSG_BATTLEGROUND_LEADER:
- target_guid = session ? session->GetPlayer()->GetGUID() : 0;
- break;
- case CHAT_MSG_MONSTER_SAY:
- case CHAT_MSG_MONSTER_PARTY:
- case CHAT_MSG_MONSTER_YELL:
- case CHAT_MSG_MONSTER_WHISPER:
- case CHAT_MSG_MONSTER_EMOTE:
- case CHAT_MSG_RAID_BOSS_WHISPER:
- case CHAT_MSG_RAID_BOSS_EMOTE:
- {
- *data << uint64(speaker->GetGUID());
- *data << uint32(0); // 2.1.0
- *data << uint32(strlen(speaker->GetName()) + 1);
- *data << speaker->GetName();
- uint64 listener_guid = 0;
- *data << uint64(listener_guid);
- if(listener_guid && !IS_PLAYER_GUID(listener_guid))
- {
- *data << uint32(1); // string listener_name_length
- *data << uint8(0); // string listener_name
- }
- *data << uint32(messageLength);
- *data << message;
- *data << uint8(0);
- return;
- }
- default:
- if (type != CHAT_MSG_REPLY && type != CHAT_MSG_IGNORED && type != CHAT_MSG_DND && type != CHAT_MSG_AFK)
- target_guid = 0; // only for CHAT_MSG_WHISPER_INFORM used original value target_guid
- break;
- }
-
- *data << uint64(target_guid); // there 0 for BG messages
- *data << uint32(0); // can be chat msg group or something
-
- if (type == CHAT_MSG_CHANNEL)
- {
- ASSERT(channelName);
- *data << channelName;
- }
-
- *data << uint64(target_guid);
- *data << uint32(messageLength);
- *data << message;
- if(session != 0 && type != CHAT_MSG_REPLY && type != CHAT_MSG_DND && type != CHAT_MSG_AFK)
- *data << uint8(session->GetPlayer()->chatTag());
- else
- *data << uint8(0);
-}
-
-Player * ChatHandler::getSelectedPlayer()
-{
- uint64 guid = m_session->GetPlayer()->GetSelection();
-
- if (guid == 0)
- return m_session->GetPlayer();
-
- return objmgr.GetPlayer(guid);
-}
-
-Unit* ChatHandler::getSelectedUnit()
-{
- uint64 guid = m_session->GetPlayer()->GetSelection();
-
- if (guid == 0)
- return m_session->GetPlayer();
-
- return ObjectAccessor::GetUnit(*m_session->GetPlayer(),guid);
-}
-
-Creature* ChatHandler::getSelectedCreature()
-{
- return ObjectAccessor::GetCreatureOrPet(*m_session->GetPlayer(),m_session->GetPlayer()->GetSelection());
-}
-
-char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** something1)
-{
- // skip empty
- if(!text)
- return NULL;
-
- // skip speces
- while(*text==' '||*text=='\t'||*text=='\b')
- ++text;
-
- if(!*text)
- return NULL;
-
- // return non link case
- if(text[0]!='|')
- return strtok(text, " ");
-
- // [name] Shift-click form |color|linkType:key|h[name]|h|r
- // or
- // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r
-
- char* check = strtok(text, "|"); // skip color
- if(!check)
- return NULL; // end of data
-
- char* cLinkType = strtok(NULL, ":"); // linktype
- if(!cLinkType)
- return NULL; // end of data
-
- if(strcmp(cLinkType,linkType) != 0)
- {
- strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after retturn from function
- SendSysMessage(LANG_WRONG_LINK_TYPE);
- return NULL;
- }
-
- char* cKeys = strtok(NULL, "|"); // extract keys and values
- char* cKeysTail = strtok(NULL, "");
-
- char* cKey = strtok(cKeys, ":|"); // extract key
- if(something1)
- *something1 = strtok(NULL, ":|"); // extract something
-
- strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces
- strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after retturn from function
- return cKey;
-}
-
-char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1)
-{
- // skip empty
- if(!text)
- return NULL;
-
- // skip speces
- while(*text==' '||*text=='\t'||*text=='\b')
- ++text;
-
- if(!*text)
- return NULL;
-
- // return non link case
- if(text[0]!='|')
- return strtok(text, " ");
-
- // [name] Shift-click form |color|linkType:key|h[name]|h|r
- // or
- // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r
-
- char* check = strtok(text, "|"); // skip color
- if(!check)
- return NULL; // end of data
-
- char* cLinkType = strtok(NULL, ":"); // linktype
- if(!cLinkType)
- return NULL; // end of data
-
- for(int i = 0; linkTypes[i]; ++i)
- {
- if(strcmp(cLinkType,linkTypes[i]) == 0)
- {
- char* cKeys = strtok(NULL, "|"); // extract keys and values
- char* cKeysTail = strtok(NULL, "");
-
- char* cKey = strtok(cKeys, ":|"); // extract key
- if(something1)
- *something1 = strtok(NULL, ":|"); // extract something
-
- strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces
- strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function
- if(found_idx)
- *found_idx = i;
- return cKey;
- }
- }
-
- strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function
- SendSysMessage(LANG_WRONG_LINK_TYPE);
- return NULL;
-}
-
-char const *fmtstring( char const *format, ... )
-{
- va_list argptr;
- #define MAX_FMT_STRING 32000
- static char temp_buffer[MAX_FMT_STRING];
- static char string[MAX_FMT_STRING];
- static int index = 0;
- char *buf;
- int len;
-
- va_start(argptr, format);
- vsnprintf(temp_buffer,MAX_FMT_STRING, format, argptr);
- va_end(argptr);
-
- len = strlen(temp_buffer);
-
- if( len >= MAX_FMT_STRING )
- return "ERROR";
-
- if (len + index >= MAX_FMT_STRING-1)
- {
- index = 0;
- }
-
- buf = &string[index];
- memcpy( buf, temp_buffer, len+1 );
-
- index += len + 1;
-
- return buf;
-}
-
-GameObject* ChatHandler::GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry)
-{
- Player* pl = m_session->GetPlayer();
-
- GameObject* obj = ObjectAccessor::GetGameObject(*pl, MAKE_NEW_GUID(lowguid, entry, HIGHGUID_GAMEOBJECT));
-
- if(!obj && objmgr.GetGOData(lowguid)) // guid is DB guid of object
- {
- // search near player then
- CellPair p(MaNGOS::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
-
- MaNGOS::GameObjectWithDbGUIDCheck go_check(*pl,lowguid);
- MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(obj,go_check);
-
- TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(pl->GetMapId(), pl));
- }
-
- return obj;
-}
-
-static char const* const spellTalentKeys[] = {
- "Hspell",
- "Htalent",
- 0
-};
-
-uint32 ChatHandler::extractSpellIdFromLink(char* text)
-{
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
- // number or [name] Shift-click form |color|Htalent:telen_id,rank|h[name]|h|r
- int type = 0;
- char* rankS = NULL;
- char* idS = extractKeyFromLink(text,spellTalentKeys,&type,&rankS);
- if(!idS)
- return 0;
-
- uint32 id = (uint32)atol(idS);
-
- // spell
- if(type==0)
- return id;
-
- // talent
- TalentEntry const* talentEntry = sTalentStore.LookupEntry(id);
- if(!talentEntry)
- return 0;
-
- int32 rank = rankS ? (uint32)atol(rankS) : 0;
- if(rank >= 5)
- return 0;
-
- if(rank < 0)
- rank = 0;
-
- return talentEntry->RankID[rank];
-}
-
-GameTele const* ChatHandler::extractGameTeleFromLink(char* text)
-{
- // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
- char* cId = extractKeyFromLink(text,"Htele");
- if(!cId)
- return false;
-
- // id case (explicit or from shift link)
- if(cId[0] >= '0' || cId[0] >= '9')
- if(uint32 id = atoi(cId))
- return objmgr.GetGameTele(id);
-
- return objmgr.GetGameTele(cId);
-}
+/*
+ * 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 "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "UpdateMask.h"
+#include "Chat.h"
+#include "MapManager.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+
+bool ChatHandler::load_command_table = true;
+
+ChatCommand * ChatHandler::getCommandTable()
+{
+ static ChatCommand serverCommandTable[] =
+ {
+ { "idlerestart", SEC_ADMINISTRATOR, &ChatHandler::HandleIdleRestartCommand, "", NULL },
+ { "idleshutdown", SEC_ADMINISTRATOR, &ChatHandler::HandleIdleShutDownCommand, "", NULL },
+ { "info", SEC_PLAYER, &ChatHandler::HandleInfoCommand, "", NULL },
+ { "restart", SEC_ADMINISTRATOR, &ChatHandler::HandleRestartCommand, "", NULL },
+ { "shutdown", SEC_ADMINISTRATOR, &ChatHandler::HandleShutDownCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand modifyCommandTable[] =
+ {
+ { "hp", SEC_MODERATOR, &ChatHandler::HandleModifyHPCommand, "", NULL },
+ { "mana", SEC_MODERATOR, &ChatHandler::HandleModifyManaCommand, "", NULL },
+ { "rage", SEC_MODERATOR, &ChatHandler::HandleModifyRageCommand, "", NULL },
+ { "energy", SEC_MODERATOR, &ChatHandler::HandleModifyEnergyCommand, "", NULL },
+ { "money", SEC_MODERATOR, &ChatHandler::HandleModifyMoneyCommand, "", NULL },
+ { "speed", SEC_MODERATOR, &ChatHandler::HandleModifySpeedCommand, "", NULL },
+ { "swim", SEC_MODERATOR, &ChatHandler::HandleModifySwimCommand, "", NULL },
+ { "scale", SEC_MODERATOR, &ChatHandler::HandleModifyScaleCommand, "", NULL },
+ { "bit", SEC_MODERATOR, &ChatHandler::HandleModifyBitCommand, "", NULL },
+ { "bwalk", SEC_MODERATOR, &ChatHandler::HandleModifyBWalkCommand, "", NULL },
+ { "fly", SEC_MODERATOR, &ChatHandler::HandleModifyFlyCommand, "", NULL },
+ { "aspeed", SEC_MODERATOR, &ChatHandler::HandleModifyASpeedCommand, "", NULL },
+ { "faction", SEC_MODERATOR, &ChatHandler::HandleModifyFactionCommand, "", NULL },
+ { "spell", SEC_MODERATOR, &ChatHandler::HandleModifySpellCommand, "", NULL },
+ { "tp", SEC_MODERATOR, &ChatHandler::HandleModifyTalentCommand, "", NULL },
+ { "titles", SEC_MODERATOR, &ChatHandler::HandleModifyKnownTitlesCommand, "", NULL },
+ { "mount", SEC_MODERATOR, &ChatHandler::HandleModifyMountCommand, "", NULL },
+ { "honor", SEC_MODERATOR, &ChatHandler::HandleModifyHonorCommand, "", NULL },
+ { "rep", SEC_MODERATOR, &ChatHandler::HandleModifyRepCommand, "", NULL },
+ { "arena", SEC_MODERATOR, &ChatHandler::HandleModifyArenaCommand, "", NULL },
+ { "drunk", SEC_MODERATOR, &ChatHandler::HandleDrunkCommand, "", NULL },
+ { "standstate", SEC_GAMEMASTER, &ChatHandler::HandleStandStateCommand, "", NULL },
+ { "morph", SEC_GAMEMASTER, &ChatHandler::HandleMorphCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand wpCommandTable[] =
+ {
+ { "show", SEC_GAMEMASTER, &ChatHandler::HandleWpShowCommand, "", NULL },
+ { "add", SEC_GAMEMASTER, &ChatHandler::HandleWpAddCommand, "", NULL },
+ { "modify", SEC_GAMEMASTER, &ChatHandler::HandleWpModifyCommand, "", NULL },
+ { "export", SEC_ADMINISTRATOR, &ChatHandler::HandleWpExportCommand, "", NULL },
+ { "import", SEC_ADMINISTRATOR, &ChatHandler::HandleWpImportCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand debugCommandTable[] =
+ {
+ { "inarc", SEC_ADMINISTRATOR, &ChatHandler::HandleDebugInArcCommand, "", NULL },
+ { "spellfail", SEC_ADMINISTRATOR, &ChatHandler::HandleDebugSpellFailCommand, "", NULL },
+ { "setpoi", SEC_ADMINISTRATOR, &ChatHandler::HandleSetPoiCommand, "", NULL },
+ { "qpartymsg", SEC_ADMINISTRATOR, &ChatHandler::HandleSendQuestPartyMsgCommand, "", NULL },
+ { "qinvalidmsg", SEC_ADMINISTRATOR, &ChatHandler::HandleSendQuestInvalidMsgCommand, "", NULL },
+ { "equiperr", SEC_ADMINISTRATOR, &ChatHandler::HandleEquipErrorCommand, "", NULL },
+ { "sellerr", SEC_ADMINISTRATOR, &ChatHandler::HandleSellErrorCommand, "", NULL },
+ { "buyerr", SEC_ADMINISTRATOR, &ChatHandler::HandleBuyErrorCommand, "", NULL },
+ { "sendopcode", SEC_ADMINISTRATOR, &ChatHandler::HandleSendOpcodeCommand, "", NULL },
+ { "uws", SEC_ADMINISTRATOR, &ChatHandler::HandleUpdateWorldStateCommand, "", NULL },
+ { "ps", SEC_ADMINISTRATOR, &ChatHandler::HandlePlaySound2Command, "", NULL },
+ { "scn", SEC_ADMINISTRATOR, &ChatHandler::HandleSendChannelNotifyCommand, "", NULL },
+ { "scm", SEC_ADMINISTRATOR, &ChatHandler::HandleSendChatMsgCommand, "", NULL },
+ { "getitemstate", SEC_ADMINISTRATOR, &ChatHandler::HandleGetItemState, "", NULL },
+ { "playsound", SEC_MODERATOR, &ChatHandler::HandlePlaySoundCommand, "", NULL },
+ { "update", SEC_ADMINISTRATOR, &ChatHandler::HandleUpdate, "", NULL },
+ { "setvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleSetValue, "", NULL },
+ { "getvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleGetValue, "", NULL },
+ { "Mod32Value", SEC_ADMINISTRATOR, &ChatHandler::HandleMod32Value, "", NULL },
+ { "anim", SEC_GAMEMASTER, &ChatHandler::HandleAnimCommand, "", NULL },
+ { "lootrecipient", SEC_GAMEMASTER, &ChatHandler::HandleGetLootRecipient, "", NULL },
+ { "arena", SEC_ADMINISTRATOR, &ChatHandler::HandleDebugArenaCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand eventCommandTable[] =
+ {
+ { "activelist", SEC_GAMEMASTER, &ChatHandler::HandleEventActiveListCommand, "", NULL },
+ { "start", SEC_GAMEMASTER, &ChatHandler::HandleEventStartCommand, "", NULL },
+ { "stop", SEC_GAMEMASTER, &ChatHandler::HandleEventStopCommand, "", NULL },
+ { "", SEC_GAMEMASTER, &ChatHandler::HandleEventInfoCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand learnCommandTable[] =
+ {
+ { "all", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllCommand, "", NULL },
+ { "all_gm", SEC_GAMEMASTER, &ChatHandler::HandleLearnAllGMCommand, "", NULL },
+ { "all_crafts", SEC_GAMEMASTER, &ChatHandler::HandleLearnAllCraftsCommand, "", NULL },
+ { "all_default", SEC_MODERATOR, &ChatHandler::HandleLearnAllDefaultCommand, "", NULL },
+ { "all_lang", SEC_MODERATOR, &ChatHandler::HandleLearnAllLangCommand, "", NULL },
+ { "all_myclass", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllMyClassCommand, "", NULL },
+ { "all_myspells", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllMySpellsCommand, "", NULL },
+ { "all_mytalents", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnAllMyTalentsCommand, "", NULL },
+ { "all_recipes", SEC_GAMEMASTER, &ChatHandler::HandleLearnAllRecipesCommand, "", NULL },
+ { "", SEC_ADMINISTRATOR, &ChatHandler::HandleLearnCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand reloadCommandTable[] =
+ {
+ { "all", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllCommand, "", NULL },
+ { "all_loot", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllLootCommand, "", NULL },
+ { "all_npc", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllNpcCommand, "", NULL },
+ { "all_quest", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllQuestCommand, "", NULL },
+ { "all_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllScriptsCommand, "", NULL },
+ { "all_spell", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllSpellCommand, "", NULL },
+ { "all_item", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllItemCommand, "", NULL },
+
+ { "config", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadConfigCommand, "", NULL },
+
+ { "areatrigger_tavern", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAreaTriggerTavernCommand, "", NULL },
+ { "areatrigger_teleport", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAreaTriggerTeleportCommand, "", NULL },
+ { "areatrigger_involvedrelation",SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestAreaTriggersCommand, "", NULL },
+ { "event_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadEventScriptsCommand, "", NULL },
+ { "command", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCommandCommand, "", NULL },
+ { "creature_involvedrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCreatureQuestInvRelationsCommand,"",NULL },
+ { "creature_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesCreatureCommand, "", NULL },
+ { "creature_questrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCreatureQuestRelationsCommand, "", NULL },
+ { "disenchant_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesDisenchantCommand, "", NULL },
+ { "fishing_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesFishingCommand, "", NULL },
+ { "game_graveyard_zone", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGameGraveyardZoneCommand, "", NULL },
+ { "game_tele", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGameTeleCommand, "", NULL },
+ { "gameobject_involvedrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGOQuestInvRelationsCommand, "", NULL },
+ { "gameobject_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesGameobjectCommand, "", NULL },
+ { "gameobject_questrelation", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGOQuestRelationsCommand, "", NULL },
+ { "gameobject_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadGameObjectScriptsCommand, "", NULL },
+ { "item_enchantment_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadItemEnchantementsCommand, "", NULL },
+ { "item_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesItemCommand, "", NULL },
+ { "mangos_string", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadMangosStringCommand, "", NULL },
+ { "npc_gossip", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadNpcGossipCommand, "", NULL },
+ { "npc_trainer", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadNpcTrainerCommand, "", NULL },
+ { "npc_vendor", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadNpcVendorCommand, "", NULL },
+ { "page_text", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadPageTextsCommand, "", NULL },
+ { "pickpocketing_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesPickpocketingCommand,"",NULL},
+ { "prospecting_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesProspectingCommand,"", NULL },
+ { "quest_mail_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesQuestMailCommand, "", NULL },
+ { "quest_end_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestEndScriptsCommand, "", NULL },
+ { "quest_start_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestStartScriptsCommand, "", NULL },
+ { "quest_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadQuestTemplateCommand, "", NULL },
+ { "reference_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesReferenceCommand, "", NULL },
+ { "reserved_name", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadReservedNameCommand, "", NULL },
+ { "skill_discovery_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSkillDiscoveryTemplateCommand, "", NULL },
+ { "skill_extra_item_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSkillExtraItemTemplateCommand, "", NULL },
+ { "skill_fishing_base_level", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSkillFishingBaseLevelCommand, "", NULL },
+ { "skinning_loot_template", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadLootTemplatesSkinningCommand, "", NULL },
+ { "spell_affect", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellAffectCommand, "", NULL },
+ { "spell_chain", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellChainCommand, "", NULL },
+ { "spell_elixir", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellElixirCommand, "", NULL },
+ { "spell_learn_spell", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellLearnSpellCommand, "", NULL },
+ { "spell_pet_auras", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellPetAurasCommand, "", NULL },
+ { "spell_proc_event", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellProcEventCommand, "", NULL },
+ { "spell_script_target", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellScriptTargetCommand, "", NULL },
+ { "spell_scripts", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellScriptsCommand, "", NULL },
+ { "spell_target_position", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellTargetPositionCommand, "", NULL },
+ { "spell_threats", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadSpellThreatsCommand, "", NULL },
+ { "", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand honorCommandTable[] =
+ {
+ { "add", SEC_GAMEMASTER, &ChatHandler::HandleAddHonorCommand, "", NULL },
+ { "addkill", SEC_GAMEMASTER, &ChatHandler::HandleHonorAddKillCommand, "", NULL },
+ { "update", SEC_GAMEMASTER, &ChatHandler::HandleUpdateHonorFieldsCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand guildCommandTable[] =
+ {
+ { "create", SEC_GAMEMASTER, &ChatHandler::HandleGuildCreateCommand, "", NULL },
+ { "delete", SEC_GAMEMASTER, &ChatHandler::HandleGuildDeleteCommand, "", NULL },
+ { "invite", SEC_GAMEMASTER, &ChatHandler::HandleGuildInviteCommand, "", NULL },
+ { "uninvite", SEC_GAMEMASTER, &ChatHandler::HandleGuildUninviteCommand, "", NULL },
+ { "rank", SEC_GAMEMASTER, &ChatHandler::HandleGuildRankCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand lookupPlayerCommandTable[] =
+ {
+ { "ip", SEC_GAMEMASTER, &ChatHandler::HandleLookupPlayerIpCommand, "", NULL },
+ { "account", SEC_GAMEMASTER, &ChatHandler::HandleLookupPlayerAccountCommand, "", NULL },
+ { "email", SEC_GAMEMASTER, &ChatHandler::HandleLookupPlayerEmailCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand lookupCommandTable[] =
+ {
+ { "area", SEC_MODERATOR, &ChatHandler::HandleLookupAreaCommand, "", NULL },
+ { "creature", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupCreatureCommand, "", NULL },
+ { "event", SEC_GAMEMASTER, &ChatHandler::HandleLookupEventCommand, "", NULL },
+ { "faction", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupFactionCommand, "", NULL },
+ { "item", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupItemCommand, "", NULL },
+ { "itemset", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupItemSetCommand, "", NULL },
+ { "object", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupObjectCommand, "", NULL },
+ { "quest", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupQuestCommand, "", NULL },
+ { "player", SEC_GAMEMASTER, NULL, "", lookupPlayerCommandTable },
+ { "skill", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupSkillCommand, "", NULL },
+ { "spell", SEC_ADMINISTRATOR, &ChatHandler::HandleLookupSpellCommand, "", NULL },
+ { "tele", SEC_MODERATOR, &ChatHandler::HandleLookupTeleCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand resetCommandTable[] =
+ {
+ { "honor", SEC_ADMINISTRATOR, &ChatHandler::HandleResetHonorCommand, "", NULL },
+ { "level", SEC_ADMINISTRATOR, &ChatHandler::HandleResetLevelCommand, "", NULL },
+ { "spells", SEC_ADMINISTRATOR, &ChatHandler::HandleResetSpellsCommand, "", NULL },
+ { "stats", SEC_ADMINISTRATOR, &ChatHandler::HandleResetStatsCommand, "", NULL },
+ { "talents", SEC_ADMINISTRATOR, &ChatHandler::HandleResetTalentsCommand, "", NULL },
+ { "all", SEC_ADMINISTRATOR, &ChatHandler::HandleResetAllCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand castCommandTable[] =
+ {
+ { "back", SEC_ADMINISTRATOR, &ChatHandler::HandleCastBackCommand, "", NULL },
+ { "dist", SEC_ADMINISTRATOR, &ChatHandler::HandleCastDistCommand, "", NULL },
+ { "self", SEC_ADMINISTRATOR, &ChatHandler::HandleCastSelfCommand, "", NULL },
+ { "target", SEC_ADMINISTRATOR, &ChatHandler::HandleCastTargetCommand, "", NULL },
+ { "", SEC_ADMINISTRATOR, &ChatHandler::HandleCastCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand pdumpCommandTable[] =
+ {
+ { "load", SEC_ADMINISTRATOR, &ChatHandler::HandleLoadPDumpCommand, "", NULL },
+ { "write", SEC_ADMINISTRATOR, &ChatHandler::HandleWritePDumpCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand listCommandTable[] =
+ {
+ { "creature", SEC_ADMINISTRATOR, &ChatHandler::HandleListCreatureCommand, "", NULL },
+ { "item", SEC_ADMINISTRATOR, &ChatHandler::HandleListItemCommand, "", NULL },
+ { "object", SEC_ADMINISTRATOR, &ChatHandler::HandleListObjectCommand, "", NULL },
+ { "auras", SEC_ADMINISTRATOR, &ChatHandler::HandleListAurasCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand teleCommandTable[] =
+ {
+ { "add", SEC_ADMINISTRATOR, &ChatHandler::HandleAddTeleCommand, "", NULL },
+ { "del", SEC_ADMINISTRATOR, &ChatHandler::HandleDelTeleCommand, "", NULL },
+ { "name", SEC_MODERATOR, &ChatHandler::HandleNameTeleCommand, "", NULL },
+ { "group", SEC_MODERATOR, &ChatHandler::HandleGroupTeleCommand, "", NULL },
+ { "", SEC_MODERATOR, &ChatHandler::HandleTeleCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand npcCommandTable[] =
+ {
+ { "say", SEC_MODERATOR, &ChatHandler::HandleSayCommand, "", NULL },
+ { "whisper", SEC_MODERATOR, &ChatHandler::HandleNpcWhisperCommand, "", NULL },
+ { "yell", SEC_MODERATOR, &ChatHandler::HandleYellCommand, "", NULL },
+ { "textemote", SEC_MODERATOR, &ChatHandler::HandleTextEmoteCommand, "", NULL },
+ { "add", SEC_GAMEMASTER, &ChatHandler::HandleAddSpwCommand, "", NULL },
+ { "delete", SEC_GAMEMASTER, &ChatHandler::HandleDelCreatureCommand, "", NULL },
+ { "spawndist", SEC_GAMEMASTER, &ChatHandler::HandleSpawnDistCommand, "", NULL },
+ { "spawntime", SEC_GAMEMASTER, &ChatHandler::HandleSpawnTimeCommand, "", NULL },
+ { "factionid", SEC_GAMEMASTER, &ChatHandler::HandleFactionIdCommand, "", NULL },
+ { "addmove", SEC_GAMEMASTER, &ChatHandler::HandleAddMoveCommand, "", NULL },
+ { "setmovetype", SEC_GAMEMASTER, &ChatHandler::HandleSetMoveTypeCommand, "", NULL },
+ { "move", SEC_GAMEMASTER, &ChatHandler::HandleMoveCreatureCommand, "", NULL },
+ { "changelevel", SEC_GAMEMASTER, &ChatHandler::HandleChangeLevelCommand, "", NULL },
+ { "setmodel", SEC_GAMEMASTER, &ChatHandler::HandleSetModelCommand, "", NULL },
+ { "additem", SEC_GAMEMASTER, &ChatHandler::HandleAddVendorItemCommand, "", NULL },
+ { "delitem", SEC_GAMEMASTER, &ChatHandler::HandleDelVendorItemCommand, "", NULL },
+ { "flag", SEC_GAMEMASTER, &ChatHandler::HandleNPCFlagCommand, "", NULL },
+ { "changeentry", SEC_ADMINISTRATOR, &ChatHandler::HandleChangeEntryCommand, "", NULL },
+ { "info", SEC_ADMINISTRATOR, &ChatHandler::HandleNpcInfoCommand, "", NULL },
+ { "playemote", SEC_ADMINISTRATOR, &ChatHandler::HandlePlayEmoteCommand, "", NULL },
+
+ //{ TODO: fix or remove this commands
+ { "name", SEC_GAMEMASTER, &ChatHandler::HandleNameCommand, "", NULL },
+ { "subname", SEC_GAMEMASTER, &ChatHandler::HandleSubNameCommand, "", NULL },
+ { "addweapon", SEC_ADMINISTRATOR, &ChatHandler::HandleAddWeaponCommand, "", NULL },
+ //}
+
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand goCommandTable[] =
+ {
+ { "grid", SEC_MODERATOR, &ChatHandler::HandleGoGridCommand, "", NULL },
+ { "creature", SEC_GAMEMASTER, &ChatHandler::HandleGoCreatureCommand, "", NULL },
+ { "object", SEC_GAMEMASTER, &ChatHandler::HandleGoObjectCommand, "", NULL },
+ { "trigger", SEC_GAMEMASTER, &ChatHandler::HandleGoTriggerCommand, "", NULL },
+ { "graveyard", SEC_GAMEMASTER, &ChatHandler::HandleGoGraveyardCommand, "", NULL },
+ { "zonexy", SEC_MODERATOR, &ChatHandler::HandleGoZoneXYCommand, "", NULL },
+ { "xy", SEC_MODERATOR, &ChatHandler::HandleGoXYCommand, "", NULL },
+ { "xyz", SEC_MODERATOR, &ChatHandler::HandleGoXYZCommand, "", NULL },
+ { "", SEC_MODERATOR, &ChatHandler::HandleGoXYZCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand gobjectCommandTable[] =
+ {
+ { "add", SEC_GAMEMASTER, &ChatHandler::HandleGameObjectCommand, "", NULL },
+ { "delete", SEC_GAMEMASTER, &ChatHandler::HandleDelObjectCommand, "", NULL },
+ { "target", SEC_GAMEMASTER, &ChatHandler::HandleTargetObjectCommand, "", NULL },
+ { "turn", SEC_GAMEMASTER, &ChatHandler::HandleTurnObjectCommand, "", NULL },
+ { "move", SEC_GAMEMASTER, &ChatHandler::HandleMoveObjectCommand, "", NULL },
+ { "near", SEC_ADMINISTRATOR, &ChatHandler::HandleNearObjectCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand questCommandTable[] =
+ {
+ { "add", SEC_ADMINISTRATOR, &ChatHandler::HandleAddQuest, "", NULL },
+ { "complete", SEC_ADMINISTRATOR, &ChatHandler::HandleCompleteQuest, "", NULL },
+ { "remove", SEC_ADMINISTRATOR, &ChatHandler::HandleRemoveQuest, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ 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 },
+ { "", SEC_MODERATOR, &ChatHandler::HandleGMmodeCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand instanceCommandTable[] =
+ {
+ { "listbinds", SEC_MODERATOR, &ChatHandler::HandleInstanceListBindsCommand, "", NULL },
+ { "unbind", SEC_MODERATOR, &ChatHandler::HandleInstanceUnbindCommand, "", NULL },
+ { "stats", SEC_MODERATOR, &ChatHandler::HandleInstanceStatsCommand, "", NULL },
+ { "savedata", SEC_MODERATOR, &ChatHandler::HandleInstanceSaveDataCommand, "", NULL },
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ static ChatCommand commandTable[] =
+ {
+ { "gm", SEC_MODERATOR, NULL, "", gmCommandTable },
+ { "npc", SEC_MODERATOR, NULL, "", npcCommandTable },
+ { "go", SEC_MODERATOR, NULL, "", goCommandTable },
+ { "learn", SEC_MODERATOR, NULL, "", learnCommandTable },
+ { "modify", SEC_MODERATOR, NULL, "", modifyCommandTable },
+ { "debug", SEC_MODERATOR, NULL, "", debugCommandTable },
+ { "tele", SEC_MODERATOR, NULL, "", teleCommandTable },
+ { "event", SEC_GAMEMASTER, NULL, "", eventCommandTable },
+ { "gobject", SEC_GAMEMASTER, NULL, "", gobjectCommandTable },
+ { "honor", SEC_GAMEMASTER, NULL, "", honorCommandTable },
+ { "wp", SEC_GAMEMASTER, NULL, "", wpCommandTable },
+ { "quest", SEC_ADMINISTRATOR, NULL, "", questCommandTable },
+ { "reload", SEC_ADMINISTRATOR, NULL, "", reloadCommandTable },
+ { "list", SEC_ADMINISTRATOR, NULL, "", listCommandTable },
+ { "lookup", SEC_ADMINISTRATOR, NULL, "", lookupCommandTable },
+ { "pdump", SEC_ADMINISTRATOR, NULL, "", pdumpCommandTable },
+ { "guild", SEC_ADMINISTRATOR, NULL, "", guildCommandTable },
+ { "cast", SEC_ADMINISTRATOR, NULL, "", castCommandTable },
+ { "reset", SEC_ADMINISTRATOR, NULL, "", resetCommandTable },
+ { "instance", SEC_ADMINISTRATOR, NULL, "", instanceCommandTable },
+ { "server", SEC_ADMINISTRATOR, NULL, "", serverCommandTable },
+
+ { "aura", SEC_ADMINISTRATOR, &ChatHandler::HandleAuraCommand, "", NULL },
+ { "unaura", SEC_ADMINISTRATOR, &ChatHandler::HandleUnAuraCommand, "", NULL },
+ { "acct", SEC_PLAYER, &ChatHandler::HandleAcctCommand, "", NULL },
+ { "announce", SEC_MODERATOR, &ChatHandler::HandleAnnounceCommand, "", NULL },
+ { "notify", SEC_MODERATOR, &ChatHandler::HandleNotifyCommand, "", NULL },
+ { "goname", SEC_MODERATOR, &ChatHandler::HandleGonameCommand, "", NULL },
+ { "namego", SEC_MODERATOR, &ChatHandler::HandleNamegoCommand, "", NULL },
+ { "groupgo", SEC_MODERATOR, &ChatHandler::HandleGroupgoCommand, "", NULL },
+ { "commands", SEC_PLAYER, &ChatHandler::HandleCommandsCommand, "", NULL },
+ { "demorph", SEC_GAMEMASTER, &ChatHandler::HandleDeMorphCommand, "", NULL },
+ { "die", SEC_ADMINISTRATOR, &ChatHandler::HandleDieCommand, "", NULL },
+ { "revive", SEC_ADMINISTRATOR, &ChatHandler::HandleReviveCommand, "", NULL },
+ { "dismount", SEC_PLAYER, &ChatHandler::HandleDismountCommand, "", NULL },
+ { "gps", SEC_MODERATOR, &ChatHandler::HandleGPSCommand, "", NULL },
+ { "guid", SEC_GAMEMASTER, &ChatHandler::HandleGUIDCommand, "", NULL },
+ { "help", SEC_PLAYER, &ChatHandler::HandleHelpCommand, "", NULL },
+ { "itemmove", SEC_GAMEMASTER, &ChatHandler::HandleItemMoveCommand, "", NULL },
+ { "cooldown", SEC_ADMINISTRATOR, &ChatHandler::HandleCooldownCommand, "", NULL },
+ { "unlearn", SEC_ADMINISTRATOR, &ChatHandler::HandleUnLearnCommand, "", NULL },
+ { "distance", SEC_ADMINISTRATOR, &ChatHandler::HandleGetDistanceCommand, "", NULL },
+ { "recall", SEC_MODERATOR, &ChatHandler::HandleRecallCommand, "", NULL },
+ { "save", SEC_PLAYER, &ChatHandler::HandleSaveCommand, "", NULL },
+ { "saveall", SEC_MODERATOR, &ChatHandler::HandleSaveAllCommand, "", NULL },
+ { "kick", SEC_GAMEMASTER, &ChatHandler::HandleKickPlayerCommand, "", NULL },
+ { "security", SEC_ADMINISTRATOR, &ChatHandler::HandleSecurityCommand, "", NULL },
+ { "ban", SEC_ADMINISTRATOR, &ChatHandler::HandleBanCommand, "", NULL },
+ { "unban", SEC_ADMINISTRATOR, &ChatHandler::HandleUnBanCommand, "", NULL },
+ { "baninfo", SEC_ADMINISTRATOR, &ChatHandler::HandleBanInfoCommand, "", NULL },
+ { "banlist", SEC_ADMINISTRATOR, &ChatHandler::HandleBanListCommand, "", NULL },
+ { "plimit", SEC_ADMINISTRATOR, &ChatHandler::HandlePLimitCommand, "", NULL },
+ { "start", SEC_PLAYER, &ChatHandler::HandleStartCommand, "", NULL },
+ { "taxicheat", SEC_MODERATOR, &ChatHandler::HandleTaxiCheatCommand, "", NULL },
+ { "allowmove", SEC_ADMINISTRATOR, &ChatHandler::HandleAllowMovementCommand, "", NULL },
+ { "linkgrave", SEC_ADMINISTRATOR, &ChatHandler::HandleLinkGraveCommand, "", NULL },
+ { "neargrave", SEC_ADMINISTRATOR, &ChatHandler::HandleNearGraveCommand, "", NULL },
+ { "transport", SEC_ADMINISTRATOR, &ChatHandler::HandleSpawnTransportCommand, "", NULL },
+ { "explorecheat", SEC_ADMINISTRATOR, &ChatHandler::HandleExploreCheatCommand, "", NULL },
+ { "hover", SEC_ADMINISTRATOR, &ChatHandler::HandleHoverCommand, "", NULL },
+ { "levelup", SEC_ADMINISTRATOR, &ChatHandler::HandleLevelUpCommand, "", NULL },
+ { "showarea", SEC_ADMINISTRATOR, &ChatHandler::HandleShowAreaCommand, "", NULL },
+ { "hidearea", SEC_ADMINISTRATOR, &ChatHandler::HandleHideAreaCommand, "", NULL },
+ { "additem", SEC_ADMINISTRATOR, &ChatHandler::HandleAddItemCommand, "", NULL },
+ { "additemset", SEC_ADMINISTRATOR, &ChatHandler::HandleAddItemSetCommand, "", NULL },
+ { "bank", SEC_ADMINISTRATOR, &ChatHandler::HandleBankCommand, "", NULL },
+ { "wchange", SEC_ADMINISTRATOR, &ChatHandler::HandleChangeWeather, "", NULL },
+ { "ticket", SEC_GAMEMASTER, &ChatHandler::HandleTicketCommand, "", NULL },
+ { "delticket", SEC_GAMEMASTER, &ChatHandler::HandleDelTicketCommand, "", NULL },
+ { "maxskill", SEC_ADMINISTRATOR, &ChatHandler::HandleMaxSkillCommand, "", NULL },
+ { "setskill", SEC_ADMINISTRATOR, &ChatHandler::HandleSetSkillCommand, "", NULL },
+ { "whispers", SEC_MODERATOR, &ChatHandler::HandleWhispersCommand, "", NULL },
+ { "pinfo", SEC_GAMEMASTER, &ChatHandler::HandlePInfoCommand, "", NULL },
+ { "password", SEC_PLAYER, &ChatHandler::HandlePasswordCommand, "", NULL },
+ { "lockaccount", SEC_PLAYER, &ChatHandler::HandleLockAccountCommand, "", NULL },
+ { "respawn", SEC_ADMINISTRATOR, &ChatHandler::HandleRespawnCommand, "", NULL },
+ { "sendmail", SEC_MODERATOR, &ChatHandler::HandleSendMailCommand, "", NULL },
+ { "rename", SEC_GAMEMASTER, &ChatHandler::HandleRenameCommand, "", NULL },
+ { "loadscripts", SEC_ADMINISTRATOR, &ChatHandler::HandleLoadScriptsCommand, "", NULL },
+ { "mute", SEC_GAMEMASTER, &ChatHandler::HandleMuteCommand, "", NULL },
+ { "unmute", SEC_GAMEMASTER, &ChatHandler::HandleUnmuteCommand, "", NULL },
+ { "movegens", SEC_ADMINISTRATOR, &ChatHandler::HandleMovegensCommand, "", NULL },
+ { "cometome", SEC_ADMINISTRATOR, &ChatHandler::HandleComeToMeCommand, "", NULL },
+ { "damage", SEC_ADMINISTRATOR, &ChatHandler::HandleDamageCommand, "", NULL },
+ { "combatstop", SEC_GAMEMASTER, &ChatHandler::HandleCombatStopCommand, "", NULL },
+ { "flusharenapoints", SEC_ADMINISTRATOR, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL },
+
+ { NULL, 0, NULL, "", NULL }
+ };
+
+ if(load_command_table)
+ {
+ load_command_table = false;
+
+ QueryResult *result = WorldDatabase.Query("SELECT name,security,help FROM command");
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ std::string name = fields[0].GetCppString();
+ for(uint32 i = 0; commandTable[i].Name != NULL; i++)
+ {
+ if (name == commandTable[i].Name)
+ {
+ commandTable[i].SecurityLevel = (uint16)fields[1].GetUInt16();
+ commandTable[i].Help = fields[2].GetCppString();
+ }
+ if(commandTable[i].ChildCommands != NULL)
+ {
+ ChatCommand *ptable = commandTable[i].ChildCommands;
+ for(uint32 j = 0; ptable[j].Name != NULL; j++)
+ {
+ // first case for "" named subcommand
+ if (ptable[j].Name[0]=='\0' && name == commandTable[i].Name ||
+ name == fmtstring("%s %s", commandTable[i].Name, ptable[j].Name) )
+ {
+ ptable[j].SecurityLevel = (uint16)fields[1].GetUInt16();
+ ptable[j].Help = fields[2].GetCppString();
+ }
+ }
+ }
+ }
+ } while(result->NextRow());
+ delete result;
+ }
+ }
+
+ return commandTable;
+}
+
+const char *ChatHandler::GetMangosString(int32 entry)
+{
+ return m_session->GetMangosString(entry);
+}
+
+bool ChatHandler::hasStringAbbr(const char* name, const char* part)
+{
+ // non "" command
+ if( *name )
+ {
+ // "" part from non-"" command
+ if( !*part )
+ return false;
+
+ 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)
+{
+ WorldPacket data;
+
+ // need copy to prevent corruption by strtok call in LineFromMessage original string
+ char* buf = strdup(str);
+ char* pos = buf;
+
+ while(char* line = LineFromMessage(pos))
+ {
+ FillSystemMessageData(&data, line);
+ m_session->SendPacket(&data);
+ }
+
+ free(buf);
+}
+
+void ChatHandler::SendGlobalSysMessage(const char *str)
+{
+ WorldPacket data;
+
+ // need copy to prevent corruption by strtok call in LineFromMessage original string
+ char* buf = strdup(str);
+ char* pos = buf;
+
+ while(char* line = LineFromMessage(pos))
+ {
+ FillSystemMessageData(&data, line);
+ sWorld.SendGlobalMessage(&data);
+ }
+
+ free(buf);
+}
+
+void ChatHandler::SendSysMessage(int32 entry)
+{
+ SendSysMessage(GetMangosString(entry));
+}
+
+void ChatHandler::PSendSysMessage(int32 entry, ...)
+{
+ const char *format = GetMangosString(entry);
+ va_list ap;
+ char str [1024];
+ va_start(ap, entry);
+ vsnprintf(str,1024,format, ap );
+ va_end(ap);
+ SendSysMessage(str);
+}
+
+void ChatHandler::PSendSysMessage(const char *format, ...)
+{
+ va_list ap;
+ char str [1024];
+ va_start(ap, format);
+ vsnprintf(str,1024,format, ap );
+ va_end(ap);
+ SendSysMessage(str);
+}
+
+bool ChatHandler::ExecuteCommandInTable(ChatCommand *table, const char* text, std::string fullcmd)
+{
+ char const* oldtext = text;
+ std::string cmd = "";
+
+ while (*text != ' ' && *text != '\0')
+ {
+ cmd += *text;
+ ++text;
+ }
+
+ while (*text == ' ') ++text;
+
+ for(uint32 i = 0; table[i].Name != NULL; i++)
+ {
+ if( !hasStringAbbr(table[i].Name, cmd.c_str()) )
+ continue;
+
+ // select subcommand from child commands list
+ if(table[i].ChildCommands != NULL)
+ {
+ if(!ExecuteCommandInTable(table[i].ChildCommands, text, fullcmd))
+ {
+ if(text && text[0] != '\0')
+ SendSysMessage(LANG_NO_SUBCMD);
+ else
+ SendSysMessage(LANG_CMD_SYNTAX);
+
+ ShowHelpForCommand(table[i].ChildCommands,text);
+ }
+
+ return true;
+ }
+
+ // check security level only for simple command (without child commands)
+ if(m_session->GetSecurity() < table[i].SecurityLevel)
+ continue;
+
+ SetSentErrorMessage(false);
+ // table[i].Name == "" is special case: send original command to handler
+ if((this->*(table[i].Handler))(strlen(table[i].Name)!=0 ? text : oldtext))
+ {
+ if(table[i].SecurityLevel > SEC_PLAYER)
+ {
+ Player* p = m_session->GetPlayer();
+ uint64 sel_guid = p->GetSelection();
+ sLog.outCommand("Command: %s [Player: %s (Account: %u) X: %f Y: %f Z: %f Map: %u Selected: %s (GUID: %u)]",
+ fullcmd.c_str(),p->GetName(),m_session->GetAccountId(),p->GetPositionX(),p->GetPositionY(),p->GetPositionZ(),p->GetMapId(),
+ GetLogNameForGuid(sel_guid),GUID_LOPART(sel_guid));
+ }
+ }
+ // some commands have custom error messages. Don't send the default one in these cases.
+ else if(!sentErrorMessage)
+ {
+ if(!table[i].Help.empty())
+ SendSysMessage(table[i].Help.c_str());
+ else
+ SendSysMessage(LANG_CMD_SYNTAX);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+int ChatHandler::ParseCommands(const char* text)
+{
+ ASSERT(text);
+ ASSERT(*text);
+
+ //if(m_session->GetSecurity() == 0)
+ // return 0;
+
+ if(text[0] != '!' && text[0] != '.')
+ return 0;
+
+ // ignore single . and ! in line
+ if(strlen(text) < 2)
+ return 0;
+
+ // ignore messages staring from many dots.
+ if(text[0] == '.' && text[1] == '.' || text[0] == '!' && text[1] == '!')
+ return 0;
+
+ ++text;
+
+ std::string fullcmd = text; // original `text` can't be used. It content destroyed in command code processing.
+
+ if(!ExecuteCommandInTable(getCommandTable(), text, fullcmd))
+ SendSysMessage(LANG_NO_CMD);
+
+ return 1;
+}
+
+bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd)
+{
+ std::string list;
+ for(uint32 i = 0; table[i].Name != NULL; ++i)
+ {
+ if(m_session->GetSecurity() < table[i].SecurityLevel)
+ continue;
+
+ if( !hasStringAbbr(table[i].Name, subcmd) )
+
+ (list += "\n ") += table[i].Name;
+ }
+
+ if(list.empty())
+ return false;
+
+ if(table==getCommandTable())
+ {
+ SendSysMessage(LANG_AVIABLE_CMD);
+ PSendSysMessage("%s",list.c_str());
+ }
+ else
+ PSendSysMessage(LANG_SUBCMDS_LIST,cmd,list.c_str());
+
+ return true;
+}
+
+bool ChatHandler::ShowHelpForCommand(ChatCommand *table, const char* cmd)
+{
+ if(*cmd)
+ {
+ for(uint32 i = 0; table[i].Name != NULL; ++i)
+ {
+ if(m_session->GetSecurity() < table[i].SecurityLevel)
+ continue;
+
+ if( !hasStringAbbr(table[i].Name, cmd) )
+ continue;
+
+ // have subcommand
+ char const* subcmd = (*cmd) ? strtok(NULL, " ") : "";
+
+ if(table[i].ChildCommands && subcmd && *subcmd)
+ {
+ if(ShowHelpForCommand(table[i].ChildCommands, subcmd))
+ return true;
+ }
+
+ if(!table[i].Help.empty())
+ SendSysMessage(table[i].Help.c_str());
+
+ if(table[i].ChildCommands)
+ if(ShowHelpForSubCommands(table[i].ChildCommands,table[i].Name,subcmd ? subcmd : ""))
+ return true;
+
+ return !table[i].Help.empty();
+ }
+ }
+ else
+ {
+ for(uint32 i = 0; table[i].Name != NULL; ++i)
+ {
+ if(m_session->GetSecurity() < table[i].SecurityLevel)
+ continue;
+
+ if(strlen(table[i].Name))
+ continue;
+
+ if(!table[i].Help.empty())
+ SendSysMessage(table[i].Help.c_str());
+
+ if(table[i].ChildCommands)
+ if(ShowHelpForSubCommands(table[i].ChildCommands,"",""))
+ return true;
+
+ return !table[i].Help.empty();
+ }
+ }
+
+ return ShowHelpForSubCommands(table,"",cmd);
+}
+
+//Note: target_guid used only in CHAT_MSG_WHISPER_INFORM mode (in this case channelName ignored)
+void ChatHandler::FillMessageData( WorldPacket *data, WorldSession* session, uint8 type, uint32 language, const char *channelName, uint64 target_guid, const char *message, Unit *speaker)
+{
+ uint32 messageLength = (message ? strlen(message) : 0) + 1;
+
+ data->Initialize(SMSG_MESSAGECHAT, 100); // guess size
+ *data << uint8(type);
+ if ((type != CHAT_MSG_CHANNEL && type != CHAT_MSG_WHISPER) || language == LANG_ADDON)
+ *data << uint32(language);
+ else
+ *data << uint32(LANG_UNIVERSAL);
+
+ switch(type)
+ {
+ case CHAT_MSG_SAY:
+ case CHAT_MSG_PARTY:
+ case CHAT_MSG_RAID:
+ case CHAT_MSG_GUILD:
+ case CHAT_MSG_OFFICER:
+ case CHAT_MSG_YELL:
+ case CHAT_MSG_WHISPER:
+ case CHAT_MSG_CHANNEL:
+ case CHAT_MSG_RAID_LEADER:
+ case CHAT_MSG_RAID_WARNING:
+ case CHAT_MSG_BG_SYSTEM_NEUTRAL:
+ case CHAT_MSG_BG_SYSTEM_ALLIANCE:
+ case CHAT_MSG_BG_SYSTEM_HORDE:
+ case CHAT_MSG_BATTLEGROUND:
+ case CHAT_MSG_BATTLEGROUND_LEADER:
+ target_guid = session ? session->GetPlayer()->GetGUID() : 0;
+ break;
+ case CHAT_MSG_MONSTER_SAY:
+ case CHAT_MSG_MONSTER_PARTY:
+ case CHAT_MSG_MONSTER_YELL:
+ case CHAT_MSG_MONSTER_WHISPER:
+ case CHAT_MSG_MONSTER_EMOTE:
+ case CHAT_MSG_RAID_BOSS_WHISPER:
+ case CHAT_MSG_RAID_BOSS_EMOTE:
+ {
+ *data << uint64(speaker->GetGUID());
+ *data << uint32(0); // 2.1.0
+ *data << uint32(strlen(speaker->GetName()) + 1);
+ *data << speaker->GetName();
+ uint64 listener_guid = 0;
+ *data << uint64(listener_guid);
+ if(listener_guid && !IS_PLAYER_GUID(listener_guid))
+ {
+ *data << uint32(1); // string listener_name_length
+ *data << uint8(0); // string listener_name
+ }
+ *data << uint32(messageLength);
+ *data << message;
+ *data << uint8(0);
+ return;
+ }
+ default:
+ if (type != CHAT_MSG_REPLY && type != CHAT_MSG_IGNORED && type != CHAT_MSG_DND && type != CHAT_MSG_AFK)
+ target_guid = 0; // only for CHAT_MSG_WHISPER_INFORM used original value target_guid
+ break;
+ }
+
+ *data << uint64(target_guid); // there 0 for BG messages
+ *data << uint32(0); // can be chat msg group or something
+
+ if (type == CHAT_MSG_CHANNEL)
+ {
+ ASSERT(channelName);
+ *data << channelName;
+ }
+
+ *data << uint64(target_guid);
+ *data << uint32(messageLength);
+ *data << message;
+ if(session != 0 && type != CHAT_MSG_REPLY && type != CHAT_MSG_DND && type != CHAT_MSG_AFK)
+ *data << uint8(session->GetPlayer()->chatTag());
+ else
+ *data << uint8(0);
+}
+
+Player * ChatHandler::getSelectedPlayer()
+{
+ uint64 guid = m_session->GetPlayer()->GetSelection();
+
+ if (guid == 0)
+ return m_session->GetPlayer();
+
+ return objmgr.GetPlayer(guid);
+}
+
+Unit* ChatHandler::getSelectedUnit()
+{
+ uint64 guid = m_session->GetPlayer()->GetSelection();
+
+ if (guid == 0)
+ return m_session->GetPlayer();
+
+ return ObjectAccessor::GetUnit(*m_session->GetPlayer(),guid);
+}
+
+Creature* ChatHandler::getSelectedCreature()
+{
+ return ObjectAccessor::GetCreatureOrPet(*m_session->GetPlayer(),m_session->GetPlayer()->GetSelection());
+}
+
+char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** something1)
+{
+ // skip empty
+ if(!text)
+ return NULL;
+
+ // skip speces
+ while(*text==' '||*text=='\t'||*text=='\b')
+ ++text;
+
+ if(!*text)
+ return NULL;
+
+ // return non link case
+ if(text[0]!='|')
+ return strtok(text, " ");
+
+ // [name] Shift-click form |color|linkType:key|h[name]|h|r
+ // or
+ // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r
+
+ char* check = strtok(text, "|"); // skip color
+ if(!check)
+ return NULL; // end of data
+
+ char* cLinkType = strtok(NULL, ":"); // linktype
+ if(!cLinkType)
+ return NULL; // end of data
+
+ if(strcmp(cLinkType,linkType) != 0)
+ {
+ strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after retturn from function
+ SendSysMessage(LANG_WRONG_LINK_TYPE);
+ return NULL;
+ }
+
+ char* cKeys = strtok(NULL, "|"); // extract keys and values
+ char* cKeysTail = strtok(NULL, "");
+
+ char* cKey = strtok(cKeys, ":|"); // extract key
+ if(something1)
+ *something1 = strtok(NULL, ":|"); // extract something
+
+ strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces
+ strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after retturn from function
+ return cKey;
+}
+
+char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1)
+{
+ // skip empty
+ if(!text)
+ return NULL;
+
+ // skip speces
+ while(*text==' '||*text=='\t'||*text=='\b')
+ ++text;
+
+ if(!*text)
+ return NULL;
+
+ // return non link case
+ if(text[0]!='|')
+ return strtok(text, " ");
+
+ // [name] Shift-click form |color|linkType:key|h[name]|h|r
+ // or
+ // [name] Shift-click form |color|linkType:key:something1:...:somethingN|h[name]|h|r
+
+ char* check = strtok(text, "|"); // skip color
+ if(!check)
+ return NULL; // end of data
+
+ char* cLinkType = strtok(NULL, ":"); // linktype
+ if(!cLinkType)
+ return NULL; // end of data
+
+ for(int i = 0; linkTypes[i]; ++i)
+ {
+ if(strcmp(cLinkType,linkTypes[i]) == 0)
+ {
+ char* cKeys = strtok(NULL, "|"); // extract keys and values
+ char* cKeysTail = strtok(NULL, "");
+
+ char* cKey = strtok(cKeys, ":|"); // extract key
+ if(something1)
+ *something1 = strtok(NULL, ":|"); // extract something
+
+ strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces
+ strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function
+ if(found_idx)
+ *found_idx = i;
+ return cKey;
+ }
+ }
+
+ strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL,s) use after return from function
+ SendSysMessage(LANG_WRONG_LINK_TYPE);
+ return NULL;
+}
+
+char const *fmtstring( char const *format, ... )
+{
+ va_list argptr;
+ #define MAX_FMT_STRING 32000
+ static char temp_buffer[MAX_FMT_STRING];
+ static char string[MAX_FMT_STRING];
+ static int index = 0;
+ char *buf;
+ int len;
+
+ va_start(argptr, format);
+ vsnprintf(temp_buffer,MAX_FMT_STRING, format, argptr);
+ va_end(argptr);
+
+ len = strlen(temp_buffer);
+
+ if( len >= MAX_FMT_STRING )
+ return "ERROR";
+
+ if (len + index >= MAX_FMT_STRING-1)
+ {
+ index = 0;
+ }
+
+ buf = &string[index];
+ memcpy( buf, temp_buffer, len+1 );
+
+ index += len + 1;
+
+ return buf;
+}
+
+GameObject* ChatHandler::GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry)
+{
+ Player* pl = m_session->GetPlayer();
+
+ GameObject* obj = ObjectAccessor::GetGameObject(*pl, MAKE_NEW_GUID(lowguid, entry, HIGHGUID_GAMEOBJECT));
+
+ if(!obj && objmgr.GetGOData(lowguid)) // guid is DB guid of object
+ {
+ // search near player then
+ CellPair p(MaNGOS::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::GameObjectWithDbGUIDCheck go_check(*pl,lowguid);
+ MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(obj,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(pl->GetMapId(), pl));
+ }
+
+ return obj;
+}
+
+static char const* const spellTalentKeys[] = {
+ "Hspell",
+ "Htalent",
+ 0
+};
+
+uint32 ChatHandler::extractSpellIdFromLink(char* text)
+{
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
+ // number or [name] Shift-click form |color|Htalent:telen_id,rank|h[name]|h|r
+ int type = 0;
+ char* rankS = NULL;
+ char* idS = extractKeyFromLink(text,spellTalentKeys,&type,&rankS);
+ if(!idS)
+ return 0;
+
+ uint32 id = (uint32)atol(idS);
+
+ // spell
+ if(type==0)
+ return id;
+
+ // talent
+ TalentEntry const* talentEntry = sTalentStore.LookupEntry(id);
+ if(!talentEntry)
+ return 0;
+
+ int32 rank = rankS ? (uint32)atol(rankS) : 0;
+ if(rank >= 5)
+ return 0;
+
+ if(rank < 0)
+ rank = 0;
+
+ return talentEntry->RankID[rank];
+}
+
+GameTele const* ChatHandler::extractGameTeleFromLink(char* text)
+{
+ // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
+ char* cId = extractKeyFromLink(text,"Htele");
+ if(!cId)
+ return false;
+
+ // id case (explicit or from shift link)
+ if(cId[0] >= '0' || cId[0] >= '9')
+ if(uint32 id = atoi(cId))
+ return objmgr.GetGameTele(id);
+
+ return objmgr.GetGameTele(cId);
+}
diff --git a/src/game/Chat.h b/src/game/Chat.h
index 74366659f66..ff0eac2caa6 100644
--- a/src/game/Chat.h
+++ b/src/game/Chat.h
@@ -1,411 +1,411 @@
-/*
- * 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
- */
-
-#ifndef MANGOSSERVER_CHAT_H
-#define MANGOSSERVER_CHAT_H
-
-#include "SharedDefines.h"
-
-class ChatHandler;
-class WorldSession;
-class Creature;
-class Player;
-class Unit;
-struct GameTele;
-
-class ChatCommand
-{
- public:
- const char * Name;
- uint32 SecurityLevel; // function pointer required correct align (use uint32)
- bool (ChatHandler::*Handler)(const char* args);
- std::string Help;
- ChatCommand * ChildCommands;
-};
-
-class ChatHandler
-{
- public:
- explicit ChatHandler(WorldSession* session) : m_session(session) {}
- explicit ChatHandler(Player* player) : m_session(player->GetSession()) {}
- ~ChatHandler() {}
-
- static void FillMessageData( WorldPacket *data, WorldSession* session, uint8 type, uint32 language, const char *channelName, uint64 target_guid, const char *message, Unit *speaker);
-
- void FillMessageData( WorldPacket *data, uint8 type, uint32 language, uint64 target_guid, const char* message)
- {
- FillMessageData( data, m_session, type, language, NULL, target_guid, message, NULL);
- }
-
- void FillSystemMessageData( WorldPacket *data, const char* message )
- {
- FillMessageData( data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, 0, message );
- }
-
- static char* LineFromMessage(char*& pos) { char* start = strtok(pos,"\n"); pos = NULL; return start; }
-
- const char *GetMangosString(int32 entry);
-
- void SendSysMessage( const char *str);
- void SendSysMessage( int32 entry);
- void PSendSysMessage( const char *format, ...) ATTR_PRINTF(2,3);
- void PSendSysMessage( int32 entry, ... );
-
- int ParseCommands(const char* text);
-
- protected:
- bool hasStringAbbr(const char* name, const char* part);
- void SendGlobalSysMessage(const char *str);
-
- bool ExecuteCommandInTable(ChatCommand *table, const char* text, std::string fullcommand);
- bool ShowHelpForCommand(ChatCommand *table, const char* cmd);
- bool ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd);
-
- ChatCommand* getCommandTable();
-
- bool HandleHelpCommand(const char* args);
- bool HandleCommandsCommand(const char* args);
- bool HandleAcctCommand(const char* args);
- bool HandleStartCommand(const char* args);
- bool HandleInfoCommand(const char* args);
- bool HandleDismountCommand(const char* args);
- bool HandleSaveCommand(const char* args);
- bool HandleGMListCommand(const char* args);
-
- bool HandleNamegoCommand(const char* args);
- bool HandleGonameCommand(const char* args);
- bool HandleGroupgoCommand(const char* args);
- bool HandleRecallCommand(const char* args);
- 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);
- bool HandleWhispersCommand(const char* args);
- bool HandleSayCommand(const char* args);
- bool HandleNpcWhisperCommand(const char* args);
- bool HandleYellCommand(const char* args);
- bool HandlePlayEmoteCommand(const char* args);
- bool HandleSendMailCommand(const char* args);
- bool HandleNameTeleCommand(const char* args);
- bool HandleGroupTeleCommand(const char* args);
- bool HandleDrunkCommand(const char* args);
-
- bool HandleEventActiveListCommand(const char* args);
- bool HandleEventStartCommand(const char* args);
- bool HandleEventStopCommand(const char* args);
- bool HandleEventInfoCommand(const char* args);
-
- bool HandleModifyKnownTitlesCommand(const char* args);
- bool HandleModifyHPCommand(const char* args);
- bool HandleModifyManaCommand(const char* args);
- bool HandleModifyRageCommand(const char* args);
- bool HandleModifyEnergyCommand(const char* args);
- bool HandleModifyMoneyCommand(const char* args);
- bool HandleModifyASpeedCommand(const char* args);
- bool HandleModifySpeedCommand(const char* args);
- bool HandleModifyBWalkCommand(const char* args);
- bool HandleModifyFlyCommand(const char* args);
- bool HandleModifySwimCommand(const char* args);
- bool HandleModifyScaleCommand(const char* args);
- bool HandleModifyMountCommand(const char* args);
- bool HandleModifyBitCommand(const char* args);
- bool HandleModifyFactionCommand(const char* args);
- bool HandleModifySpellCommand(const char* args);
- bool HandleModifyTalentCommand (const char* args);
- bool HandleModifyHonorCommand (const char* args);
- bool HandleModifyRepCommand(const char* args);
- bool HandleModifyArenaCommand(const char* args);
-
- bool HandleReloadCommand(const char* args);
- bool HandleReloadAllCommand(const char* args);
- bool HandleReloadAllAreaCommand(const char* args);
- bool HandleReloadAllItemCommand(const char* args);
- bool HandleReloadAllLootCommand(const char* args);
- bool HandleReloadAllNpcCommand(const char* args);
- bool HandleReloadAllQuestCommand(const char* args);
- bool HandleReloadAllScriptsCommand(const char* args);
- bool HandleReloadAllSpellCommand(const char* args);
-
- bool HandleReloadConfigCommand(const char* args);
-
- bool HandleReloadAreaTriggerTavernCommand(const char* args);
- bool HandleReloadAreaTriggerTeleportCommand(const char* args);
- bool HandleReloadEventScriptsCommand(const char* args);
- bool HandleReloadCommandCommand(const char* args);
- bool HandleReloadCreatureQuestRelationsCommand(const char* args);
- bool HandleReloadCreatureQuestInvRelationsCommand(const char* args);
- bool HandleReloadGameGraveyardZoneCommand(const char* args);
- bool HandleReloadGameObjectScriptsCommand(const char* args);
- bool HandleReloadGameTeleCommand(const char* args);
- bool HandleReloadGOQuestRelationsCommand(const char* args);
- bool HandleReloadGOQuestInvRelationsCommand(const char* args);
- bool HandleReloadLootTemplatesCreatureCommand(const char* args);
- bool HandleReloadLootTemplatesDisenchantCommand(const char* args);
- bool HandleReloadLootTemplatesFishingCommand(const char* args);
- bool HandleReloadLootTemplatesGameobjectCommand(const char* args);
- bool HandleReloadLootTemplatesItemCommand(const char* args);
- bool HandleReloadLootTemplatesPickpocketingCommand(const char* args);
- bool HandleReloadLootTemplatesProspectingCommand(const char* args);
- bool HandleReloadLootTemplatesReferenceCommand(const char* args);
- bool HandleReloadLootTemplatesQuestMailCommand(const char* args);
- bool HandleReloadLootTemplatesSkinningCommand(const char* args);
- bool HandleReloadMangosStringCommand(const char* args);
- bool HandleReloadNpcGossipCommand(const char* args);
- bool HandleReloadNpcTrainerCommand(const char* args);
- bool HandleReloadNpcVendorCommand(const char* args);
- bool HandleReloadQuestAreaTriggersCommand(const char* args);
- bool HandleReloadQuestEndScriptsCommand(const char* args);
- bool HandleReloadQuestStartScriptsCommand(const char* args);
- bool HandleReloadQuestTemplateCommand(const char* args);
- bool HandleReloadReservedNameCommand(const char*);
- bool HandleReloadSkillDiscoveryTemplateCommand(const char* args);
- bool HandleReloadSkillExtraItemTemplateCommand(const char* args);
- bool HandleReloadSkillFishingBaseLevelCommand(const char* args);
- bool HandleReloadSpellAffectCommand(const char* args);
- bool HandleReloadSpellChainCommand(const char* args);
- bool HandleReloadSpellElixirCommand(const char* args);
- bool HandleReloadSpellLearnSpellCommand(const char* args);
- bool HandleReloadSpellProcEventCommand(const char* args);
- bool HandleReloadSpellScriptTargetCommand(const char* args);
- bool HandleReloadSpellScriptsCommand(const char* args);
- bool HandleReloadSpellTargetPositionCommand(const char* args);
- bool HandleReloadSpellThreatsCommand(const char* args);
- bool HandleReloadSpellPetAurasCommand(const char* args);
- bool HandleReloadPageTextsCommand(const char* args);
- bool HandleReloadItemEnchantementsCommand(const char* args);
-
- bool HandleInstanceListBindsCommand(const char* args);
- bool HandleInstanceUnbindCommand(const char* args);
- bool HandleInstanceStatsCommand(const char* args);
- bool HandleInstanceSaveDataCommand(const char * args);
-
- bool HandleAddHonorCommand(const char* args);
- bool HandleHonorAddKillCommand(const char* args);
- bool HandleUpdateHonorFieldsCommand(const char* args);
-
- bool HandleLoadScriptsCommand(const char* args);
- bool HandleSendQuestPartyMsgCommand(const char* args);
- bool HandleSendQuestInvalidMsgCommand(const char* args);
-
- bool HandleDebugInArcCommand(const char* args);
- bool HandleDebugSpellFailCommand(const char* args);
-
- bool HandleGUIDCommand(const char* args);
- bool HandleNameCommand(const char* args);
- bool HandleSubNameCommand(const char* args);
- bool HandleItemMoveCommand(const char* args);
- bool HandleDelCreatureCommand(const char* args);
- bool HandleDeMorphCommand(const char* args);
- bool HandleAddVendorItemCommand(const char* args);
- bool HandleDelVendorItemCommand(const char* args);
- bool HandleAddMoveCommand(const char* args);
- bool HandleSetMoveTypeCommand(const char* args);
- bool HandleChangeLevelCommand(const char* args);
- bool HandleSetPoiCommand(const char* args);
- bool HandleEquipErrorCommand(const char* args);
- bool HandleNPCFlagCommand(const char* args);
- bool HandleSetModelCommand(const char* args);
- bool HandleFactionIdCommand(const char* args);
- bool HandleAddSpwCommand(const char* args);
- bool HandleSpawnDistCommand(const char* args);
- bool HandleSpawnTimeCommand(const char* args);
- bool HandleGoCreatureCommand(const char* args);
- bool HandleGoObjectCommand(const char* args);
- bool HandleGoTriggerCommand(const char* args);
- bool HandleGoGraveyardCommand(const char* args);
- bool HandleTargetObjectCommand(const char* args);
- bool HandleDelObjectCommand(const char* args);
- bool HandleMoveCreatureCommand(const char* args);
- bool HandleMoveObjectCommand(const char* args);
- bool HandleTurnObjectCommand(const char* args);
- bool HandlePInfoCommand(const char* args);
- bool HandlePLimitCommand(const char* args);
- bool HandleMuteCommand(const char* args);
- bool HandleUnmuteCommand(const char* args);
- bool HandleMovegensCommand(const char* args);
-
- bool HandleBanCommand(const char* args);
- bool HandleUnBanCommand(const char* args);
- bool HandleBanInfoCommand(const char* args);
- bool HandleBanListCommand(const char* args);
- bool HandleIdleRestartCommand(const char* args);
- bool HandleIdleShutDownCommand(const char* args);
- bool HandleShutDownCommand(const char* args);
- bool HandleRestartCommand(const char* args);
- bool HandleSecurityCommand(const char* args);
- bool HandleGoXYCommand(const char* args);
- bool HandleGoXYZCommand(const char* args);
- bool HandleGoZoneXYCommand(const char* args);
- bool HandleGoGridCommand(const char* args);
- bool HandleAddWeaponCommand(const char* args);
- bool HandleAllowMovementCommand(const char* args);
- bool HandleGoCommand(const char* args);
-
- bool HandleLearnCommand(const char* args);
- bool HandleLearnAllCommand(const char* args);
- bool HandleLearnAllGMCommand(const char* args);
- bool HandleLearnAllCraftsCommand(const char* args);
- bool HandleLearnAllRecipesCommand(const char* args);
- bool HandleLearnAllDefaultCommand(const char* args);
- bool HandleLearnAllLangCommand(const char* args);
- bool HandleLearnAllMyClassCommand(const char* args);
- bool HandleLearnAllMySpellsCommand(const char* args);
- bool HandleLearnAllMyTalentsCommand(const char* args);
-
- bool HandleLookupAreaCommand(const char* args);
- bool HandleLookupCreatureCommand(const char* args);
- bool HandleLookupEventCommand(const char* args);
- bool HandleLookupFactionCommand(const char * args);
- bool HandleLookupItemCommand(const char * args);
- bool HandleLookupItemSetCommand(const char * args);
- bool HandleLookupObjectCommand(const char* args);
- bool HandleLookupPlayerIpCommand(const char* args);
- bool HandleLookupPlayerAccountCommand(const char* args);
- bool HandleLookupPlayerEmailCommand(const char* args);
- bool HandleLookupQuestCommand(const char* args);
- bool HandleLookupSkillCommand(const char* args);
- bool HandleLookupSpellCommand(const char* args);
- bool HandleLookupTeleCommand(const char * args);
-
- bool HandleCooldownCommand(const char* args);
- bool HandleUnLearnCommand(const char* args);
- bool HandleGetDistanceCommand(const char* args);
- bool HandleGameObjectCommand(const char* args);
- bool HandleAnimCommand(const char* args);
- bool HandlePlaySoundCommand(const char* args);
- bool HandleStandStateCommand(const char* args);
- bool HandleDieCommand(const char* args);
- bool HandleDamageCommand(const char *args);
- bool HandleReviveCommand(const char* args);
- bool HandleMorphCommand(const char* args);
- bool HandleAuraCommand(const char* args);
- bool HandleUnAuraCommand(const char* args);
- bool HandleLinkGraveCommand(const char* args);
- bool HandleNearGraveCommand(const char* args);
- bool HandleSpawnTransportCommand(const char* args);
- bool HandleExploreCheatCommand(const char* args);
- bool HandleTextEmoteCommand(const char* args);
- bool HandleNpcInfoCommand(const char* args);
- bool HandleHoverCommand(const char* args);
- bool HandleLevelUpCommand(const char* args);
- bool HandleShowAreaCommand(const char* args);
- bool HandleHideAreaCommand(const char* args);
- bool HandleAddItemCommand(const char* args);
- bool HandleAddItemSetCommand(const char* args);
-
- bool HandleGuildCreateCommand(const char* args);
- bool HandleGuildInviteCommand(const char* args);
- bool HandleGuildUninviteCommand(const char* args);
- bool HandleGuildRankCommand(const char* args);
- bool HandleGuildDeleteCommand(const char* args);
- bool HandleUpdate(const char* args);
- bool HandleBankCommand(const char* args);
- bool HandleChangeWeather(const char* args);
- bool HandleKickPlayerCommand(const char * args);
- bool HandleTeleCommand(const char * args);
- bool HandleAddTeleCommand(const char * args);
- bool HandleDelTeleCommand(const char * args);
- bool HandleListAurasCommand(const char * args);
-
- bool HandleResetHonorCommand(const char * args);
- bool HandleResetLevelCommand(const char * args);
- bool HandleResetSpellsCommand(const char * args);
-
- bool HandleResetStatsCommand(const char * args);
- bool HandleResetTalentsCommand(const char * args);
-
- bool HandleResetAllCommand(const char * args);
- bool HandleTicketCommand(const char* args);
- bool HandleDelTicketCommand(const char* args);
- bool HandleMaxSkillCommand(const char* args);
- bool HandleSetSkillCommand(const char* args);
- bool HandleListCreatureCommand(const char* args);
- bool HandleListItemCommand(const char* args);
- bool HandleListObjectCommand(const char* args);
- bool HandleNearObjectCommand(const char* args);
- bool HandlePasswordCommand(const char* args);
- bool HandleLockAccountCommand(const char* args);
- bool HandleRespawnCommand(const char* args);
- bool HandleWpAddCommand(const char* args);
- bool HandleWpModifyCommand(const char* args);
- bool HandleWpShowCommand(const char* args);
- bool HandleWpExportCommand(const char* args);
- bool HandleWpImportCommand(const char* args);
- bool HandleFlyModeCommand(const char* args);
- bool HandleSendOpcodeCommand(const char* args);
- bool HandleSellErrorCommand(const char* args);
- bool HandleBuyErrorCommand(const char* args);
- bool HandleUpdateWorldStateCommand(const char* args);
- bool HandlePlaySound2Command(const char* args);
- bool HandleSendChannelNotifyCommand(const char* args);
- bool HandleSendChatMsgCommand(const char* args);
- bool HandleRenameCommand(const char * args);
- bool HandleLoadPDumpCommand(const char *args);
- bool HandleWritePDumpCommand(const char *args);
- bool HandleChangeEntryCommand(const char *args);
- bool HandleCastCommand(const char *args);
- bool HandleCastBackCommand(const char *args);
- bool HandleCastDistCommand(const char *args);
- bool HandleCastSelfCommand(const char *args);
- bool HandleCastTargetCommand(const char *args);
- bool HandleComeToMeCommand(const char *args);
- bool HandleCombatStopCommand(const char *args);
- bool HandleFlushArenaPointsCommand(const char *args);
-
- //! Development Commands
- bool HandleSetValue(const char* args);
- bool HandleGetValue(const char* args);
- bool HandleSet32Bit(const char* args);
- bool HandleMod32Value(const char* args);
- bool HandleAddQuest(const char * args);
- bool HandleRemoveQuest(const char * args);
- bool HandleCompleteQuest(const char * args);
- bool HandleSaveAllCommand(const char* args);
- bool HandleGetItemState(const char * args);
- bool HandleGetLootRecipient(const char * args);
- bool HandleDebugArenaCommand(const char * args);
-
- Player* getSelectedPlayer();
- Creature* getSelectedCreature();
- Unit* getSelectedUnit();
- char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL);
- char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL);
- uint32 extractSpellIdFromLink(char* text);
- GameTele const* extractGameTeleFromLink(char* text);
-
- GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry);
-
- WorldSession * m_session;
-
- // Utility methods for commands
- void ShowTicket(uint64 guid, char const* text, char const* time);
- uint32 GetTicketIDByNum(uint32 num);
- bool LookupPlayerSearchCommand(QueryResult* result, int32 limit);
-
- void SetSentErrorMessage(bool val){ sentErrorMessage = val;};
- private:
- // common global flag
- static bool load_command_table;
- bool sentErrorMessage;
-};
-#endif
-
-char const *fmtstring( char const *format, ... );
+/*
+ * 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
+ */
+
+#ifndef MANGOSSERVER_CHAT_H
+#define MANGOSSERVER_CHAT_H
+
+#include "SharedDefines.h"
+
+class ChatHandler;
+class WorldSession;
+class Creature;
+class Player;
+class Unit;
+struct GameTele;
+
+class ChatCommand
+{
+ public:
+ const char * Name;
+ uint32 SecurityLevel; // function pointer required correct align (use uint32)
+ bool (ChatHandler::*Handler)(const char* args);
+ std::string Help;
+ ChatCommand * ChildCommands;
+};
+
+class ChatHandler
+{
+ public:
+ explicit ChatHandler(WorldSession* session) : m_session(session) {}
+ explicit ChatHandler(Player* player) : m_session(player->GetSession()) {}
+ ~ChatHandler() {}
+
+ static void FillMessageData( WorldPacket *data, WorldSession* session, uint8 type, uint32 language, const char *channelName, uint64 target_guid, const char *message, Unit *speaker);
+
+ void FillMessageData( WorldPacket *data, uint8 type, uint32 language, uint64 target_guid, const char* message)
+ {
+ FillMessageData( data, m_session, type, language, NULL, target_guid, message, NULL);
+ }
+
+ void FillSystemMessageData( WorldPacket *data, const char* message )
+ {
+ FillMessageData( data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, 0, message );
+ }
+
+ static char* LineFromMessage(char*& pos) { char* start = strtok(pos,"\n"); pos = NULL; return start; }
+
+ const char *GetMangosString(int32 entry);
+
+ void SendSysMessage( const char *str);
+ void SendSysMessage( int32 entry);
+ void PSendSysMessage( const char *format, ...) ATTR_PRINTF(2,3);
+ void PSendSysMessage( int32 entry, ... );
+
+ int ParseCommands(const char* text);
+
+ protected:
+ bool hasStringAbbr(const char* name, const char* part);
+ void SendGlobalSysMessage(const char *str);
+
+ bool ExecuteCommandInTable(ChatCommand *table, const char* text, std::string fullcommand);
+ bool ShowHelpForCommand(ChatCommand *table, const char* cmd);
+ bool ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd);
+
+ ChatCommand* getCommandTable();
+
+ bool HandleHelpCommand(const char* args);
+ bool HandleCommandsCommand(const char* args);
+ bool HandleAcctCommand(const char* args);
+ bool HandleStartCommand(const char* args);
+ bool HandleInfoCommand(const char* args);
+ bool HandleDismountCommand(const char* args);
+ bool HandleSaveCommand(const char* args);
+ bool HandleGMListCommand(const char* args);
+
+ bool HandleNamegoCommand(const char* args);
+ bool HandleGonameCommand(const char* args);
+ bool HandleGroupgoCommand(const char* args);
+ bool HandleRecallCommand(const char* args);
+ 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);
+ bool HandleWhispersCommand(const char* args);
+ bool HandleSayCommand(const char* args);
+ bool HandleNpcWhisperCommand(const char* args);
+ bool HandleYellCommand(const char* args);
+ bool HandlePlayEmoteCommand(const char* args);
+ bool HandleSendMailCommand(const char* args);
+ bool HandleNameTeleCommand(const char* args);
+ bool HandleGroupTeleCommand(const char* args);
+ bool HandleDrunkCommand(const char* args);
+
+ bool HandleEventActiveListCommand(const char* args);
+ bool HandleEventStartCommand(const char* args);
+ bool HandleEventStopCommand(const char* args);
+ bool HandleEventInfoCommand(const char* args);
+
+ bool HandleModifyKnownTitlesCommand(const char* args);
+ bool HandleModifyHPCommand(const char* args);
+ bool HandleModifyManaCommand(const char* args);
+ bool HandleModifyRageCommand(const char* args);
+ bool HandleModifyEnergyCommand(const char* args);
+ bool HandleModifyMoneyCommand(const char* args);
+ bool HandleModifyASpeedCommand(const char* args);
+ bool HandleModifySpeedCommand(const char* args);
+ bool HandleModifyBWalkCommand(const char* args);
+ bool HandleModifyFlyCommand(const char* args);
+ bool HandleModifySwimCommand(const char* args);
+ bool HandleModifyScaleCommand(const char* args);
+ bool HandleModifyMountCommand(const char* args);
+ bool HandleModifyBitCommand(const char* args);
+ bool HandleModifyFactionCommand(const char* args);
+ bool HandleModifySpellCommand(const char* args);
+ bool HandleModifyTalentCommand (const char* args);
+ bool HandleModifyHonorCommand (const char* args);
+ bool HandleModifyRepCommand(const char* args);
+ bool HandleModifyArenaCommand(const char* args);
+
+ bool HandleReloadCommand(const char* args);
+ bool HandleReloadAllCommand(const char* args);
+ bool HandleReloadAllAreaCommand(const char* args);
+ bool HandleReloadAllItemCommand(const char* args);
+ bool HandleReloadAllLootCommand(const char* args);
+ bool HandleReloadAllNpcCommand(const char* args);
+ bool HandleReloadAllQuestCommand(const char* args);
+ bool HandleReloadAllScriptsCommand(const char* args);
+ bool HandleReloadAllSpellCommand(const char* args);
+
+ bool HandleReloadConfigCommand(const char* args);
+
+ bool HandleReloadAreaTriggerTavernCommand(const char* args);
+ bool HandleReloadAreaTriggerTeleportCommand(const char* args);
+ bool HandleReloadEventScriptsCommand(const char* args);
+ bool HandleReloadCommandCommand(const char* args);
+ bool HandleReloadCreatureQuestRelationsCommand(const char* args);
+ bool HandleReloadCreatureQuestInvRelationsCommand(const char* args);
+ bool HandleReloadGameGraveyardZoneCommand(const char* args);
+ bool HandleReloadGameObjectScriptsCommand(const char* args);
+ bool HandleReloadGameTeleCommand(const char* args);
+ bool HandleReloadGOQuestRelationsCommand(const char* args);
+ bool HandleReloadGOQuestInvRelationsCommand(const char* args);
+ bool HandleReloadLootTemplatesCreatureCommand(const char* args);
+ bool HandleReloadLootTemplatesDisenchantCommand(const char* args);
+ bool HandleReloadLootTemplatesFishingCommand(const char* args);
+ bool HandleReloadLootTemplatesGameobjectCommand(const char* args);
+ bool HandleReloadLootTemplatesItemCommand(const char* args);
+ bool HandleReloadLootTemplatesPickpocketingCommand(const char* args);
+ bool HandleReloadLootTemplatesProspectingCommand(const char* args);
+ bool HandleReloadLootTemplatesReferenceCommand(const char* args);
+ bool HandleReloadLootTemplatesQuestMailCommand(const char* args);
+ bool HandleReloadLootTemplatesSkinningCommand(const char* args);
+ bool HandleReloadMangosStringCommand(const char* args);
+ bool HandleReloadNpcGossipCommand(const char* args);
+ bool HandleReloadNpcTrainerCommand(const char* args);
+ bool HandleReloadNpcVendorCommand(const char* args);
+ bool HandleReloadQuestAreaTriggersCommand(const char* args);
+ bool HandleReloadQuestEndScriptsCommand(const char* args);
+ bool HandleReloadQuestStartScriptsCommand(const char* args);
+ bool HandleReloadQuestTemplateCommand(const char* args);
+ bool HandleReloadReservedNameCommand(const char*);
+ bool HandleReloadSkillDiscoveryTemplateCommand(const char* args);
+ bool HandleReloadSkillExtraItemTemplateCommand(const char* args);
+ bool HandleReloadSkillFishingBaseLevelCommand(const char* args);
+ bool HandleReloadSpellAffectCommand(const char* args);
+ bool HandleReloadSpellChainCommand(const char* args);
+ bool HandleReloadSpellElixirCommand(const char* args);
+ bool HandleReloadSpellLearnSpellCommand(const char* args);
+ bool HandleReloadSpellProcEventCommand(const char* args);
+ bool HandleReloadSpellScriptTargetCommand(const char* args);
+ bool HandleReloadSpellScriptsCommand(const char* args);
+ bool HandleReloadSpellTargetPositionCommand(const char* args);
+ bool HandleReloadSpellThreatsCommand(const char* args);
+ bool HandleReloadSpellPetAurasCommand(const char* args);
+ bool HandleReloadPageTextsCommand(const char* args);
+ bool HandleReloadItemEnchantementsCommand(const char* args);
+
+ bool HandleInstanceListBindsCommand(const char* args);
+ bool HandleInstanceUnbindCommand(const char* args);
+ bool HandleInstanceStatsCommand(const char* args);
+ bool HandleInstanceSaveDataCommand(const char * args);
+
+ bool HandleAddHonorCommand(const char* args);
+ bool HandleHonorAddKillCommand(const char* args);
+ bool HandleUpdateHonorFieldsCommand(const char* args);
+
+ bool HandleLoadScriptsCommand(const char* args);
+ bool HandleSendQuestPartyMsgCommand(const char* args);
+ bool HandleSendQuestInvalidMsgCommand(const char* args);
+
+ bool HandleDebugInArcCommand(const char* args);
+ bool HandleDebugSpellFailCommand(const char* args);
+
+ bool HandleGUIDCommand(const char* args);
+ bool HandleNameCommand(const char* args);
+ bool HandleSubNameCommand(const char* args);
+ bool HandleItemMoveCommand(const char* args);
+ bool HandleDelCreatureCommand(const char* args);
+ bool HandleDeMorphCommand(const char* args);
+ bool HandleAddVendorItemCommand(const char* args);
+ bool HandleDelVendorItemCommand(const char* args);
+ bool HandleAddMoveCommand(const char* args);
+ bool HandleSetMoveTypeCommand(const char* args);
+ bool HandleChangeLevelCommand(const char* args);
+ bool HandleSetPoiCommand(const char* args);
+ bool HandleEquipErrorCommand(const char* args);
+ bool HandleNPCFlagCommand(const char* args);
+ bool HandleSetModelCommand(const char* args);
+ bool HandleFactionIdCommand(const char* args);
+ bool HandleAddSpwCommand(const char* args);
+ bool HandleSpawnDistCommand(const char* args);
+ bool HandleSpawnTimeCommand(const char* args);
+ bool HandleGoCreatureCommand(const char* args);
+ bool HandleGoObjectCommand(const char* args);
+ bool HandleGoTriggerCommand(const char* args);
+ bool HandleGoGraveyardCommand(const char* args);
+ bool HandleTargetObjectCommand(const char* args);
+ bool HandleDelObjectCommand(const char* args);
+ bool HandleMoveCreatureCommand(const char* args);
+ bool HandleMoveObjectCommand(const char* args);
+ bool HandleTurnObjectCommand(const char* args);
+ bool HandlePInfoCommand(const char* args);
+ bool HandlePLimitCommand(const char* args);
+ bool HandleMuteCommand(const char* args);
+ bool HandleUnmuteCommand(const char* args);
+ bool HandleMovegensCommand(const char* args);
+
+ bool HandleBanCommand(const char* args);
+ bool HandleUnBanCommand(const char* args);
+ bool HandleBanInfoCommand(const char* args);
+ bool HandleBanListCommand(const char* args);
+ bool HandleIdleRestartCommand(const char* args);
+ bool HandleIdleShutDownCommand(const char* args);
+ bool HandleShutDownCommand(const char* args);
+ bool HandleRestartCommand(const char* args);
+ bool HandleSecurityCommand(const char* args);
+ bool HandleGoXYCommand(const char* args);
+ bool HandleGoXYZCommand(const char* args);
+ bool HandleGoZoneXYCommand(const char* args);
+ bool HandleGoGridCommand(const char* args);
+ bool HandleAddWeaponCommand(const char* args);
+ bool HandleAllowMovementCommand(const char* args);
+ bool HandleGoCommand(const char* args);
+
+ bool HandleLearnCommand(const char* args);
+ bool HandleLearnAllCommand(const char* args);
+ bool HandleLearnAllGMCommand(const char* args);
+ bool HandleLearnAllCraftsCommand(const char* args);
+ bool HandleLearnAllRecipesCommand(const char* args);
+ bool HandleLearnAllDefaultCommand(const char* args);
+ bool HandleLearnAllLangCommand(const char* args);
+ bool HandleLearnAllMyClassCommand(const char* args);
+ bool HandleLearnAllMySpellsCommand(const char* args);
+ bool HandleLearnAllMyTalentsCommand(const char* args);
+
+ bool HandleLookupAreaCommand(const char* args);
+ bool HandleLookupCreatureCommand(const char* args);
+ bool HandleLookupEventCommand(const char* args);
+ bool HandleLookupFactionCommand(const char * args);
+ bool HandleLookupItemCommand(const char * args);
+ bool HandleLookupItemSetCommand(const char * args);
+ bool HandleLookupObjectCommand(const char* args);
+ bool HandleLookupPlayerIpCommand(const char* args);
+ bool HandleLookupPlayerAccountCommand(const char* args);
+ bool HandleLookupPlayerEmailCommand(const char* args);
+ bool HandleLookupQuestCommand(const char* args);
+ bool HandleLookupSkillCommand(const char* args);
+ bool HandleLookupSpellCommand(const char* args);
+ bool HandleLookupTeleCommand(const char * args);
+
+ bool HandleCooldownCommand(const char* args);
+ bool HandleUnLearnCommand(const char* args);
+ bool HandleGetDistanceCommand(const char* args);
+ bool HandleGameObjectCommand(const char* args);
+ bool HandleAnimCommand(const char* args);
+ bool HandlePlaySoundCommand(const char* args);
+ bool HandleStandStateCommand(const char* args);
+ bool HandleDieCommand(const char* args);
+ bool HandleDamageCommand(const char *args);
+ bool HandleReviveCommand(const char* args);
+ bool HandleMorphCommand(const char* args);
+ bool HandleAuraCommand(const char* args);
+ bool HandleUnAuraCommand(const char* args);
+ bool HandleLinkGraveCommand(const char* args);
+ bool HandleNearGraveCommand(const char* args);
+ bool HandleSpawnTransportCommand(const char* args);
+ bool HandleExploreCheatCommand(const char* args);
+ bool HandleTextEmoteCommand(const char* args);
+ bool HandleNpcInfoCommand(const char* args);
+ bool HandleHoverCommand(const char* args);
+ bool HandleLevelUpCommand(const char* args);
+ bool HandleShowAreaCommand(const char* args);
+ bool HandleHideAreaCommand(const char* args);
+ bool HandleAddItemCommand(const char* args);
+ bool HandleAddItemSetCommand(const char* args);
+
+ bool HandleGuildCreateCommand(const char* args);
+ bool HandleGuildInviteCommand(const char* args);
+ bool HandleGuildUninviteCommand(const char* args);
+ bool HandleGuildRankCommand(const char* args);
+ bool HandleGuildDeleteCommand(const char* args);
+ bool HandleUpdate(const char* args);
+ bool HandleBankCommand(const char* args);
+ bool HandleChangeWeather(const char* args);
+ bool HandleKickPlayerCommand(const char * args);
+ bool HandleTeleCommand(const char * args);
+ bool HandleAddTeleCommand(const char * args);
+ bool HandleDelTeleCommand(const char * args);
+ bool HandleListAurasCommand(const char * args);
+
+ bool HandleResetHonorCommand(const char * args);
+ bool HandleResetLevelCommand(const char * args);
+ bool HandleResetSpellsCommand(const char * args);
+
+ bool HandleResetStatsCommand(const char * args);
+ bool HandleResetTalentsCommand(const char * args);
+
+ bool HandleResetAllCommand(const char * args);
+ bool HandleTicketCommand(const char* args);
+ bool HandleDelTicketCommand(const char* args);
+ bool HandleMaxSkillCommand(const char* args);
+ bool HandleSetSkillCommand(const char* args);
+ bool HandleListCreatureCommand(const char* args);
+ bool HandleListItemCommand(const char* args);
+ bool HandleListObjectCommand(const char* args);
+ bool HandleNearObjectCommand(const char* args);
+ bool HandlePasswordCommand(const char* args);
+ bool HandleLockAccountCommand(const char* args);
+ bool HandleRespawnCommand(const char* args);
+ bool HandleWpAddCommand(const char* args);
+ bool HandleWpModifyCommand(const char* args);
+ bool HandleWpShowCommand(const char* args);
+ bool HandleWpExportCommand(const char* args);
+ bool HandleWpImportCommand(const char* args);
+ bool HandleFlyModeCommand(const char* args);
+ bool HandleSendOpcodeCommand(const char* args);
+ bool HandleSellErrorCommand(const char* args);
+ bool HandleBuyErrorCommand(const char* args);
+ bool HandleUpdateWorldStateCommand(const char* args);
+ bool HandlePlaySound2Command(const char* args);
+ bool HandleSendChannelNotifyCommand(const char* args);
+ bool HandleSendChatMsgCommand(const char* args);
+ bool HandleRenameCommand(const char * args);
+ bool HandleLoadPDumpCommand(const char *args);
+ bool HandleWritePDumpCommand(const char *args);
+ bool HandleChangeEntryCommand(const char *args);
+ bool HandleCastCommand(const char *args);
+ bool HandleCastBackCommand(const char *args);
+ bool HandleCastDistCommand(const char *args);
+ bool HandleCastSelfCommand(const char *args);
+ bool HandleCastTargetCommand(const char *args);
+ bool HandleComeToMeCommand(const char *args);
+ bool HandleCombatStopCommand(const char *args);
+ bool HandleFlushArenaPointsCommand(const char *args);
+
+ //! Development Commands
+ bool HandleSetValue(const char* args);
+ bool HandleGetValue(const char* args);
+ bool HandleSet32Bit(const char* args);
+ bool HandleMod32Value(const char* args);
+ bool HandleAddQuest(const char * args);
+ bool HandleRemoveQuest(const char * args);
+ bool HandleCompleteQuest(const char * args);
+ bool HandleSaveAllCommand(const char* args);
+ bool HandleGetItemState(const char * args);
+ bool HandleGetLootRecipient(const char * args);
+ bool HandleDebugArenaCommand(const char * args);
+
+ Player* getSelectedPlayer();
+ Creature* getSelectedCreature();
+ Unit* getSelectedUnit();
+ char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL);
+ char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL);
+ uint32 extractSpellIdFromLink(char* text);
+ GameTele const* extractGameTeleFromLink(char* text);
+
+ GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry);
+
+ WorldSession * m_session;
+
+ // Utility methods for commands
+ void ShowTicket(uint64 guid, char const* text, char const* time);
+ uint32 GetTicketIDByNum(uint32 num);
+ bool LookupPlayerSearchCommand(QueryResult* result, int32 limit);
+
+ void SetSentErrorMessage(bool val){ sentErrorMessage = val;};
+ private:
+ // common global flag
+ static bool load_command_table;
+ bool sentErrorMessage;
+};
+#endif
+
+char const *fmtstring( char const *format, ... );
diff --git a/src/game/ChatHandler.cpp b/src/game/ChatHandler.cpp
index 3a3f2d1d1db..80935be78c6 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(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);
-}
+/*
+ * 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 7b4c5b91f71..7618519d5ee 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_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);
+/*
+ * 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 814b133a1b3..4532c4e8514 100644
--- a/src/game/Creature.cpp
+++ b/src/game/Creature.cpp
@@ -1,2066 +1,2066 @@
-/*
- * 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 "World.h"
-#include "ObjectMgr.h"
-#include "SpellMgr.h"
-#include "Creature.h"
-#include "QuestDef.h"
-#include "GossipDef.h"
-#include "Player.h"
-#include "Opcodes.h"
-#include "Log.h"
-#include "LootMgr.h"
-#include "MapManager.h"
-#include "CreatureAI.h"
-#include "CreatureAISelector.h"
-#include "Formulas.h"
-#include "SpellAuras.h"
-#include "WaypointMovementGenerator.h"
-#include "InstanceData.h"
-#include "BattleGround.h"
-#include "Util.h"
-#include "GridNotifiers.h"
-#include "GridNotifiersImpl.h"
-#include "CellImpl.h"
-
-// apply implementation of the singletons
-#include "Policies/SingletonImp.h"
-
-void TrainerSpellData::Clear()
-{
- for (TrainerSpellList::iterator itr = spellList.begin(); itr != spellList.end(); ++itr)
- delete (*itr);
- spellList.empty();
-}
-
-TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const
-{
- for(TrainerSpellList::const_iterator itr = spellList.begin(); itr != spellList.end(); ++itr)
- if((*itr)->spell == spell_id)
- return *itr;
-
- return NULL;
-}
-
-bool VendorItemData::RemoveItem( uint32 item_id )
-{
- for(VendorItemList::iterator i = m_items.begin(); i != m_items.end(); ++i )
- {
- if((*i)->item==item_id)
- {
- m_items.erase(i);
- return true;
- }
- }
- 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 )
- if((*i)->item==item_id)
- return *i;
- return NULL;
-}
-
-Creature::Creature() :
-Unit(), i_AI(NULL),
-lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0),
-m_lootMoney(0), m_lootRecipient(0),
-m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(0.0f),
-m_gossipOptionLoaded(false),m_emoteState(0), m_isPet(false), m_isTotem(false),
-m_regenTimer(2000), m_defaultMovementType(IDLE_MOTION_TYPE), m_equipmentId(0),
-m_AlreadyCallAssistence(false), m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false),
-m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),m_creatureInfo(NULL), m_DBTableGuid(0)
-{
- m_valuesCount = UNIT_END;
-
- for(int i =0; i<4; ++i)
- m_spells[i] = 0;
-
- m_CreatureSpellCooldowns.clear();
- m_CreatureCategoryCooldowns.clear();
- m_GlobalCooldown = 0;
- m_unit_movement_flags = MOVEMENTFLAG_WALK_MODE;
-}
-
-Creature::~Creature()
-{
- CleanupsBeforeDelete();
-
- m_vendorItemCounts.clear();
-
- delete i_AI;
- i_AI = NULL;
-}
-
-void Creature::AddToWorld()
-{
- ///- Register the creature for guid lookup
- if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
- Unit::AddToWorld();
-}
-
-void Creature::RemoveFromWorld()
-{
- ///- Remove the creature from the accessor
- if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
- Unit::RemoveFromWorld();
-}
-
-void Creature::RemoveCorpse()
-{
- if( getDeathState()!=CORPSE && !m_isDeadByDefault || getDeathState()!=ALIVE && m_isDeadByDefault )
- return;
-
- m_deathTimer = 0;
- setDeathState(DEAD);
- ObjectAccessor::UpdateObjectVisibility(this);
- loot.clear();
- m_respawnTime = time(NULL) + m_respawnDelay;
-
- float x,y,z,o;
- GetRespawnCoord(x, y, z, &o);
- MapManager::Instance().GetMap(GetMapId(), this)->CreatureRelocation(this,x,y,z,o);
-}
-
-/**
- * change the entry of creature until respawn
- */
-bool Creature::InitEntry(uint32 Entry, uint32 team, const CreatureData *data )
-{
- CreatureInfo const *normalInfo = objmgr.GetCreatureTemplate(Entry);
- if(!normalInfo)
- {
- sLog.outErrorDb("Creature::UpdateEntry creature entry %u does not exist.", Entry);
- return false;
- }
-
- // get heroic mode entry
- uint32 actualEntry = Entry;
- CreatureInfo const *cinfo = normalInfo;
- if(normalInfo->HeroicEntry)
- {
- Map *map = MapManager::Instance().FindMap(GetMapId(), GetInstanceId());
- if(map && map->IsHeroic())
- {
- cinfo = objmgr.GetCreatureTemplate(normalInfo->HeroicEntry);
- if(!cinfo)
- {
- sLog.outErrorDb("Creature::UpdateEntry creature heroic entry %u does not exist.", actualEntry);
- return false;
- }
- }
- }
-
- SetUInt32Value(OBJECT_FIELD_ENTRY, Entry); // normal entry always
- m_creatureInfo = cinfo; // map mode related always
-
- if (cinfo->DisplayID_A == 0 || cinfo->DisplayID_H == 0) // Cancel load if no model defined
- {
- sLog.outErrorDb("Creature (Entry: %u) has no model defined for Horde or Alliance in table `creature_template`, can't load. ",Entry);
- return false;
- }
-
- uint32 display_id = objmgr.ChooseDisplayId(team, GetCreatureInfo(), data);
- CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
- if (!minfo)
- {
- sLog.outErrorDb("Creature (Entry: %u) has model %u not found in table `creature_model_info`, can't load. ", Entry, display_id);
- return false;
- }
- else
- display_id = minfo->modelid; // it can be different (for another gender)
-
- SetDisplayId(display_id);
- SetNativeDisplayId(display_id);
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
-
- // Load creature equipment
- if(!data || data->equipmentId == 0)
- { // use default from the template
- LoadEquipment(cinfo->equipmentId);
- }
- else if(data && data->equipmentId != -1)
- { // override, -1 means no equipment
- LoadEquipment(data->equipmentId);
- }
-
- SetName(normalInfo->Name); // at normal entry always
-
- SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius);
- SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach );
-
- SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
-
- SetSpeed(MOVE_WALK, cinfo->speed );
- SetSpeed(MOVE_RUN, cinfo->speed );
- SetSpeed(MOVE_SWIM, cinfo->speed );
-
- SetFloatValue(OBJECT_FIELD_SCALE_X, cinfo->scale);
-
- // checked at loading
- m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
- if(!m_respawnradius && m_defaultMovementType==RANDOM_MOTION_TYPE)
- m_defaultMovementType = IDLE_MOTION_TYPE;
-
- return true;
-}
-
-bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData *data )
-{
- if(!InitEntry(Entry,team,data))
- return false;
-
- m_regenHealth = GetCreatureInfo()->RegenHealth;
-
- // creatures always have melee weapon ready if any
- SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
- SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_AURAS );
-
- SelectLevel(GetCreatureInfo());
- if (team == HORDE)
- SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetCreatureInfo()->faction_H);
- else
- SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetCreatureInfo()->faction_A);
-
- SetUInt32Value(UNIT_NPC_FLAGS,GetCreatureInfo()->npcflag);
-
- SetAttackTime(BASE_ATTACK, GetCreatureInfo()->baseattacktime);
- SetAttackTime(OFF_ATTACK, GetCreatureInfo()->baseattacktime);
- SetAttackTime(RANGED_ATTACK,GetCreatureInfo()->rangeattacktime);
-
- SetUInt32Value(UNIT_FIELD_FLAGS,GetCreatureInfo()->Flags);
- SetUInt32Value(UNIT_DYNAMIC_FLAGS,GetCreatureInfo()->dynamicflags);
-
- SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(GetCreatureInfo()->armor));
- SetModifierValue(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(GetCreatureInfo()->resistance1));
- SetModifierValue(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(GetCreatureInfo()->resistance2));
- SetModifierValue(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(GetCreatureInfo()->resistance3));
- SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(GetCreatureInfo()->resistance4));
- SetModifierValue(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(GetCreatureInfo()->resistance5));
- SetModifierValue(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(GetCreatureInfo()->resistance6));
-
- SetCanModifyStats(true);
- UpdateAllStats();
-
- FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(GetCreatureInfo()->faction_A);
- if (factionTemplate) // check and error show at loading templates
- {
- FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->faction);
- if (factionEntry)
- if( !(GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN) &&
- (factionEntry->team == ALLIANCE || factionEntry->team == HORDE) )
- SetPvP(true);
- }
-
- m_spells[0] = GetCreatureInfo()->spell1;
- m_spells[1] = GetCreatureInfo()->spell2;
- m_spells[2] = GetCreatureInfo()->spell3;
- m_spells[3] = GetCreatureInfo()->spell4;
-
- return true;
-}
-
-void Creature::Update(uint32 diff)
-{
- if(m_GlobalCooldown <= diff)
- m_GlobalCooldown = 0;
- else
- m_GlobalCooldown -= diff;
-
- switch( m_deathState )
- {
- case JUST_ALIVED:
- // Dont must be called, see Creature::setDeathState JUST_ALIVED -> ALIVE promoting.
- sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_ALIVED (4)",GetGUIDLow(),GetEntry());
- break;
- case JUST_DIED:
- // Dont must be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
- sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_DEAD (1)",GetGUIDLow(),GetEntry());
- break;
- case DEAD:
- {
- if( m_respawnTime <= time(NULL) )
- {
- DEBUG_LOG("Respawning...");
- m_respawnTime = 0;
- lootForPickPocketed = false;
- lootForBody = false;
-
- if(m_originalEntry != GetUInt32Value(OBJECT_FIELD_ENTRY))
- UpdateEntry(m_originalEntry);
-
- CreatureInfo const *cinfo = GetCreatureInfo();
-
- SelectLevel(cinfo);
- SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
- if (m_isDeadByDefault)
- {
- setDeathState(JUST_DIED);
- SetHealth(0);
- i_motionMaster.Clear();
- clearUnitState(UNIT_STAT_ALL_STATE);
- LoadCreaturesAddon(true);
- }
- else
- setDeathState( JUST_ALIVED );
-
- //Call AI respawn virtual function
- i_AI->JustRespawned();
-
- MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
- }
- break;
- }
- case CORPSE:
- {
- if (m_isDeadByDefault)
- break;
-
- if( m_deathTimer <= diff )
- {
- RemoveCorpse();
- DEBUG_LOG("Removing corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY));
- }
- else
- {
- m_deathTimer -= diff;
- if (m_groupLootTimer && lootingGroupLeaderGUID)
- {
- if(diff <= m_groupLootTimer)
- {
- m_groupLootTimer -= diff;
- }
- else
- {
- Group* group = objmgr.GetGroupByLeader(lootingGroupLeaderGUID);
- if (group)
- group->EndRoll();
- m_groupLootTimer = 0;
- lootingGroupLeaderGUID = 0;
- }
- }
- }
-
- break;
- }
- case ALIVE:
- {
- if (m_isDeadByDefault)
- {
- if( m_deathTimer <= diff )
- {
- RemoveCorpse();
- DEBUG_LOG("Removing alive corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY));
- }
- else
- {
- m_deathTimer -= diff;
- }
- }
-
- Unit::Update( diff );
-
- // creature can be dead after Unit::Update call
- // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
- if(!isAlive())
- break;
-
- if(!IsInEvadeMode())
- {
- // do not allow the AI to be changed during update
- m_AI_locked = true;
- i_AI->UpdateAI(diff);
- m_AI_locked = false;
- }
-
- // creature can be dead after UpdateAI call
- // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
- if(!isAlive())
- break;
- if(m_regenTimer > 0)
- {
- if(diff >= m_regenTimer)
- m_regenTimer = 0;
- else
- m_regenTimer -= diff;
- }
- if (m_regenTimer != 0)
- break;
-
- if (!isInCombat() || IsPolymorphed())
- RegenerateHealth();
-
- RegenerateMana();
-
- m_regenTimer = 2000;
- break;
- }
- default:
- break;
- }
-}
-
-void Creature::RegenerateMana()
-{
- uint32 curValue = GetPower(POWER_MANA);
- uint32 maxValue = GetMaxPower(POWER_MANA);
-
- if (curValue >= maxValue)
- return;
-
- uint32 addvalue = 0;
-
- // Combat and any controlled creature
- if (isInCombat() || GetCharmerOrOwnerGUID())
- {
- if(!IsUnderLastManaUseEffect())
- {
- float ManaIncreaseRate = sWorld.getRate(RATE_POWER_MANA);
- float Spirit = GetStat(STAT_SPIRIT);
-
- addvalue = uint32((Spirit/5.0f + 17.0f) * ManaIncreaseRate);
- }
- }
- else
- addvalue = maxValue/3;
-
- ModifyPower(POWER_MANA, addvalue);
-}
-
-void Creature::RegenerateHealth()
-{
- if (!isRegeneratingHealth())
- return;
-
- uint32 curValue = GetHealth();
- uint32 maxValue = GetMaxHealth();
-
- if (curValue >= maxValue)
- return;
-
- uint32 addvalue = 0;
-
- // Not only pet, but any controelled creature
- if(GetCharmerOrOwnerGUID())
- {
- float HealthIncreaseRate = sWorld.getRate(RATE_HEALTH);
- float Spirit = GetStat(STAT_SPIRIT);
-
- if( GetPower(POWER_MANA) > 0 )
- addvalue = uint32(Spirit * 0.25 * HealthIncreaseRate);
- else
- addvalue = uint32(Spirit * 0.80 * HealthIncreaseRate);
- }
- else
- addvalue = maxValue/3;
-
- ModifyHealth(addvalue);
-}
-
-bool Creature::AIM_Initialize()
-{
- // make sure nothing can change the AI during AI update
- if(m_AI_locked)
- {
- sLog.outDebug("AIM_Initialize: failed to init, locked.");
- return false;
- }
-
- CreatureAI * oldAI = i_AI;
- i_motionMaster.Initialize();
- i_AI = FactorySelector::selectAI(this);
- if (oldAI)
- delete oldAI;
- return true;
-}
-
-bool Creature::Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, const CreatureData *data)
-{
- SetMapId(map->GetId());
- SetInstanceId(map->GetInstanceId());
-
- //oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0;
- const bool bResult = CreateFromProto(guidlow, Entry, team, data);
-
- if (bResult)
- {
- switch (GetCreatureInfo()->rank)
- {
- case CREATURE_ELITE_RARE:
- m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_RARE);
- break;
- case CREATURE_ELITE_ELITE:
- m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_ELITE);
- break;
- case CREATURE_ELITE_RAREELITE:
- m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_RAREELITE);
- break;
- case CREATURE_ELITE_WORLDBOSS:
- m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_WORLDBOSS);
- break;
- default:
- m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_NORMAL);
- break;
- }
- LoadCreaturesAddon();
- }
-
- return bResult;
-}
-
-bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const
-{
- if(!isTrainer())
- return false;
-
- TrainerSpellData const* trainer_spells = GetTrainerSpells();
-
-
- if(!trainer_spells || trainer_spells->spellList.empty())
- {
- sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_TRAINER but have empty trainer spell list.",
- GetGUIDLow(),GetEntry());
- return false;
- }
-
- switch(GetCreatureInfo()->trainer_type)
- {
- case TRAINER_TYPE_CLASS:
- if(pPlayer->getClass()!=GetCreatureInfo()->classNum)
- {
- if(msg)
- {
- pPlayer->PlayerTalkClass->ClearMenus();
- switch(GetCreatureInfo()->classNum)
- {
- case CLASS_DRUID: pPlayer->PlayerTalkClass->SendGossipMenu( 4913,GetGUID()); break;
- case CLASS_HUNTER: pPlayer->PlayerTalkClass->SendGossipMenu(10090,GetGUID()); break;
- case CLASS_MAGE: pPlayer->PlayerTalkClass->SendGossipMenu( 328,GetGUID()); break;
- case CLASS_PALADIN:pPlayer->PlayerTalkClass->SendGossipMenu( 1635,GetGUID()); break;
- case CLASS_PRIEST: pPlayer->PlayerTalkClass->SendGossipMenu( 4436,GetGUID()); break;
- case CLASS_ROGUE: pPlayer->PlayerTalkClass->SendGossipMenu( 4797,GetGUID()); break;
- case CLASS_SHAMAN: pPlayer->PlayerTalkClass->SendGossipMenu( 5003,GetGUID()); break;
- case CLASS_WARLOCK:pPlayer->PlayerTalkClass->SendGossipMenu( 5836,GetGUID()); break;
- case CLASS_WARRIOR:pPlayer->PlayerTalkClass->SendGossipMenu( 4985,GetGUID()); break;
- }
- }
- return false;
- }
- break;
- case TRAINER_TYPE_PETS:
- if(pPlayer->getClass()!=CLASS_HUNTER)
- {
- pPlayer->PlayerTalkClass->ClearMenus();
- pPlayer->PlayerTalkClass->SendGossipMenu(3620,GetGUID());
- return false;
- }
- break;
- case TRAINER_TYPE_MOUNTS:
- if(GetCreatureInfo()->race && pPlayer->getRace() != GetCreatureInfo()->race)
- {
- if(msg)
- {
- pPlayer->PlayerTalkClass->ClearMenus();
- switch(GetCreatureInfo()->classNum)
- {
- case RACE_DWARF: pPlayer->PlayerTalkClass->SendGossipMenu(5865,GetGUID()); break;
- case RACE_GNOME: pPlayer->PlayerTalkClass->SendGossipMenu(4881,GetGUID()); break;
- case RACE_HUMAN: pPlayer->PlayerTalkClass->SendGossipMenu(5861,GetGUID()); break;
- case RACE_NIGHTELF: pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break;
- case RACE_ORC: pPlayer->PlayerTalkClass->SendGossipMenu(5863,GetGUID()); break;
- case RACE_TAUREN: pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break;
- case RACE_TROLL: pPlayer->PlayerTalkClass->SendGossipMenu(5816,GetGUID()); break;
- case RACE_UNDEAD_PLAYER:pPlayer->PlayerTalkClass->SendGossipMenu( 624,GetGUID()); break;
- case RACE_BLOODELF: pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break;
- case RACE_DRAENEI: pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break;
- }
- }
- return false;
- }
- break;
- case TRAINER_TYPE_TRADESKILLS:
- if(GetCreatureInfo()->trainer_spell && !pPlayer->HasSpell(GetCreatureInfo()->trainer_spell))
- {
- if(msg)
- {
- pPlayer->PlayerTalkClass->ClearMenus();
- pPlayer->PlayerTalkClass->SendGossipMenu(11031,GetGUID());
- }
- return false;
- }
- break;
- default:
- return false; // checked and error output at creature_template loading
- }
- return true;
-}
-
-bool Creature::isCanIneractWithBattleMaster(Player* pPlayer, bool msg) const
-{
- if(!isBattleMaster())
- return false;
-
- uint32 bgTypeId = objmgr.GetBattleMasterBG(GetEntry());
- if(!msg)
- return pPlayer->GetBGAccessByLevel(bgTypeId);
-
- if(!pPlayer->GetBGAccessByLevel(bgTypeId))
- {
- pPlayer->PlayerTalkClass->ClearMenus();
- switch(bgTypeId)
- {
- case BATTLEGROUND_AV: pPlayer->PlayerTalkClass->SendGossipMenu(7616,GetGUID()); break;
- case BATTLEGROUND_WS: pPlayer->PlayerTalkClass->SendGossipMenu(7599,GetGUID()); break;
- case BATTLEGROUND_AB: pPlayer->PlayerTalkClass->SendGossipMenu(7642,GetGUID()); break;
- case BATTLEGROUND_EY:
- case BATTLEGROUND_NA:
- case BATTLEGROUND_BE:
- case BATTLEGROUND_AA:
- case BATTLEGROUND_RL: pPlayer->PlayerTalkClass->SendGossipMenu(10024,GetGUID()); break;
- break;
- }
- return false;
- }
- return true;
-}
-
-bool Creature::isCanTrainingAndResetTalentsOf(Player* pPlayer) const
-{
- return pPlayer->getLevel() >= 10
- && GetCreatureInfo()->trainer_type == TRAINER_TYPE_CLASS
- && pPlayer->getClass() == GetCreatureInfo()->classNum;
-}
-
-void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
-{
- PlayerMenu* pm=pPlayer->PlayerTalkClass;
- pm->ClearMenus();
-
- // lazy loading single time at use
- LoadGossipOptions();
-
- GossipOption* gso;
- GossipOption* ingso;
-
- for( GossipOptionList::iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
- {
- gso=&*i;
- if(gso->GossipId == gossipid)
- {
- bool cantalking=true;
- if(gso->Id==1)
- {
- uint32 textid=GetNpcTextId();
- GossipText * gossiptext=objmgr.GetGossipText(textid);
- if(!gossiptext)
- cantalking=false;
- }
- else
- {
- switch (gso->Action)
- {
- case GOSSIP_OPTION_QUESTGIVER:
- pPlayer->PrepareQuestMenu(GetGUID());
- //if (pm->GetQuestMenu()->MenuItemCount() == 0)
- cantalking=false;
- //pm->GetQuestMenu()->ClearMenu();
- break;
- case GOSSIP_OPTION_ARMORER:
- cantalking=false; // added in special mode
- break;
- case GOSSIP_OPTION_SPIRITHEALER:
- if( !pPlayer->isDead() )
- cantalking=false;
- break;
- case GOSSIP_OPTION_VENDOR:
- {
- VendorItemData const* vItems = GetVendorItems();
- if(!vItems || vItems->Empty())
- {
- sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.",
- GetGUIDLow(),GetEntry());
- cantalking=false;
- }
- break;
- }
- case GOSSIP_OPTION_TRAINER:
- if(!isCanTrainingOf(pPlayer,false))
- cantalking=false;
- break;
- case GOSSIP_OPTION_UNLEARNTALENTS:
- if(!isCanTrainingAndResetTalentsOf(pPlayer))
- cantalking=false;
- break;
- case GOSSIP_OPTION_UNLEARNPETSKILLS:
- if(!pPlayer->GetPet() || pPlayer->GetPet()->getPetType() != HUNTER_PET || pPlayer->GetPet()->m_spells.size() <= 1 || GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || GetCreatureInfo()->classNum != CLASS_HUNTER)
- cantalking=false;
- break;
- case GOSSIP_OPTION_TAXIVENDOR:
- if ( pPlayer->GetSession()->SendLearnNewTaxiNode(this) )
- return;
- break;
- case GOSSIP_OPTION_BATTLEFIELD:
- if(!isCanIneractWithBattleMaster(pPlayer,false))
- cantalking=false;
- break;
- case GOSSIP_OPTION_SPIRITGUIDE:
- case GOSSIP_OPTION_INNKEEPER:
- case GOSSIP_OPTION_BANKER:
- case GOSSIP_OPTION_PETITIONER:
- case GOSSIP_OPTION_STABLEPET:
- case GOSSIP_OPTION_TABARDDESIGNER:
- case GOSSIP_OPTION_AUCTIONEER:
- break; // no checks
- default:
- sLog.outErrorDb("Creature %u (entry: %u) have unknown gossip option %u",GetGUIDLow(),GetEntry(),gso->Action);
- break;
- }
- }
-
- if(!gso->Option.empty() && cantalking )
- { //note for future dev: should have database fields for BoxMessage & BoxMoney
- pm->GetGossipMenu().AddMenuItem((uint8)gso->Icon,gso->Option, gossipid,gso->Action,"",0,false);
- ingso=gso;
- }
- }
- }
-
- ///some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
- if(pm->Empty())
- {
- if(HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER))
- {
- isCanTrainingOf(pPlayer,true); // output error message if need
- }
- if(HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_BATTLEMASTER))
- {
- isCanIneractWithBattleMaster(pPlayer,true); // output error message if need
- }
- }
-}
-
-void Creature::sendPreparedGossip(Player* player)
-{
- if(!player)
- return;
-
- GossipMenu& gossipmenu = player->PlayerTalkClass->GetGossipMenu();
-
- // in case empty gossip menu open quest menu if any
- if (gossipmenu.Empty() && GetNpcTextId() == 0)
- {
- player->SendPreparedQuest(GetGUID());
- return;
- }
-
- // in case non empty gossip menu (that not included quests list size) show it
- // (quest entries from quest menu wiill be included in list)
- player->PlayerTalkClass->SendGossipMenu(GetNpcTextId(), GetGUID());
-}
-
-void Creature::OnGossipSelect(Player* player, uint32 option)
-{
- GossipMenu& gossipmenu = player->PlayerTalkClass->GetGossipMenu();
-
- if(option >= gossipmenu.MenuItemCount())
- return;
-
- uint32 action=gossipmenu.GetItem(option).m_gAction;
- uint32 zoneid=GetZoneId();
- uint64 guid=GetGUID();
- GossipOption const *gossip=GetGossipOption( action );
- uint32 textid;
- if(!gossip)
- {
- zoneid=0;
- gossip=GetGossipOption( action );
- if(!gossip)
- return;
- }
- textid=GetGossipTextId( action, zoneid);
- if(textid==0)
- textid=GetNpcTextId();
-
- switch (gossip->Action)
- {
- case GOSSIP_OPTION_GOSSIP:
- player->PlayerTalkClass->CloseGossip();
- player->PlayerTalkClass->SendTalking( textid );
- break;
- case GOSSIP_OPTION_SPIRITHEALER:
- if( player->isDead() )
- CastSpell(this,17251,true,NULL,NULL,player->GetGUID());
- break;
- case GOSSIP_OPTION_QUESTGIVER:
- player->PrepareQuestMenu( guid );
- player->SendPreparedQuest( guid );
- break;
- case GOSSIP_OPTION_VENDOR:
- case GOSSIP_OPTION_ARMORER:
- player->GetSession()->SendListInventory(guid);
- break;
- case GOSSIP_OPTION_STABLEPET:
- player->GetSession()->SendStablePet(guid);
- break;
- case GOSSIP_OPTION_TRAINER:
- player->GetSession()->SendTrainerList(guid);
- break;
- case GOSSIP_OPTION_UNLEARNTALENTS:
- player->PlayerTalkClass->CloseGossip();
- player->SendTalentWipeConfirm(guid);
- break;
- case GOSSIP_OPTION_UNLEARNPETSKILLS:
- player->PlayerTalkClass->CloseGossip();
- player->SendPetSkillWipeConfirm();
- break;
- case GOSSIP_OPTION_TAXIVENDOR:
- player->GetSession()->SendTaxiMenu(this);
- break;
- case GOSSIP_OPTION_INNKEEPER:
- player->PlayerTalkClass->CloseGossip();
- player->SetBindPoint( guid );
- break;
- case GOSSIP_OPTION_BANKER:
- player->GetSession()->SendShowBank( guid );
- break;
- case GOSSIP_OPTION_PETITIONER:
- player->PlayerTalkClass->CloseGossip();
- player->GetSession()->SendPetitionShowList( guid );
- break;
- case GOSSIP_OPTION_TABARDDESIGNER:
- player->PlayerTalkClass->CloseGossip();
- player->GetSession()->SendTabardVendorActivate( guid );
- break;
- case GOSSIP_OPTION_AUCTIONEER:
- player->GetSession()->SendAuctionHello( guid, this );
- break;
- case GOSSIP_OPTION_SPIRITGUIDE:
- case GOSSIP_GUARD_SPELLTRAINER:
- case GOSSIP_GUARD_SKILLTRAINER:
- prepareGossipMenu( player,gossip->Id );
- sendPreparedGossip( player );
- break;
- case GOSSIP_OPTION_BATTLEFIELD:
- {
- uint32 bgTypeId = objmgr.GetBattleMasterBG(GetEntry());
- player->GetSession()->SendBattlegGroundList( GetGUID(), bgTypeId );
- break;
- }
- default:
- OnPoiSelect( player, gossip );
- break;
- }
-
-}
-
-void Creature::OnPoiSelect(Player* player, GossipOption const *gossip)
-{
- if(gossip->GossipId==GOSSIP_GUARD_SPELLTRAINER || gossip->GossipId==GOSSIP_GUARD_SKILLTRAINER)
- {
- //float x,y;
- //bool findnpc=false;
- Poi_Icon icon = ICON_POI_0;
- //QueryResult *result;
- //Field *fields;
- uint32 mapid=GetMapId();
- Map const* map=MapManager::Instance().GetBaseMap( mapid );
- uint16 areaflag=map->GetAreaFlag(GetPositionX(),GetPositionY());
- uint32 zoneid=Map::GetZoneId(areaflag,mapid);
- std::string areaname= gossip->Option;
- /*
- uint16 pflag;
-
- // use the action relate to creaturetemplate.trainer_type ?
- result= WorldDatabase.PQuery("SELECT creature.position_x,creature.position_y FROM creature,creature_template WHERE creature.map = '%u' AND creature.id = creature_template.entry AND creature_template.trainer_type = '%u'", mapid, gossip->Action );
- if(!result)
- return;
- do
- {
- fields = result->Fetch();
- x=fields[0].GetFloat();
- y=fields[1].GetFloat();
- pflag=map->GetAreaFlag(GetPositionX(),GetPositionY());
- if(pflag==areaflag)
- {
- findnpc=true;
- break;
- }
- }while(result->NextRow());
-
- delete result;
-
- if(!findnpc)
- {
- player->PlayerTalkClass->SendTalking( "$NSorry", "Here no this person.");
- return;
- }*/
-
- //need add more case.
- switch(gossip->Action)
- {
- case GOSSIP_GUARD_BANK:
- icon=ICON_POI_HOUSE;
- break;
- case GOSSIP_GUARD_RIDE:
- icon=ICON_POI_RWHORSE;
- break;
- case GOSSIP_GUARD_GUILD:
- icon=ICON_POI_BLUETOWER;
- break;
- default:
- icon=ICON_POI_TOWER;
- break;
- }
- uint32 textid=GetGossipTextId( gossip->Action, zoneid );
- player->PlayerTalkClass->SendTalking( textid );
- // how this could worked player->PlayerTalkClass->SendPointOfInterest( x, y, icon, 2, 15, areaname.c_str() );
- }
-}
-
-uint32 Creature::GetGossipTextId(uint32 action, uint32 zoneid)
-{
- QueryResult *result= WorldDatabase.PQuery("SELECT textid FROM npc_gossip_textid WHERE action = '%u' AND zoneid ='%u'", action, zoneid );
-
- if(!result)
- return 0;
-
- Field *fields = result->Fetch();
- uint32 id = fields[0].GetUInt32();
-
- delete result;
-
- return id;
-}
-
-uint32 Creature::GetNpcTextId()
-{
- if (!m_DBTableGuid)
- return DEFAULT_GOSSIP_MESSAGE;
-
- if(uint32 pos = objmgr.GetNpcGossip(m_DBTableGuid))
- return pos;
-
- return DEFAULT_GOSSIP_MESSAGE;
-}
-
-GossipOption const* Creature::GetGossipOption( uint32 id ) const
-{
- for( GossipOptionList::const_iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
- {
- if(i->Action==id )
- return &*i;
- }
- return NULL;
-}
-
-void Creature::LoadGossipOptions()
-{
- if(m_gossipOptionLoaded)
- return;
-
- uint32 npcflags=GetUInt32Value(UNIT_NPC_FLAGS);
-
- QueryResult *result = WorldDatabase.PQuery( "SELECT id,gossip_id,npcflag,icon,action,option_text FROM npc_option WHERE (npcflag & %u)<>0", npcflags );
-
- if(!result)
- return;
-
- GossipOption go;
- do
- {
- Field *fields = result->Fetch();
- go.Id= fields[0].GetUInt32();
- go.GossipId = fields[1].GetUInt32();
- go.NpcFlag=fields[2].GetUInt32();
- go.Icon=fields[3].GetUInt32();
- go.Action=fields[4].GetUInt32();
- go.Option=fields[5].GetCppString();
- addGossipOption(go);
- }while( result->NextRow() );
- delete result;
-
- m_gossipOptionLoaded = true;
-}
-
-void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type)
-{
- /* uint32 timeElap = getMSTime();
- if ((timeElap - m_startMove) < m_moveTime)
- {
- oX = (dX - oX) * ( (timeElap - m_startMove) / m_moveTime );
- oY = (dY - oY) * ( (timeElap - m_startMove) / m_moveTime );
- }
- else
- {
- oX = dX;
- oY = dY;
- }
-
- dX = x;
- dY = y;
- m_orientation = atan2((oY - dY), (oX - dX));
-
- m_startMove = getMSTime();
- m_moveTime = time;*/
- SendMonsterMove(x, y, z, type, MovementFlags, time);
-}
-
-Player *Creature::GetLootRecipient() const
-{
- if (!m_lootRecipient) return NULL;
- else return ObjectAccessor::FindPlayer(m_lootRecipient);
-}
-
-void Creature::SetLootRecipient(Unit *unit)
-{
- // set the player whose group should receive the right
- // to loot the creature after it dies
- // should be set to NULL after the loot disappears
-
- if (!unit)
- {
- m_lootRecipient = 0;
- RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_OTHER_TAGGER);
- return;
- }
-
- Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself();
- if(!player) // normal creature, no player involved
- return;
-
- m_lootRecipient = player->GetGUID();
- SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_OTHER_TAGGER);
-}
-
-void Creature::SaveToDB()
-{
- // this should only be used when the creature has already been loaded
- // perferably after adding to map, because mapid may not be valid otherwise
- CreatureData const *data = objmgr.GetCreatureData(m_DBTableGuid);
- if(!data)
- {
- sLog.outError("Creature::SaveToDB failed, cannot get creature data!");
- return;
- }
-
- SaveToDB(GetMapId(), data->spawnMask);
-}
-
-void Creature::SaveToDB(uint32 mapid, uint8 spawnMask)
-{
- // update in loaded data
- if (!m_DBTableGuid)
- m_DBTableGuid = GetGUIDLow();
- CreatureData& data = objmgr.NewOrExistCreatureData(m_DBTableGuid);
-
- uint32 displayId = GetNativeDisplayId();
-
- // check if it's a custom model and if not, use 0 for displayId
- CreatureInfo const *cinfo = GetCreatureInfo();
- if(cinfo)
- {
- if(displayId != cinfo->DisplayID_A && displayId != cinfo->DisplayID_H)
- {
- CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(cinfo->DisplayID_A);
- if(!minfo || displayId != minfo->modelid_other_gender)
- {
- minfo = objmgr.GetCreatureModelInfo(cinfo->DisplayID_H);
- if(minfo && displayId == minfo->modelid_other_gender)
- displayId = 0;
- }
- else
- displayId = 0;
- }
- else
- displayId = 0;
- }
-
- // data->guid = guid don't must be update at save
- data.id = GetEntry();
- data.mapid = mapid;
- data.displayid = displayId;
- data.equipmentId = GetEquipmentId();
- data.posX = GetPositionX();
- data.posY = GetPositionY();
- data.posZ = GetPositionZ();
- data.orientation = GetOrientation();
- data.spawntimesecs = m_respawnDelay;
- // prevent add data integrity problems
- data.spawndist = GetDefaultMovementType()==IDLE_MOTION_TYPE ? 0 : m_respawnradius;
- data.currentwaypoint = 0;
- data.curhealth = GetHealth();
- data.curmana = GetPower(POWER_MANA);
- data.is_dead = m_isDeadByDefault;
- // prevent add data integrity problems
- data.movementType = !m_respawnradius && GetDefaultMovementType()==RANDOM_MOTION_TYPE
- ? IDLE_MOTION_TYPE : GetDefaultMovementType();
- data.spawnMask = spawnMask;
-
- // updated in DB
- WorldDatabase.BeginTransaction();
-
- WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid);
-
- std::ostringstream ss;
- ss << "INSERT INTO creature VALUES ("
- << m_DBTableGuid << ","
- << GetEntry() << ","
- << mapid <<","
- << (uint32)spawnMask << ","
- << displayId <<","
- << GetEquipmentId() <<","
- << GetPositionX() << ","
- << GetPositionY() << ","
- << GetPositionZ() << ","
- << GetOrientation() << ","
- << m_respawnDelay << "," //respawn time
- << (float) m_respawnradius << "," //spawn distance (float)
- << (uint32) (0) << "," //currentwaypoint
- << GetHealth() << "," //curhealth
- << GetPower(POWER_MANA) << "," //curmana
- << (m_isDeadByDefault ? 1 : 0) << "," //is_dead
- << GetDefaultMovementType() << ")"; //default movement generator type
-
- WorldDatabase.PExecuteLog( ss.str( ).c_str( ) );
-
- WorldDatabase.CommitTransaction();
-}
-
-void Creature::SelectLevel(const CreatureInfo *cinfo)
-{
- uint32 rank = isPet()? 0 : cinfo->rank;
-
- // level
- uint32 minlevel = std::min(cinfo->maxlevel, cinfo->minlevel);
- uint32 maxlevel = std::max(cinfo->maxlevel, cinfo->minlevel);
- uint32 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel);
- SetLevel(level);
-
- float rellevel = maxlevel == minlevel ? 0 : (float(level - minlevel))/(maxlevel - minlevel);
-
- // health
- float healthmod = _GetHealthMod(rank);
-
- uint32 minhealth = std::min(cinfo->maxhealth, cinfo->minhealth);
- uint32 maxhealth = std::max(cinfo->maxhealth, cinfo->minhealth);
- uint32 health = uint32(healthmod * (minhealth + uint32(rellevel*(maxhealth - minhealth))));
-
- SetCreateHealth(health);
- SetMaxHealth(health);
- SetHealth(health);
-
- // mana
- uint32 minmana = std::min(cinfo->maxmana, cinfo->minmana);
- uint32 maxmana = std::max(cinfo->maxmana, cinfo->minmana);
- uint32 mana = minmana + uint32(rellevel*(maxmana - minmana));
-
- SetCreateMana(mana);
- SetMaxPower(POWER_MANA, mana); //MAX Mana
- SetPower(POWER_MANA, mana);
-
- SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, health);
- SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, mana);
-
- // damage
- float damagemod = _GetDamageMod(rank);
-
- SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, cinfo->mindmg * damagemod);
- SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, cinfo->maxdmg * damagemod);
-
- SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,cinfo->minrangedmg * damagemod);
- SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,cinfo->maxrangedmg * damagemod);
-
- SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, cinfo->attackpower * damagemod);
-}
-
-float Creature::_GetHealthMod(int32 Rank)
-{
- switch (Rank) // define rates for each elite rank
- {
- case CREATURE_ELITE_NORMAL:
- return sWorld.getRate(RATE_CREATURE_NORMAL_HP);
- case CREATURE_ELITE_ELITE:
- return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
- case CREATURE_ELITE_RAREELITE:
- return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_HP);
- case CREATURE_ELITE_WORLDBOSS:
- return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_HP);
- case CREATURE_ELITE_RARE:
- return sWorld.getRate(RATE_CREATURE_ELITE_RARE_HP);
- default:
- return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
- }
-}
-
-float Creature::_GetDamageMod(int32 Rank)
-{
- switch (Rank) // define rates for each elite rank
- {
- case CREATURE_ELITE_NORMAL:
- return sWorld.getRate(RATE_CREATURE_NORMAL_DAMAGE);
- case CREATURE_ELITE_ELITE:
- return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
- case CREATURE_ELITE_RAREELITE:
- return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_DAMAGE);
- case CREATURE_ELITE_WORLDBOSS:
- return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE);
- case CREATURE_ELITE_RARE:
- return sWorld.getRate(RATE_CREATURE_ELITE_RARE_DAMAGE);
- default:
- return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
- }
-}
-
-float Creature::GetSpellDamageMod(int32 Rank)
-{
- switch (Rank) // define rates for each elite rank
- {
- case CREATURE_ELITE_NORMAL:
- return sWorld.getRate(RATE_CREATURE_NORMAL_SPELLDAMAGE);
- case CREATURE_ELITE_ELITE:
- return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
- case CREATURE_ELITE_RAREELITE:
- return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE);
- case CREATURE_ELITE_WORLDBOSS:
- return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE);
- case CREATURE_ELITE_RARE:
- return sWorld.getRate(RATE_CREATURE_ELITE_RARE_SPELLDAMAGE);
- default:
- return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
- }
-}
-
-bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 team, const CreatureData *data)
-{
- CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(Entry);
- if(!cinfo)
- {
- sLog.outErrorDb("Error: creature entry %u does not exist.", Entry);
- return false;
- }
- m_originalEntry = Entry;
-
- Object::_Create(guidlow, Entry, HIGHGUID_UNIT);
-
- if(!UpdateEntry(Entry, team, data))
- return false;
-
- //Notify the map's instance data.
- //Only works if you create the object in it, not if it is moves to that map.
- //Normally non-players do not teleport to other maps.
- Map *map = MapManager::Instance().FindMap(GetMapId(), GetInstanceId());
- if(map && map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData())
- {
- ((InstanceMap*)map)->GetInstanceData()->OnCreatureCreate(this, Entry);
- }
-
- return true;
-}
-
-bool Creature::LoadFromDB(uint32 guid, Map *map)
-{
- CreatureData const* data = objmgr.GetCreatureData(guid);
-
- if(!data)
- {
- sLog.outErrorDb("Creature (GUID: %u) not found in table `creature`, can't load. ",guid);
- return false;
- }
-
- m_DBTableGuid = guid;
- if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT);
-
- uint16 team = 0;
- if(!Create(guid,map,data->id,team,data))
- return false;
-
- Relocate(data->posX,data->posY,data->posZ,data->orientation);
-
- if(!IsPositionValid())
- {
- sLog.outError("ERROR: Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",GetGUIDLow(),GetEntry(),GetPositionX(),GetPositionY());
- return false;
- }
-
- m_respawnradius = data->spawndist;
-
- m_respawnDelay = data->spawntimesecs;
- m_isDeadByDefault = data->is_dead;
- m_deathState = m_isDeadByDefault ? DEAD : ALIVE;
-
- m_respawnTime = objmgr.GetCreatureRespawnTime(m_DBTableGuid,GetInstanceId());
- if(m_respawnTime > time(NULL)) // not ready to respawn
- m_deathState = DEAD;
- else if(m_respawnTime) // respawn time set but expired
- {
- m_respawnTime = 0;
- objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
- }
-
- uint32 curhealth = data->curhealth;
- if(curhealth)
- {
- curhealth = uint32(curhealth*_GetHealthMod(GetCreatureInfo()->rank));
- if(curhealth < 1)
- curhealth = 1;
- }
-
- SetHealth(m_deathState == ALIVE ? curhealth : 0);
- SetPower(POWER_MANA,data->curmana);
-
- SetMeleeDamageSchool(SpellSchools(GetCreatureInfo()->dmgschool));
-
- // checked at creature_template loading
- m_defaultMovementType = MovementGeneratorType(data->movementType);
-
- AIM_Initialize();
- return true;
-}
-
-void Creature::LoadEquipment(uint32 equip_entry, bool force)
-{
- if(equip_entry == 0)
- {
- if (force)
- {
- for (uint8 i=0;i<3;i++)
- {
- SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i, 0);
- SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2), 0);
- SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 1, 0);
- }
- m_equipmentId = 0;
- }
- return;
- }
-
- EquipmentInfo const *einfo = objmgr.GetEquipmentInfo(equip_entry);
- if (!einfo)
- return;
-
- m_equipmentId = equip_entry;
- for (uint8 i=0;i<3;i++)
- {
- SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i, einfo->equipmodel[i]);
- SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2), einfo->equipinfo[i]);
- SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 1, einfo->equipslot[i]);
- }
-}
-
-bool Creature::hasQuest(uint32 quest_id) const
-{
- QuestRelations const& qr = objmgr.mCreatureQuestRelations;
- for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
- {
- if(itr->second==quest_id)
- return true;
- }
- return false;
-}
-
-bool Creature::hasInvolvedQuest(uint32 quest_id) const
-{
- QuestRelations const& qr = objmgr.mCreatureQuestInvolvedRelations;
- for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
- {
- if(itr->second==quest_id)
- return true;
- }
- return false;
-}
-
-void Creature::DeleteFromDB()
-{
- if (!m_DBTableGuid)
- {
- sLog.outDebug("Trying to delete not saved creature!");
- return;
- }
-
- objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
- objmgr.DeleteCreatureData(m_DBTableGuid);
-
- WorldDatabase.BeginTransaction();
- WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid);
- WorldDatabase.PExecuteLog("DELETE FROM creature_addon WHERE guid = '%u'", m_DBTableGuid);
- WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id = '%u'", m_DBTableGuid);
- WorldDatabase.PExecuteLog("DELETE FROM game_event_creature WHERE guid = '%u'", m_DBTableGuid);
- WorldDatabase.PExecuteLog("DELETE FROM game_event_model_equip WHERE guid = '%u'", m_DBTableGuid);
- WorldDatabase.CommitTransaction();
-}
-
-float Creature::GetAttackDistance(Unit const* pl) const
-{
- float aggroRate = sWorld.getRate(RATE_CREATURE_AGGRO);
- if(aggroRate==0)
- return 0.0f;
-
- int32 playerlevel = pl->getLevelForTarget(this);
- int32 creaturelevel = getLevelForTarget(pl);
-
- int32 leveldif = playerlevel - creaturelevel;
-
- // "The maximum Aggro Radius has a cap of 25 levels under. Example: A level 30 char has the same Aggro Radius of a level 5 char on a level 60 mob."
- if ( leveldif < - 25)
- leveldif = -25;
-
- // "The aggro radius of a mob having the same level as the player is roughly 20 yards"
- float RetDistance = 20;
-
- // "Aggro Radius varries with level difference at a rate of roughly 1 yard/level"
- // radius grow if playlevel < creaturelevel
- RetDistance -= (float)leveldif;
-
- if(creaturelevel+5 <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- {
- // detect range auras
- RetDistance += GetTotalAuraModifier(SPELL_AURA_MOD_DETECT_RANGE);
-
- // detected range auras
- RetDistance += pl->GetTotalAuraModifier(SPELL_AURA_MOD_DETECTED_RANGE);
- }
-
- // "Minimum Aggro Radius for a mob seems to be combat range (5 yards)"
- if(RetDistance < 5)
- RetDistance = 5;
-
- return (RetDistance*aggroRate);
-}
-
-void Creature::setDeathState(DeathState s)
-{
- if((s == JUST_DIED && !m_isDeadByDefault)||(s == JUST_ALIVED && m_isDeadByDefault))
- {
- m_deathTimer = m_corpseDelay*1000;
-
- // always save boss respawn time at death to prevent crash cheating
- if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY) || isWorldBoss())
- SaveRespawnTime();
-
- if(!IsStopped())
- StopMoving();
- }
- Unit::setDeathState(s);
-
- if(s == JUST_DIED)
- {
- SetUInt64Value (UNIT_FIELD_TARGET,0); // remove target selection in any cases (can be set at aura remove in Unit::setDeathState)
- SetUInt32Value(UNIT_NPC_FLAGS, 0);
-
- if(!isPet() && GetCreatureInfo()->SkinLootId)
- if ( LootTemplates_Skinning.HaveLootFor(GetCreatureInfo()->SkinLootId) )
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
-
- Unit::setDeathState(CORPSE);
- }
- if(s == JUST_ALIVED)
- {
- SetHealth(GetMaxHealth());
- SetLootRecipient(NULL);
- Unit::setDeathState(ALIVE);
- CreatureInfo const *cinfo = GetCreatureInfo();
- SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
- RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
- AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
- SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag);
- clearUnitState(UNIT_STAT_ALL_STATE);
- i_motionMaster.Clear();
- SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
- LoadCreaturesAddon(true);
- }
-}
-
-void Creature::Respawn()
-{
- RemoveCorpse();
-
- // forced recreate creature object at clients
- UnitVisibility currentVis = GetVisibility();
- SetVisibility(VISIBILITY_RESPAWN);
- ObjectAccessor::UpdateObjectVisibility(this);
- SetVisibility(currentVis); // restore visibility state
- ObjectAccessor::UpdateObjectVisibility(this);
-
- if(getDeathState()==DEAD)
- {
- if (m_DBTableGuid)
- objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
- m_respawnTime = time(NULL); // respawn at next tick
- }
-}
-
-bool Creature::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
-{
- if (!spellInfo)
- return false;
-
- if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))
- return true;
-
- return Unit::IsImmunedToSpell(spellInfo, useCharges);
-}
-
-bool Creature::IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const
-{
- if (GetCreatureInfo()->MechanicImmuneMask & (1 << (mechanic-1)))
- return true;
-
- return Unit::IsImmunedToSpellEffect(effect, mechanic);
-}
-
-SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim)
-{
- if(!pVictim)
- return NULL;
-
- for(uint32 i=0; i < CREATURE_MAX_SPELLS; i++)
- {
- if(!m_spells[i])
- continue;
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i] );
- if(!spellInfo)
- {
- sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]);
- continue;
- }
-
- bool bcontinue = true;
- for(uint32 j=0;j<3;j++)
- {
- if( (spellInfo->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE ) ||
- (spellInfo->Effect[j] == SPELL_EFFECT_INSTAKILL) ||
- (spellInfo->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE) ||
- (spellInfo->Effect[j] == SPELL_EFFECT_HEALTH_LEECH )
- )
- {
- bcontinue = false;
- break;
- }
- }
- if(bcontinue) continue;
-
- if(spellInfo->manaCost > GetPower(POWER_MANA))
- continue;
- SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
- float range = GetSpellMaxRange(srange);
- float minrange = GetSpellMinRange(srange);
- float dist = GetDistance(pVictim);
- //if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
- // continue;
- if( dist > range || dist < minrange )
- continue;
- if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
- continue;
- return spellInfo;
- }
- return NULL;
-}
-
-SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim)
-{
- if(!pVictim)
- return NULL;
-
- for(uint32 i=0; i < CREATURE_MAX_SPELLS; i++)
- {
- if(!m_spells[i])
- continue;
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i] );
- if(!spellInfo)
- {
- sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]);
- continue;
- }
-
- bool bcontinue = true;
- for(uint32 j=0;j<3;j++)
- {
- if( (spellInfo->Effect[j] == SPELL_EFFECT_HEAL ) )
- {
- bcontinue = false;
- break;
- }
- }
- if(bcontinue) continue;
-
- if(spellInfo->manaCost > GetPower(POWER_MANA))
- continue;
- SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
- float range = GetSpellMaxRange(srange);
- float minrange = GetSpellMinRange(srange);
- float dist = GetDistance(pVictim);
- //if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
- // continue;
- if( dist > range || dist < minrange )
- continue;
- if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
- continue;
- return spellInfo;
- }
- return NULL;
-}
-
-bool Creature::IsVisibleInGridForPlayer(Player* pl) const
-{
- // gamemaster in GM mode see all, including ghosts
- if(pl->isGameMaster())
- return true;
-
- // Live player (or with not release body see live creatures or death creatures with corpse disappearing time > 0
- if(pl->isAlive() || pl->GetDeathTimer() > 0)
- {
- if( GetEntry() == VISUAL_WAYPOINT && !pl->isGameMaster() )
- return false;
- return isAlive() || m_deathTimer > 0 || m_isDeadByDefault && m_deathState==CORPSE;
- }
-
- // Dead player see live creatures near own corpse
- if(isAlive())
- {
- Corpse *corpse = pl->GetCorpse();
- if(corpse)
- {
- // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level
- if(corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO)))
- return true;
- }
- }
-
- // Dead player see Spirit Healer or Spirit Guide
- if(isSpiritService())
- return true;
-
- // and not see any other
- return false;
-}
-
-void Creature::DoFleeToGetAssistance(float radius) // Optional parameter
-{
- if (!getVictim())
- return;
-
- Creature* pCreature = NULL;
-
- CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- MaNGOS::NearestAssistCreatureInCreatureRangeCheck u_check(this,getVictim(),radius);
- MaNGOS::CreatureLastSearcher<MaNGOS::NearestAssistCreatureInCreatureRangeCheck> searcher(pCreature, u_check);
-
- TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestAssistCreatureInCreatureRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, grid_creature_searcher, *(GetMap()));
-
- if(!GetMotionMaster()->empty() && (GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE))
- GetMotionMaster()->Clear(false);
- if(pCreature == NULL)
- {
- GetMotionMaster()->MoveIdle();
- GetMotionMaster()->MoveFleeing(getVictim());
- }
- else
- {
- GetMotionMaster()->MoveIdle();
- GetMotionMaster()->MovePoint(0,pCreature->GetPositionX(),pCreature->GetPositionY(),pCreature->GetPositionZ());
- }
-}
-
-void Creature::CallAssistence()
-{
- if( !m_AlreadyCallAssistence && getVictim() && !isPet() && !isCharmed())
- {
- SetNoCallAssistence(true);
-
- float radius = sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS);
- if(radius > 0)
- {
- std::list<Creature*> assistList;
-
- {
- CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- MaNGOS::AnyAssistCreatureInRangeCheck u_check(this, getVictim(), radius);
- MaNGOS::CreatureListSearcher<MaNGOS::AnyAssistCreatureInRangeCheck> searcher(assistList, u_check);
-
- TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::AnyAssistCreatureInRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, grid_creature_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
- }
-
- for(std::list<Creature*>::iterator iter = assistList.begin(); iter != assistList.end(); ++iter)
- {
- (*iter)->SetNoCallAssistence(true);
- if((*iter)->AI())
- (*iter)->AI()->AttackStart(getVictim());
- }
- }
- }
-}
-
-void Creature::SaveRespawnTime()
-{
- if(isPet() || !m_DBTableGuid)
- return;
-
- if(m_respawnTime > time(NULL)) // dead (no corpse)
- objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime);
- else if(m_deathTimer > 0) // dead (corpse)
- objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),time(NULL)+m_respawnDelay+m_deathTimer/1000);
-}
-
-bool Creature::IsOutOfThreatArea(Unit* pVictim) const
-{
- if(!pVictim)
- return true;
-
- if(!pVictim->IsInMap(this))
- return true;
-
- if(!pVictim->isTargetableForAttack())
- return true;
-
- if(!pVictim->isInAccessablePlaceFor(this))
- return true;
-
- if(sMapStore.LookupEntry(GetMapId())->Instanceable())
- return false;
-
- float length = pVictim->GetDistance(CombatStartX,CombatStartY,CombatStartZ);
- float AttackDist = GetAttackDistance(pVictim);
- uint32 ThreatRadius = sWorld.getConfig(CONFIG_THREAT_RADIUS);
-
- //Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and ouf of combat every update tick.
- return ( length > (ThreatRadius > AttackDist ? ThreatRadius : AttackDist));
-}
-
-CreatureDataAddon const* Creature::GetCreatureAddon() const
-{
- if (m_DBTableGuid)
- {
- if(CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(m_DBTableGuid))
- return addon;
- }
-
- // dependent from heroic mode entry
- return ObjectMgr::GetCreatureTemplateAddon(GetCreatureInfo()->Entry);
-}
-
-//creature_addon table
-bool Creature::LoadCreaturesAddon(bool reload)
-{
- CreatureDataAddon const *cainfo = GetCreatureAddon();
- if(!cainfo)
- return false;
-
- if (cainfo->mount != 0)
- Mount(cainfo->mount);
-
- if (cainfo->bytes0 != 0)
- SetUInt32Value(UNIT_FIELD_BYTES_0, cainfo->bytes0);
-
- if (cainfo->bytes1 != 0)
- SetUInt32Value(UNIT_FIELD_BYTES_1, cainfo->bytes1);
-
- if (cainfo->bytes2 != 0)
- SetUInt32Value(UNIT_FIELD_BYTES_2, cainfo->bytes2);
-
- if (cainfo->emote != 0)
- SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote);
-
- if (cainfo->move_flags != 0)
- SetUnitMovementFlags(cainfo->move_flags);
-
- if(cainfo->auras)
- {
- for (CreatureDataAddonAura const* cAura = cainfo->auras; cAura->spell_id; ++cAura)
- {
- SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura->spell_id);
- if (!AdditionalSpellInfo)
- {
- sLog.outErrorDb("Creature (GUIDLow: %u Entry: %u ) has wrong spell %u defined in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id);
- continue;
- }
-
- // skip already applied aura
- if(HasAura(cAura->spell_id,cAura->effect_idx))
- {
- if(!reload)
- sLog.outErrorDb("Creature (GUIDLow: %u Entry: %u ) has duplicate aura (spell %u effect %u) in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id,cAura->effect_idx);
-
- continue;
- }
-
- Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, cAura->effect_idx, NULL, this, this, 0);
- AddAura(AdditionalAura);
- sLog.outDebug("Spell: %u with Aura %u added to creature (GUIDLow: %u Entry: %u )", cAura->spell_id, AdditionalSpellInfo->EffectApplyAuraName[0],GetGUIDLow(),GetEntry());
- }
- }
- return true;
-}
-
-/// Send a message to LocalDefense channel for players oposition team in the zone
-void Creature::SendZoneUnderAttackMessage(Player* attacker)
-{
- uint32 enemy_team = attacker->GetTeam();
-
- WorldPacket data(SMSG_ZONE_UNDER_ATTACK,4);
- data << (uint32)GetZoneId();
- sWorld.SendGlobalMessage(&data,NULL,(enemy_team==ALLIANCE ? HORDE : ALLIANCE));
-}
-
-void Creature::_AddCreatureSpellCooldown(uint32 spell_id, time_t end_time)
-{
- m_CreatureSpellCooldowns[spell_id] = end_time;
-}
-
-void Creature::_AddCreatureCategoryCooldown(uint32 category, time_t apply_time)
-{
- m_CreatureCategoryCooldowns[category] = apply_time;
-}
-
-void Creature::AddCreatureSpellCooldown(uint32 spellid)
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
- if(!spellInfo)
- return;
-
- uint32 cooldown = GetSpellRecoveryTime(spellInfo);
- if(cooldown)
- _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/1000);
-
- if(spellInfo->Category)
- _AddCreatureCategoryCooldown(spellInfo->Category, time(NULL));
-
- m_GlobalCooldown = spellInfo->StartRecoveryTime;
-}
-
-bool Creature::HasCategoryCooldown(uint32 spell_id) const
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
- if(!spellInfo)
- return false;
-
- // check global cooldown if spell affected by it
- if (spellInfo->StartRecoveryCategory > 0 && m_GlobalCooldown > 0)
- return true;
-
- CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->Category);
- return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / 1000)) > time(NULL));
-}
-
-bool Creature::HasSpellCooldown(uint32 spell_id) const
-{
- CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id);
- return (itr != m_CreatureSpellCooldowns.end() && itr->second > time(NULL)) || HasCategoryCooldown(spell_id);
-}
-
-bool Creature::IsInEvadeMode() const
-{
- return !i_motionMaster.empty() && i_motionMaster.GetCurrentMovementGeneratorType() == HOME_MOTION_TYPE;
-}
-
-bool Creature::HasSpell(uint32 spellID) const
-{
- uint8 i;
- for(i = 0; i < CREATURE_MAX_SPELLS; ++i)
- if(spellID == m_spells[i])
- break;
- return i < CREATURE_MAX_SPELLS; //broke before end of iteration of known spells
-}
-
-time_t Creature::GetRespawnTimeEx() const
-{
- time_t now = time(NULL);
- if(m_respawnTime > now) // dead (no corpse)
- return m_respawnTime;
- else if(m_deathTimer > 0) // dead (corpse)
- return now+m_respawnDelay+m_deathTimer/1000;
- else
- return now;
-}
-
-void Creature::GetRespawnCoord( float &x, float &y, float &z, float* ori, float* dist ) const
-{
- if (m_DBTableGuid)
- {
- 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()
-{
- if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE))
- {
- uint32 nDeathTimer;
-
- CreatureInfo const *cinfo = GetCreatureInfo();
-
- // corpse was not skinnable -> apply corpse looted timer
- if (!cinfo || !cinfo->SkinLootId)
- nDeathTimer = (uint32)((m_corpseDelay * 1000) * sWorld.getRate(RATE_CORPSE_DECAY_LOOTED));
- // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
- else
- nDeathTimer = 0;
-
- // update death timer only if looted timer is shorter
- if (m_deathTimer > nDeathTimer)
- m_deathTimer = nDeathTimer;
- }
-}
-
-uint32 Creature::getLevelForTarget( Unit const* target ) const
-{
- if(!isWorldBoss())
- return Unit::getLevelForTarget(target);
-
- uint32 level = target->getLevel()+sWorld.getConfig(CONFIG_WORLD_BOSS_LEVEL_DIFF);
- if(level < 1)
- return 1;
- if(level > 255)
- return 255;
- return level;
-}
-
-char const* Creature::GetScriptName() const
-{
- return ObjectMgr::GetCreatureTemplate(GetEntry())->ScriptName;
-}
-
-
-VendorItemData const* Creature::GetVendorItems() const
-{
- return objmgr.GetNpcVendorItemList(GetEntry());
-}
-
-uint32 Creature::GetVendorItemCurrentCount(VendorItem const* vItem)
-{
- if(!vItem->maxcount)
- return vItem->maxcount;
-
- VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
- for(; itr != m_vendorItemCounts.end(); ++itr)
- if(itr->itemId==vItem->item)
- break;
-
- if(itr == m_vendorItemCounts.end())
- return vItem->maxcount;
-
- VendorItemCount* vCount = &*itr;
-
- time_t ptime = time(NULL);
-
- if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
- {
- ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
-
- uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
- if((vCount->count + diff * pProto->BuyCount) >= vItem->maxcount )
- {
- m_vendorItemCounts.erase(itr);
- return vItem->maxcount;
- }
-
- vCount->count += diff * pProto->BuyCount;
- vCount->lastIncrementTime = ptime;
- }
-
- return vCount->count;
-}
-
-uint32 Creature::UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count)
-{
- if(!vItem->maxcount)
- return 0;
-
- VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
- for(; itr != m_vendorItemCounts.end(); ++itr)
- if(itr->itemId==vItem->item)
- break;
-
- if(itr == m_vendorItemCounts.end())
- {
- uint32 new_count = vItem->maxcount > used_count ? vItem->maxcount-used_count : 0;
- m_vendorItemCounts.push_back(VendorItemCount(vItem->item,new_count));
- return new_count;
- }
-
- VendorItemCount* vCount = &*itr;
-
- time_t ptime = time(NULL);
-
- if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
- {
- ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
-
- uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
- if((vCount->count + diff * pProto->BuyCount) < vItem->maxcount )
- vCount->count += diff * pProto->BuyCount;
- else
- vCount->count = vItem->maxcount;
- }
-
- vCount->count = vCount->count > used_count ? vCount->count-used_count : 0;
- vCount->lastIncrementTime = ptime;
- return vCount->count;
-}
-
-TrainerSpellData const* Creature::GetTrainerSpells() const
-{
- return objmgr.GetNpcTrainerSpells(GetEntry());
-}
+/*
+ * 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 "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Creature.h"
+#include "QuestDef.h"
+#include "GossipDef.h"
+#include "Player.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "LootMgr.h"
+#include "MapManager.h"
+#include "CreatureAI.h"
+#include "CreatureAISelector.h"
+#include "Formulas.h"
+#include "SpellAuras.h"
+#include "WaypointMovementGenerator.h"
+#include "InstanceData.h"
+#include "BattleGround.h"
+#include "Util.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+
+// apply implementation of the singletons
+#include "Policies/SingletonImp.h"
+
+void TrainerSpellData::Clear()
+{
+ for (TrainerSpellList::iterator itr = spellList.begin(); itr != spellList.end(); ++itr)
+ delete (*itr);
+ spellList.empty();
+}
+
+TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const
+{
+ for(TrainerSpellList::const_iterator itr = spellList.begin(); itr != spellList.end(); ++itr)
+ if((*itr)->spell == spell_id)
+ return *itr;
+
+ return NULL;
+}
+
+bool VendorItemData::RemoveItem( uint32 item_id )
+{
+ for(VendorItemList::iterator i = m_items.begin(); i != m_items.end(); ++i )
+ {
+ if((*i)->item==item_id)
+ {
+ m_items.erase(i);
+ return true;
+ }
+ }
+ 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 )
+ if((*i)->item==item_id)
+ return *i;
+ return NULL;
+}
+
+Creature::Creature() :
+Unit(), i_AI(NULL),
+lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0),
+m_lootMoney(0), m_lootRecipient(0),
+m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(0.0f),
+m_gossipOptionLoaded(false),m_emoteState(0), m_isPet(false), m_isTotem(false),
+m_regenTimer(2000), m_defaultMovementType(IDLE_MOTION_TYPE), m_equipmentId(0),
+m_AlreadyCallAssistence(false), m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false),
+m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),m_creatureInfo(NULL), m_DBTableGuid(0)
+{
+ m_valuesCount = UNIT_END;
+
+ for(int i =0; i<4; ++i)
+ m_spells[i] = 0;
+
+ m_CreatureSpellCooldowns.clear();
+ m_CreatureCategoryCooldowns.clear();
+ m_GlobalCooldown = 0;
+ m_unit_movement_flags = MOVEMENTFLAG_WALK_MODE;
+}
+
+Creature::~Creature()
+{
+ CleanupsBeforeDelete();
+
+ m_vendorItemCounts.clear();
+
+ delete i_AI;
+ i_AI = NULL;
+}
+
+void Creature::AddToWorld()
+{
+ ///- Register the creature for guid lookup
+ if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
+ Unit::AddToWorld();
+}
+
+void Creature::RemoveFromWorld()
+{
+ ///- Remove the creature from the accessor
+ if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
+ Unit::RemoveFromWorld();
+}
+
+void Creature::RemoveCorpse()
+{
+ if( getDeathState()!=CORPSE && !m_isDeadByDefault || getDeathState()!=ALIVE && m_isDeadByDefault )
+ return;
+
+ m_deathTimer = 0;
+ setDeathState(DEAD);
+ ObjectAccessor::UpdateObjectVisibility(this);
+ loot.clear();
+ m_respawnTime = time(NULL) + m_respawnDelay;
+
+ float x,y,z,o;
+ GetRespawnCoord(x, y, z, &o);
+ MapManager::Instance().GetMap(GetMapId(), this)->CreatureRelocation(this,x,y,z,o);
+}
+
+/**
+ * change the entry of creature until respawn
+ */
+bool Creature::InitEntry(uint32 Entry, uint32 team, const CreatureData *data )
+{
+ CreatureInfo const *normalInfo = objmgr.GetCreatureTemplate(Entry);
+ if(!normalInfo)
+ {
+ sLog.outErrorDb("Creature::UpdateEntry creature entry %u does not exist.", Entry);
+ return false;
+ }
+
+ // get heroic mode entry
+ uint32 actualEntry = Entry;
+ CreatureInfo const *cinfo = normalInfo;
+ if(normalInfo->HeroicEntry)
+ {
+ Map *map = MapManager::Instance().FindMap(GetMapId(), GetInstanceId());
+ if(map && map->IsHeroic())
+ {
+ cinfo = objmgr.GetCreatureTemplate(normalInfo->HeroicEntry);
+ if(!cinfo)
+ {
+ sLog.outErrorDb("Creature::UpdateEntry creature heroic entry %u does not exist.", actualEntry);
+ return false;
+ }
+ }
+ }
+
+ SetUInt32Value(OBJECT_FIELD_ENTRY, Entry); // normal entry always
+ m_creatureInfo = cinfo; // map mode related always
+
+ if (cinfo->DisplayID_A == 0 || cinfo->DisplayID_H == 0) // Cancel load if no model defined
+ {
+ sLog.outErrorDb("Creature (Entry: %u) has no model defined for Horde or Alliance in table `creature_template`, can't load. ",Entry);
+ return false;
+ }
+
+ uint32 display_id = objmgr.ChooseDisplayId(team, GetCreatureInfo(), data);
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
+ if (!minfo)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) has model %u not found in table `creature_model_info`, can't load. ", Entry, display_id);
+ return false;
+ }
+ else
+ display_id = minfo->modelid; // it can be different (for another gender)
+
+ SetDisplayId(display_id);
+ SetNativeDisplayId(display_id);
+ SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+
+ // Load creature equipment
+ if(!data || data->equipmentId == 0)
+ { // use default from the template
+ LoadEquipment(cinfo->equipmentId);
+ }
+ else if(data && data->equipmentId != -1)
+ { // override, -1 means no equipment
+ LoadEquipment(data->equipmentId);
+ }
+
+ SetName(normalInfo->Name); // at normal entry always
+
+ SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius);
+ SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach );
+
+ SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
+
+ SetSpeed(MOVE_WALK, cinfo->speed );
+ SetSpeed(MOVE_RUN, cinfo->speed );
+ SetSpeed(MOVE_SWIM, cinfo->speed );
+
+ SetFloatValue(OBJECT_FIELD_SCALE_X, cinfo->scale);
+
+ // checked at loading
+ m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
+ if(!m_respawnradius && m_defaultMovementType==RANDOM_MOTION_TYPE)
+ m_defaultMovementType = IDLE_MOTION_TYPE;
+
+ return true;
+}
+
+bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData *data )
+{
+ if(!InitEntry(Entry,team,data))
+ return false;
+
+ m_regenHealth = GetCreatureInfo()->RegenHealth;
+
+ // creatures always have melee weapon ready if any
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_AURAS );
+
+ SelectLevel(GetCreatureInfo());
+ if (team == HORDE)
+ SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetCreatureInfo()->faction_H);
+ else
+ SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, GetCreatureInfo()->faction_A);
+
+ SetUInt32Value(UNIT_NPC_FLAGS,GetCreatureInfo()->npcflag);
+
+ SetAttackTime(BASE_ATTACK, GetCreatureInfo()->baseattacktime);
+ SetAttackTime(OFF_ATTACK, GetCreatureInfo()->baseattacktime);
+ SetAttackTime(RANGED_ATTACK,GetCreatureInfo()->rangeattacktime);
+
+ SetUInt32Value(UNIT_FIELD_FLAGS,GetCreatureInfo()->Flags);
+ SetUInt32Value(UNIT_DYNAMIC_FLAGS,GetCreatureInfo()->dynamicflags);
+
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(GetCreatureInfo()->armor));
+ SetModifierValue(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(GetCreatureInfo()->resistance1));
+ SetModifierValue(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(GetCreatureInfo()->resistance2));
+ SetModifierValue(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(GetCreatureInfo()->resistance3));
+ SetModifierValue(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(GetCreatureInfo()->resistance4));
+ SetModifierValue(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(GetCreatureInfo()->resistance5));
+ SetModifierValue(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(GetCreatureInfo()->resistance6));
+
+ SetCanModifyStats(true);
+ UpdateAllStats();
+
+ FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(GetCreatureInfo()->faction_A);
+ if (factionTemplate) // check and error show at loading templates
+ {
+ FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->faction);
+ if (factionEntry)
+ if( !(GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN) &&
+ (factionEntry->team == ALLIANCE || factionEntry->team == HORDE) )
+ SetPvP(true);
+ }
+
+ m_spells[0] = GetCreatureInfo()->spell1;
+ m_spells[1] = GetCreatureInfo()->spell2;
+ m_spells[2] = GetCreatureInfo()->spell3;
+ m_spells[3] = GetCreatureInfo()->spell4;
+
+ return true;
+}
+
+void Creature::Update(uint32 diff)
+{
+ if(m_GlobalCooldown <= diff)
+ m_GlobalCooldown = 0;
+ else
+ m_GlobalCooldown -= diff;
+
+ switch( m_deathState )
+ {
+ case JUST_ALIVED:
+ // Dont must be called, see Creature::setDeathState JUST_ALIVED -> ALIVE promoting.
+ sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_ALIVED (4)",GetGUIDLow(),GetEntry());
+ break;
+ case JUST_DIED:
+ // Dont must be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting.
+ sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_DEAD (1)",GetGUIDLow(),GetEntry());
+ break;
+ case DEAD:
+ {
+ if( m_respawnTime <= time(NULL) )
+ {
+ DEBUG_LOG("Respawning...");
+ m_respawnTime = 0;
+ lootForPickPocketed = false;
+ lootForBody = false;
+
+ if(m_originalEntry != GetUInt32Value(OBJECT_FIELD_ENTRY))
+ UpdateEntry(m_originalEntry);
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+
+ SelectLevel(cinfo);
+ SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
+ if (m_isDeadByDefault)
+ {
+ setDeathState(JUST_DIED);
+ SetHealth(0);
+ i_motionMaster.Clear();
+ clearUnitState(UNIT_STAT_ALL_STATE);
+ LoadCreaturesAddon(true);
+ }
+ else
+ setDeathState( JUST_ALIVED );
+
+ //Call AI respawn virtual function
+ i_AI->JustRespawned();
+
+ MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
+ }
+ break;
+ }
+ case CORPSE:
+ {
+ if (m_isDeadByDefault)
+ break;
+
+ if( m_deathTimer <= diff )
+ {
+ RemoveCorpse();
+ DEBUG_LOG("Removing corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY));
+ }
+ else
+ {
+ m_deathTimer -= diff;
+ if (m_groupLootTimer && lootingGroupLeaderGUID)
+ {
+ if(diff <= m_groupLootTimer)
+ {
+ m_groupLootTimer -= diff;
+ }
+ else
+ {
+ Group* group = objmgr.GetGroupByLeader(lootingGroupLeaderGUID);
+ if (group)
+ group->EndRoll();
+ m_groupLootTimer = 0;
+ lootingGroupLeaderGUID = 0;
+ }
+ }
+ }
+
+ break;
+ }
+ case ALIVE:
+ {
+ if (m_isDeadByDefault)
+ {
+ if( m_deathTimer <= diff )
+ {
+ RemoveCorpse();
+ DEBUG_LOG("Removing alive corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY));
+ }
+ else
+ {
+ m_deathTimer -= diff;
+ }
+ }
+
+ Unit::Update( diff );
+
+ // creature can be dead after Unit::Update call
+ // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
+ if(!isAlive())
+ break;
+
+ if(!IsInEvadeMode())
+ {
+ // do not allow the AI to be changed during update
+ m_AI_locked = true;
+ i_AI->UpdateAI(diff);
+ m_AI_locked = false;
+ }
+
+ // creature can be dead after UpdateAI call
+ // CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
+ if(!isAlive())
+ break;
+ if(m_regenTimer > 0)
+ {
+ if(diff >= m_regenTimer)
+ m_regenTimer = 0;
+ else
+ m_regenTimer -= diff;
+ }
+ if (m_regenTimer != 0)
+ break;
+
+ if (!isInCombat() || IsPolymorphed())
+ RegenerateHealth();
+
+ RegenerateMana();
+
+ m_regenTimer = 2000;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void Creature::RegenerateMana()
+{
+ uint32 curValue = GetPower(POWER_MANA);
+ uint32 maxValue = GetMaxPower(POWER_MANA);
+
+ if (curValue >= maxValue)
+ return;
+
+ uint32 addvalue = 0;
+
+ // Combat and any controlled creature
+ if (isInCombat() || GetCharmerOrOwnerGUID())
+ {
+ if(!IsUnderLastManaUseEffect())
+ {
+ float ManaIncreaseRate = sWorld.getRate(RATE_POWER_MANA);
+ float Spirit = GetStat(STAT_SPIRIT);
+
+ addvalue = uint32((Spirit/5.0f + 17.0f) * ManaIncreaseRate);
+ }
+ }
+ else
+ addvalue = maxValue/3;
+
+ ModifyPower(POWER_MANA, addvalue);
+}
+
+void Creature::RegenerateHealth()
+{
+ if (!isRegeneratingHealth())
+ return;
+
+ uint32 curValue = GetHealth();
+ uint32 maxValue = GetMaxHealth();
+
+ if (curValue >= maxValue)
+ return;
+
+ uint32 addvalue = 0;
+
+ // Not only pet, but any controelled creature
+ if(GetCharmerOrOwnerGUID())
+ {
+ float HealthIncreaseRate = sWorld.getRate(RATE_HEALTH);
+ float Spirit = GetStat(STAT_SPIRIT);
+
+ if( GetPower(POWER_MANA) > 0 )
+ addvalue = uint32(Spirit * 0.25 * HealthIncreaseRate);
+ else
+ addvalue = uint32(Spirit * 0.80 * HealthIncreaseRate);
+ }
+ else
+ addvalue = maxValue/3;
+
+ ModifyHealth(addvalue);
+}
+
+bool Creature::AIM_Initialize()
+{
+ // make sure nothing can change the AI during AI update
+ if(m_AI_locked)
+ {
+ sLog.outDebug("AIM_Initialize: failed to init, locked.");
+ return false;
+ }
+
+ CreatureAI * oldAI = i_AI;
+ i_motionMaster.Initialize();
+ i_AI = FactorySelector::selectAI(this);
+ if (oldAI)
+ delete oldAI;
+ return true;
+}
+
+bool Creature::Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, const CreatureData *data)
+{
+ SetMapId(map->GetId());
+ SetInstanceId(map->GetInstanceId());
+
+ //oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0;
+ const bool bResult = CreateFromProto(guidlow, Entry, team, data);
+
+ if (bResult)
+ {
+ switch (GetCreatureInfo()->rank)
+ {
+ case CREATURE_ELITE_RARE:
+ m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_RARE);
+ break;
+ case CREATURE_ELITE_ELITE:
+ m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_ELITE);
+ break;
+ case CREATURE_ELITE_RAREELITE:
+ m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_RAREELITE);
+ break;
+ case CREATURE_ELITE_WORLDBOSS:
+ m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_WORLDBOSS);
+ break;
+ default:
+ m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_NORMAL);
+ break;
+ }
+ LoadCreaturesAddon();
+ }
+
+ return bResult;
+}
+
+bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const
+{
+ if(!isTrainer())
+ return false;
+
+ TrainerSpellData const* trainer_spells = GetTrainerSpells();
+
+
+ if(!trainer_spells || trainer_spells->spellList.empty())
+ {
+ sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_TRAINER but have empty trainer spell list.",
+ GetGUIDLow(),GetEntry());
+ return false;
+ }
+
+ switch(GetCreatureInfo()->trainer_type)
+ {
+ case TRAINER_TYPE_CLASS:
+ if(pPlayer->getClass()!=GetCreatureInfo()->classNum)
+ {
+ if(msg)
+ {
+ pPlayer->PlayerTalkClass->ClearMenus();
+ switch(GetCreatureInfo()->classNum)
+ {
+ case CLASS_DRUID: pPlayer->PlayerTalkClass->SendGossipMenu( 4913,GetGUID()); break;
+ case CLASS_HUNTER: pPlayer->PlayerTalkClass->SendGossipMenu(10090,GetGUID()); break;
+ case CLASS_MAGE: pPlayer->PlayerTalkClass->SendGossipMenu( 328,GetGUID()); break;
+ case CLASS_PALADIN:pPlayer->PlayerTalkClass->SendGossipMenu( 1635,GetGUID()); break;
+ case CLASS_PRIEST: pPlayer->PlayerTalkClass->SendGossipMenu( 4436,GetGUID()); break;
+ case CLASS_ROGUE: pPlayer->PlayerTalkClass->SendGossipMenu( 4797,GetGUID()); break;
+ case CLASS_SHAMAN: pPlayer->PlayerTalkClass->SendGossipMenu( 5003,GetGUID()); break;
+ case CLASS_WARLOCK:pPlayer->PlayerTalkClass->SendGossipMenu( 5836,GetGUID()); break;
+ case CLASS_WARRIOR:pPlayer->PlayerTalkClass->SendGossipMenu( 4985,GetGUID()); break;
+ }
+ }
+ return false;
+ }
+ break;
+ case TRAINER_TYPE_PETS:
+ if(pPlayer->getClass()!=CLASS_HUNTER)
+ {
+ pPlayer->PlayerTalkClass->ClearMenus();
+ pPlayer->PlayerTalkClass->SendGossipMenu(3620,GetGUID());
+ return false;
+ }
+ break;
+ case TRAINER_TYPE_MOUNTS:
+ if(GetCreatureInfo()->race && pPlayer->getRace() != GetCreatureInfo()->race)
+ {
+ if(msg)
+ {
+ pPlayer->PlayerTalkClass->ClearMenus();
+ switch(GetCreatureInfo()->classNum)
+ {
+ case RACE_DWARF: pPlayer->PlayerTalkClass->SendGossipMenu(5865,GetGUID()); break;
+ case RACE_GNOME: pPlayer->PlayerTalkClass->SendGossipMenu(4881,GetGUID()); break;
+ case RACE_HUMAN: pPlayer->PlayerTalkClass->SendGossipMenu(5861,GetGUID()); break;
+ case RACE_NIGHTELF: pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break;
+ case RACE_ORC: pPlayer->PlayerTalkClass->SendGossipMenu(5863,GetGUID()); break;
+ case RACE_TAUREN: pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break;
+ case RACE_TROLL: pPlayer->PlayerTalkClass->SendGossipMenu(5816,GetGUID()); break;
+ case RACE_UNDEAD_PLAYER:pPlayer->PlayerTalkClass->SendGossipMenu( 624,GetGUID()); break;
+ case RACE_BLOODELF: pPlayer->PlayerTalkClass->SendGossipMenu(5862,GetGUID()); break;
+ case RACE_DRAENEI: pPlayer->PlayerTalkClass->SendGossipMenu(5864,GetGUID()); break;
+ }
+ }
+ return false;
+ }
+ break;
+ case TRAINER_TYPE_TRADESKILLS:
+ if(GetCreatureInfo()->trainer_spell && !pPlayer->HasSpell(GetCreatureInfo()->trainer_spell))
+ {
+ if(msg)
+ {
+ pPlayer->PlayerTalkClass->ClearMenus();
+ pPlayer->PlayerTalkClass->SendGossipMenu(11031,GetGUID());
+ }
+ return false;
+ }
+ break;
+ default:
+ return false; // checked and error output at creature_template loading
+ }
+ return true;
+}
+
+bool Creature::isCanIneractWithBattleMaster(Player* pPlayer, bool msg) const
+{
+ if(!isBattleMaster())
+ return false;
+
+ uint32 bgTypeId = objmgr.GetBattleMasterBG(GetEntry());
+ if(!msg)
+ return pPlayer->GetBGAccessByLevel(bgTypeId);
+
+ if(!pPlayer->GetBGAccessByLevel(bgTypeId))
+ {
+ pPlayer->PlayerTalkClass->ClearMenus();
+ switch(bgTypeId)
+ {
+ case BATTLEGROUND_AV: pPlayer->PlayerTalkClass->SendGossipMenu(7616,GetGUID()); break;
+ case BATTLEGROUND_WS: pPlayer->PlayerTalkClass->SendGossipMenu(7599,GetGUID()); break;
+ case BATTLEGROUND_AB: pPlayer->PlayerTalkClass->SendGossipMenu(7642,GetGUID()); break;
+ case BATTLEGROUND_EY:
+ case BATTLEGROUND_NA:
+ case BATTLEGROUND_BE:
+ case BATTLEGROUND_AA:
+ case BATTLEGROUND_RL: pPlayer->PlayerTalkClass->SendGossipMenu(10024,GetGUID()); break;
+ break;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool Creature::isCanTrainingAndResetTalentsOf(Player* pPlayer) const
+{
+ return pPlayer->getLevel() >= 10
+ && GetCreatureInfo()->trainer_type == TRAINER_TYPE_CLASS
+ && pPlayer->getClass() == GetCreatureInfo()->classNum;
+}
+
+void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
+{
+ PlayerMenu* pm=pPlayer->PlayerTalkClass;
+ pm->ClearMenus();
+
+ // lazy loading single time at use
+ LoadGossipOptions();
+
+ GossipOption* gso;
+ GossipOption* ingso;
+
+ for( GossipOptionList::iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
+ {
+ gso=&*i;
+ if(gso->GossipId == gossipid)
+ {
+ bool cantalking=true;
+ if(gso->Id==1)
+ {
+ uint32 textid=GetNpcTextId();
+ GossipText * gossiptext=objmgr.GetGossipText(textid);
+ if(!gossiptext)
+ cantalking=false;
+ }
+ else
+ {
+ switch (gso->Action)
+ {
+ case GOSSIP_OPTION_QUESTGIVER:
+ pPlayer->PrepareQuestMenu(GetGUID());
+ //if (pm->GetQuestMenu()->MenuItemCount() == 0)
+ cantalking=false;
+ //pm->GetQuestMenu()->ClearMenu();
+ break;
+ case GOSSIP_OPTION_ARMORER:
+ cantalking=false; // added in special mode
+ break;
+ case GOSSIP_OPTION_SPIRITHEALER:
+ if( !pPlayer->isDead() )
+ cantalking=false;
+ break;
+ case GOSSIP_OPTION_VENDOR:
+ {
+ VendorItemData const* vItems = GetVendorItems();
+ if(!vItems || vItems->Empty())
+ {
+ sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.",
+ GetGUIDLow(),GetEntry());
+ cantalking=false;
+ }
+ break;
+ }
+ case GOSSIP_OPTION_TRAINER:
+ if(!isCanTrainingOf(pPlayer,false))
+ cantalking=false;
+ break;
+ case GOSSIP_OPTION_UNLEARNTALENTS:
+ if(!isCanTrainingAndResetTalentsOf(pPlayer))
+ cantalking=false;
+ break;
+ case GOSSIP_OPTION_UNLEARNPETSKILLS:
+ if(!pPlayer->GetPet() || pPlayer->GetPet()->getPetType() != HUNTER_PET || pPlayer->GetPet()->m_spells.size() <= 1 || GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || GetCreatureInfo()->classNum != CLASS_HUNTER)
+ cantalking=false;
+ break;
+ case GOSSIP_OPTION_TAXIVENDOR:
+ if ( pPlayer->GetSession()->SendLearnNewTaxiNode(this) )
+ return;
+ break;
+ case GOSSIP_OPTION_BATTLEFIELD:
+ if(!isCanIneractWithBattleMaster(pPlayer,false))
+ cantalking=false;
+ break;
+ case GOSSIP_OPTION_SPIRITGUIDE:
+ case GOSSIP_OPTION_INNKEEPER:
+ case GOSSIP_OPTION_BANKER:
+ case GOSSIP_OPTION_PETITIONER:
+ case GOSSIP_OPTION_STABLEPET:
+ case GOSSIP_OPTION_TABARDDESIGNER:
+ case GOSSIP_OPTION_AUCTIONEER:
+ break; // no checks
+ default:
+ sLog.outErrorDb("Creature %u (entry: %u) have unknown gossip option %u",GetGUIDLow(),GetEntry(),gso->Action);
+ break;
+ }
+ }
+
+ if(!gso->Option.empty() && cantalking )
+ { //note for future dev: should have database fields for BoxMessage & BoxMoney
+ pm->GetGossipMenu().AddMenuItem((uint8)gso->Icon,gso->Option, gossipid,gso->Action,"",0,false);
+ ingso=gso;
+ }
+ }
+ }
+
+ ///some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
+ if(pm->Empty())
+ {
+ if(HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER))
+ {
+ isCanTrainingOf(pPlayer,true); // output error message if need
+ }
+ if(HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_BATTLEMASTER))
+ {
+ isCanIneractWithBattleMaster(pPlayer,true); // output error message if need
+ }
+ }
+}
+
+void Creature::sendPreparedGossip(Player* player)
+{
+ if(!player)
+ return;
+
+ GossipMenu& gossipmenu = player->PlayerTalkClass->GetGossipMenu();
+
+ // in case empty gossip menu open quest menu if any
+ if (gossipmenu.Empty() && GetNpcTextId() == 0)
+ {
+ player->SendPreparedQuest(GetGUID());
+ return;
+ }
+
+ // in case non empty gossip menu (that not included quests list size) show it
+ // (quest entries from quest menu wiill be included in list)
+ player->PlayerTalkClass->SendGossipMenu(GetNpcTextId(), GetGUID());
+}
+
+void Creature::OnGossipSelect(Player* player, uint32 option)
+{
+ GossipMenu& gossipmenu = player->PlayerTalkClass->GetGossipMenu();
+
+ if(option >= gossipmenu.MenuItemCount())
+ return;
+
+ uint32 action=gossipmenu.GetItem(option).m_gAction;
+ uint32 zoneid=GetZoneId();
+ uint64 guid=GetGUID();
+ GossipOption const *gossip=GetGossipOption( action );
+ uint32 textid;
+ if(!gossip)
+ {
+ zoneid=0;
+ gossip=GetGossipOption( action );
+ if(!gossip)
+ return;
+ }
+ textid=GetGossipTextId( action, zoneid);
+ if(textid==0)
+ textid=GetNpcTextId();
+
+ switch (gossip->Action)
+ {
+ case GOSSIP_OPTION_GOSSIP:
+ player->PlayerTalkClass->CloseGossip();
+ player->PlayerTalkClass->SendTalking( textid );
+ break;
+ case GOSSIP_OPTION_SPIRITHEALER:
+ if( player->isDead() )
+ CastSpell(this,17251,true,NULL,NULL,player->GetGUID());
+ break;
+ case GOSSIP_OPTION_QUESTGIVER:
+ player->PrepareQuestMenu( guid );
+ player->SendPreparedQuest( guid );
+ break;
+ case GOSSIP_OPTION_VENDOR:
+ case GOSSIP_OPTION_ARMORER:
+ player->GetSession()->SendListInventory(guid);
+ break;
+ case GOSSIP_OPTION_STABLEPET:
+ player->GetSession()->SendStablePet(guid);
+ break;
+ case GOSSIP_OPTION_TRAINER:
+ player->GetSession()->SendTrainerList(guid);
+ break;
+ case GOSSIP_OPTION_UNLEARNTALENTS:
+ player->PlayerTalkClass->CloseGossip();
+ player->SendTalentWipeConfirm(guid);
+ break;
+ case GOSSIP_OPTION_UNLEARNPETSKILLS:
+ player->PlayerTalkClass->CloseGossip();
+ player->SendPetSkillWipeConfirm();
+ break;
+ case GOSSIP_OPTION_TAXIVENDOR:
+ player->GetSession()->SendTaxiMenu(this);
+ break;
+ case GOSSIP_OPTION_INNKEEPER:
+ player->PlayerTalkClass->CloseGossip();
+ player->SetBindPoint( guid );
+ break;
+ case GOSSIP_OPTION_BANKER:
+ player->GetSession()->SendShowBank( guid );
+ break;
+ case GOSSIP_OPTION_PETITIONER:
+ player->PlayerTalkClass->CloseGossip();
+ player->GetSession()->SendPetitionShowList( guid );
+ break;
+ case GOSSIP_OPTION_TABARDDESIGNER:
+ player->PlayerTalkClass->CloseGossip();
+ player->GetSession()->SendTabardVendorActivate( guid );
+ break;
+ case GOSSIP_OPTION_AUCTIONEER:
+ player->GetSession()->SendAuctionHello( guid, this );
+ break;
+ case GOSSIP_OPTION_SPIRITGUIDE:
+ case GOSSIP_GUARD_SPELLTRAINER:
+ case GOSSIP_GUARD_SKILLTRAINER:
+ prepareGossipMenu( player,gossip->Id );
+ sendPreparedGossip( player );
+ break;
+ case GOSSIP_OPTION_BATTLEFIELD:
+ {
+ uint32 bgTypeId = objmgr.GetBattleMasterBG(GetEntry());
+ player->GetSession()->SendBattlegGroundList( GetGUID(), bgTypeId );
+ break;
+ }
+ default:
+ OnPoiSelect( player, gossip );
+ break;
+ }
+
+}
+
+void Creature::OnPoiSelect(Player* player, GossipOption const *gossip)
+{
+ if(gossip->GossipId==GOSSIP_GUARD_SPELLTRAINER || gossip->GossipId==GOSSIP_GUARD_SKILLTRAINER)
+ {
+ //float x,y;
+ //bool findnpc=false;
+ Poi_Icon icon = ICON_POI_0;
+ //QueryResult *result;
+ //Field *fields;
+ uint32 mapid=GetMapId();
+ Map const* map=MapManager::Instance().GetBaseMap( mapid );
+ uint16 areaflag=map->GetAreaFlag(GetPositionX(),GetPositionY());
+ uint32 zoneid=Map::GetZoneId(areaflag,mapid);
+ std::string areaname= gossip->Option;
+ /*
+ uint16 pflag;
+
+ // use the action relate to creaturetemplate.trainer_type ?
+ result= WorldDatabase.PQuery("SELECT creature.position_x,creature.position_y FROM creature,creature_template WHERE creature.map = '%u' AND creature.id = creature_template.entry AND creature_template.trainer_type = '%u'", mapid, gossip->Action );
+ if(!result)
+ return;
+ do
+ {
+ fields = result->Fetch();
+ x=fields[0].GetFloat();
+ y=fields[1].GetFloat();
+ pflag=map->GetAreaFlag(GetPositionX(),GetPositionY());
+ if(pflag==areaflag)
+ {
+ findnpc=true;
+ break;
+ }
+ }while(result->NextRow());
+
+ delete result;
+
+ if(!findnpc)
+ {
+ player->PlayerTalkClass->SendTalking( "$NSorry", "Here no this person.");
+ return;
+ }*/
+
+ //need add more case.
+ switch(gossip->Action)
+ {
+ case GOSSIP_GUARD_BANK:
+ icon=ICON_POI_HOUSE;
+ break;
+ case GOSSIP_GUARD_RIDE:
+ icon=ICON_POI_RWHORSE;
+ break;
+ case GOSSIP_GUARD_GUILD:
+ icon=ICON_POI_BLUETOWER;
+ break;
+ default:
+ icon=ICON_POI_TOWER;
+ break;
+ }
+ uint32 textid=GetGossipTextId( gossip->Action, zoneid );
+ player->PlayerTalkClass->SendTalking( textid );
+ // how this could worked player->PlayerTalkClass->SendPointOfInterest( x, y, icon, 2, 15, areaname.c_str() );
+ }
+}
+
+uint32 Creature::GetGossipTextId(uint32 action, uint32 zoneid)
+{
+ QueryResult *result= WorldDatabase.PQuery("SELECT textid FROM npc_gossip_textid WHERE action = '%u' AND zoneid ='%u'", action, zoneid );
+
+ if(!result)
+ return 0;
+
+ Field *fields = result->Fetch();
+ uint32 id = fields[0].GetUInt32();
+
+ delete result;
+
+ return id;
+}
+
+uint32 Creature::GetNpcTextId()
+{
+ if (!m_DBTableGuid)
+ return DEFAULT_GOSSIP_MESSAGE;
+
+ if(uint32 pos = objmgr.GetNpcGossip(m_DBTableGuid))
+ return pos;
+
+ return DEFAULT_GOSSIP_MESSAGE;
+}
+
+GossipOption const* Creature::GetGossipOption( uint32 id ) const
+{
+ for( GossipOptionList::const_iterator i = m_goptions.begin( ); i != m_goptions.end( ); i++ )
+ {
+ if(i->Action==id )
+ return &*i;
+ }
+ return NULL;
+}
+
+void Creature::LoadGossipOptions()
+{
+ if(m_gossipOptionLoaded)
+ return;
+
+ uint32 npcflags=GetUInt32Value(UNIT_NPC_FLAGS);
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT id,gossip_id,npcflag,icon,action,option_text FROM npc_option WHERE (npcflag & %u)<>0", npcflags );
+
+ if(!result)
+ return;
+
+ GossipOption go;
+ do
+ {
+ Field *fields = result->Fetch();
+ go.Id= fields[0].GetUInt32();
+ go.GossipId = fields[1].GetUInt32();
+ go.NpcFlag=fields[2].GetUInt32();
+ go.Icon=fields[3].GetUInt32();
+ go.Action=fields[4].GetUInt32();
+ go.Option=fields[5].GetCppString();
+ addGossipOption(go);
+ }while( result->NextRow() );
+ delete result;
+
+ m_gossipOptionLoaded = true;
+}
+
+void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type)
+{
+ /* uint32 timeElap = getMSTime();
+ if ((timeElap - m_startMove) < m_moveTime)
+ {
+ oX = (dX - oX) * ( (timeElap - m_startMove) / m_moveTime );
+ oY = (dY - oY) * ( (timeElap - m_startMove) / m_moveTime );
+ }
+ else
+ {
+ oX = dX;
+ oY = dY;
+ }
+
+ dX = x;
+ dY = y;
+ m_orientation = atan2((oY - dY), (oX - dX));
+
+ m_startMove = getMSTime();
+ m_moveTime = time;*/
+ SendMonsterMove(x, y, z, type, MovementFlags, time);
+}
+
+Player *Creature::GetLootRecipient() const
+{
+ if (!m_lootRecipient) return NULL;
+ else return ObjectAccessor::FindPlayer(m_lootRecipient);
+}
+
+void Creature::SetLootRecipient(Unit *unit)
+{
+ // set the player whose group should receive the right
+ // to loot the creature after it dies
+ // should be set to NULL after the loot disappears
+
+ if (!unit)
+ {
+ m_lootRecipient = 0;
+ RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_OTHER_TAGGER);
+ return;
+ }
+
+ Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself();
+ if(!player) // normal creature, no player involved
+ return;
+
+ m_lootRecipient = player->GetGUID();
+ SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_OTHER_TAGGER);
+}
+
+void Creature::SaveToDB()
+{
+ // this should only be used when the creature has already been loaded
+ // perferably after adding to map, because mapid may not be valid otherwise
+ CreatureData const *data = objmgr.GetCreatureData(m_DBTableGuid);
+ if(!data)
+ {
+ sLog.outError("Creature::SaveToDB failed, cannot get creature data!");
+ return;
+ }
+
+ SaveToDB(GetMapId(), data->spawnMask);
+}
+
+void Creature::SaveToDB(uint32 mapid, uint8 spawnMask)
+{
+ // update in loaded data
+ if (!m_DBTableGuid)
+ m_DBTableGuid = GetGUIDLow();
+ CreatureData& data = objmgr.NewOrExistCreatureData(m_DBTableGuid);
+
+ uint32 displayId = GetNativeDisplayId();
+
+ // check if it's a custom model and if not, use 0 for displayId
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ if(cinfo)
+ {
+ if(displayId != cinfo->DisplayID_A && displayId != cinfo->DisplayID_H)
+ {
+ CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(cinfo->DisplayID_A);
+ if(!minfo || displayId != minfo->modelid_other_gender)
+ {
+ minfo = objmgr.GetCreatureModelInfo(cinfo->DisplayID_H);
+ if(minfo && displayId == minfo->modelid_other_gender)
+ displayId = 0;
+ }
+ else
+ displayId = 0;
+ }
+ else
+ displayId = 0;
+ }
+
+ // data->guid = guid don't must be update at save
+ data.id = GetEntry();
+ data.mapid = mapid;
+ data.displayid = displayId;
+ data.equipmentId = GetEquipmentId();
+ data.posX = GetPositionX();
+ data.posY = GetPositionY();
+ data.posZ = GetPositionZ();
+ data.orientation = GetOrientation();
+ data.spawntimesecs = m_respawnDelay;
+ // prevent add data integrity problems
+ data.spawndist = GetDefaultMovementType()==IDLE_MOTION_TYPE ? 0 : m_respawnradius;
+ data.currentwaypoint = 0;
+ data.curhealth = GetHealth();
+ data.curmana = GetPower(POWER_MANA);
+ data.is_dead = m_isDeadByDefault;
+ // prevent add data integrity problems
+ data.movementType = !m_respawnradius && GetDefaultMovementType()==RANDOM_MOTION_TYPE
+ ? IDLE_MOTION_TYPE : GetDefaultMovementType();
+ data.spawnMask = spawnMask;
+
+ // updated in DB
+ WorldDatabase.BeginTransaction();
+
+ WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid);
+
+ std::ostringstream ss;
+ ss << "INSERT INTO creature VALUES ("
+ << m_DBTableGuid << ","
+ << GetEntry() << ","
+ << mapid <<","
+ << (uint32)spawnMask << ","
+ << displayId <<","
+ << GetEquipmentId() <<","
+ << GetPositionX() << ","
+ << GetPositionY() << ","
+ << GetPositionZ() << ","
+ << GetOrientation() << ","
+ << m_respawnDelay << "," //respawn time
+ << (float) m_respawnradius << "," //spawn distance (float)
+ << (uint32) (0) << "," //currentwaypoint
+ << GetHealth() << "," //curhealth
+ << GetPower(POWER_MANA) << "," //curmana
+ << (m_isDeadByDefault ? 1 : 0) << "," //is_dead
+ << GetDefaultMovementType() << ")"; //default movement generator type
+
+ WorldDatabase.PExecuteLog( ss.str( ).c_str( ) );
+
+ WorldDatabase.CommitTransaction();
+}
+
+void Creature::SelectLevel(const CreatureInfo *cinfo)
+{
+ uint32 rank = isPet()? 0 : cinfo->rank;
+
+ // level
+ uint32 minlevel = std::min(cinfo->maxlevel, cinfo->minlevel);
+ uint32 maxlevel = std::max(cinfo->maxlevel, cinfo->minlevel);
+ uint32 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel);
+ SetLevel(level);
+
+ float rellevel = maxlevel == minlevel ? 0 : (float(level - minlevel))/(maxlevel - minlevel);
+
+ // health
+ float healthmod = _GetHealthMod(rank);
+
+ uint32 minhealth = std::min(cinfo->maxhealth, cinfo->minhealth);
+ uint32 maxhealth = std::max(cinfo->maxhealth, cinfo->minhealth);
+ uint32 health = uint32(healthmod * (minhealth + uint32(rellevel*(maxhealth - minhealth))));
+
+ SetCreateHealth(health);
+ SetMaxHealth(health);
+ SetHealth(health);
+
+ // mana
+ uint32 minmana = std::min(cinfo->maxmana, cinfo->minmana);
+ uint32 maxmana = std::max(cinfo->maxmana, cinfo->minmana);
+ uint32 mana = minmana + uint32(rellevel*(maxmana - minmana));
+
+ SetCreateMana(mana);
+ SetMaxPower(POWER_MANA, mana); //MAX Mana
+ SetPower(POWER_MANA, mana);
+
+ SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, health);
+ SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, mana);
+
+ // damage
+ float damagemod = _GetDamageMod(rank);
+
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, cinfo->mindmg * damagemod);
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, cinfo->maxdmg * damagemod);
+
+ SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,cinfo->minrangedmg * damagemod);
+ SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,cinfo->maxrangedmg * damagemod);
+
+ SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, cinfo->attackpower * damagemod);
+}
+
+float Creature::_GetHealthMod(int32 Rank)
+{
+ switch (Rank) // define rates for each elite rank
+ {
+ case CREATURE_ELITE_NORMAL:
+ return sWorld.getRate(RATE_CREATURE_NORMAL_HP);
+ case CREATURE_ELITE_ELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
+ case CREATURE_ELITE_RAREELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_HP);
+ case CREATURE_ELITE_WORLDBOSS:
+ return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_HP);
+ case CREATURE_ELITE_RARE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RARE_HP);
+ default:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
+ }
+}
+
+float Creature::_GetDamageMod(int32 Rank)
+{
+ switch (Rank) // define rates for each elite rank
+ {
+ case CREATURE_ELITE_NORMAL:
+ return sWorld.getRate(RATE_CREATURE_NORMAL_DAMAGE);
+ case CREATURE_ELITE_ELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
+ case CREATURE_ELITE_RAREELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_DAMAGE);
+ case CREATURE_ELITE_WORLDBOSS:
+ return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE);
+ case CREATURE_ELITE_RARE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RARE_DAMAGE);
+ default:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
+ }
+}
+
+float Creature::GetSpellDamageMod(int32 Rank)
+{
+ switch (Rank) // define rates for each elite rank
+ {
+ case CREATURE_ELITE_NORMAL:
+ return sWorld.getRate(RATE_CREATURE_NORMAL_SPELLDAMAGE);
+ case CREATURE_ELITE_ELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
+ case CREATURE_ELITE_RAREELITE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE);
+ case CREATURE_ELITE_WORLDBOSS:
+ return sWorld.getRate(RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE);
+ case CREATURE_ELITE_RARE:
+ return sWorld.getRate(RATE_CREATURE_ELITE_RARE_SPELLDAMAGE);
+ default:
+ return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
+ }
+}
+
+bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 team, const CreatureData *data)
+{
+ CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(Entry);
+ if(!cinfo)
+ {
+ sLog.outErrorDb("Error: creature entry %u does not exist.", Entry);
+ return false;
+ }
+ m_originalEntry = Entry;
+
+ Object::_Create(guidlow, Entry, HIGHGUID_UNIT);
+
+ if(!UpdateEntry(Entry, team, data))
+ return false;
+
+ //Notify the map's instance data.
+ //Only works if you create the object in it, not if it is moves to that map.
+ //Normally non-players do not teleport to other maps.
+ Map *map = MapManager::Instance().FindMap(GetMapId(), GetInstanceId());
+ if(map && map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData())
+ {
+ ((InstanceMap*)map)->GetInstanceData()->OnCreatureCreate(this, Entry);
+ }
+
+ return true;
+}
+
+bool Creature::LoadFromDB(uint32 guid, Map *map)
+{
+ CreatureData const* data = objmgr.GetCreatureData(guid);
+
+ if(!data)
+ {
+ sLog.outErrorDb("Creature (GUID: %u) not found in table `creature`, can't load. ",guid);
+ return false;
+ }
+
+ m_DBTableGuid = guid;
+ if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT);
+
+ uint16 team = 0;
+ if(!Create(guid,map,data->id,team,data))
+ return false;
+
+ Relocate(data->posX,data->posY,data->posZ,data->orientation);
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",GetGUIDLow(),GetEntry(),GetPositionX(),GetPositionY());
+ return false;
+ }
+
+ m_respawnradius = data->spawndist;
+
+ m_respawnDelay = data->spawntimesecs;
+ m_isDeadByDefault = data->is_dead;
+ m_deathState = m_isDeadByDefault ? DEAD : ALIVE;
+
+ m_respawnTime = objmgr.GetCreatureRespawnTime(m_DBTableGuid,GetInstanceId());
+ if(m_respawnTime > time(NULL)) // not ready to respawn
+ m_deathState = DEAD;
+ else if(m_respawnTime) // respawn time set but expired
+ {
+ m_respawnTime = 0;
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ }
+
+ uint32 curhealth = data->curhealth;
+ if(curhealth)
+ {
+ curhealth = uint32(curhealth*_GetHealthMod(GetCreatureInfo()->rank));
+ if(curhealth < 1)
+ curhealth = 1;
+ }
+
+ SetHealth(m_deathState == ALIVE ? curhealth : 0);
+ SetPower(POWER_MANA,data->curmana);
+
+ SetMeleeDamageSchool(SpellSchools(GetCreatureInfo()->dmgschool));
+
+ // checked at creature_template loading
+ m_defaultMovementType = MovementGeneratorType(data->movementType);
+
+ AIM_Initialize();
+ return true;
+}
+
+void Creature::LoadEquipment(uint32 equip_entry, bool force)
+{
+ if(equip_entry == 0)
+ {
+ if (force)
+ {
+ for (uint8 i=0;i<3;i++)
+ {
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i, 0);
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2), 0);
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 1, 0);
+ }
+ m_equipmentId = 0;
+ }
+ return;
+ }
+
+ EquipmentInfo const *einfo = objmgr.GetEquipmentInfo(equip_entry);
+ if (!einfo)
+ return;
+
+ m_equipmentId = equip_entry;
+ for (uint8 i=0;i<3;i++)
+ {
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + i, einfo->equipmodel[i]);
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2), einfo->equipinfo[i]);
+ SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + (i * 2) + 1, einfo->equipslot[i]);
+ }
+}
+
+bool Creature::hasQuest(uint32 quest_id) const
+{
+ QuestRelations const& qr = objmgr.mCreatureQuestRelations;
+ for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
+ {
+ if(itr->second==quest_id)
+ return true;
+ }
+ return false;
+}
+
+bool Creature::hasInvolvedQuest(uint32 quest_id) const
+{
+ QuestRelations const& qr = objmgr.mCreatureQuestInvolvedRelations;
+ for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
+ {
+ if(itr->second==quest_id)
+ return true;
+ }
+ return false;
+}
+
+void Creature::DeleteFromDB()
+{
+ if (!m_DBTableGuid)
+ {
+ sLog.outDebug("Trying to delete not saved creature!");
+ return;
+ }
+
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ objmgr.DeleteCreatureData(m_DBTableGuid);
+
+ WorldDatabase.BeginTransaction();
+ WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM creature_addon WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM game_event_creature WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM game_event_model_equip WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.CommitTransaction();
+}
+
+float Creature::GetAttackDistance(Unit const* pl) const
+{
+ float aggroRate = sWorld.getRate(RATE_CREATURE_AGGRO);
+ if(aggroRate==0)
+ return 0.0f;
+
+ int32 playerlevel = pl->getLevelForTarget(this);
+ int32 creaturelevel = getLevelForTarget(pl);
+
+ int32 leveldif = playerlevel - creaturelevel;
+
+ // "The maximum Aggro Radius has a cap of 25 levels under. Example: A level 30 char has the same Aggro Radius of a level 5 char on a level 60 mob."
+ if ( leveldif < - 25)
+ leveldif = -25;
+
+ // "The aggro radius of a mob having the same level as the player is roughly 20 yards"
+ float RetDistance = 20;
+
+ // "Aggro Radius varries with level difference at a rate of roughly 1 yard/level"
+ // radius grow if playlevel < creaturelevel
+ RetDistance -= (float)leveldif;
+
+ if(creaturelevel+5 <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ // detect range auras
+ RetDistance += GetTotalAuraModifier(SPELL_AURA_MOD_DETECT_RANGE);
+
+ // detected range auras
+ RetDistance += pl->GetTotalAuraModifier(SPELL_AURA_MOD_DETECTED_RANGE);
+ }
+
+ // "Minimum Aggro Radius for a mob seems to be combat range (5 yards)"
+ if(RetDistance < 5)
+ RetDistance = 5;
+
+ return (RetDistance*aggroRate);
+}
+
+void Creature::setDeathState(DeathState s)
+{
+ if((s == JUST_DIED && !m_isDeadByDefault)||(s == JUST_ALIVED && m_isDeadByDefault))
+ {
+ m_deathTimer = m_corpseDelay*1000;
+
+ // always save boss respawn time at death to prevent crash cheating
+ if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY) || isWorldBoss())
+ SaveRespawnTime();
+
+ if(!IsStopped())
+ StopMoving();
+ }
+ Unit::setDeathState(s);
+
+ if(s == JUST_DIED)
+ {
+ SetUInt64Value (UNIT_FIELD_TARGET,0); // remove target selection in any cases (can be set at aura remove in Unit::setDeathState)
+ SetUInt32Value(UNIT_NPC_FLAGS, 0);
+
+ if(!isPet() && GetCreatureInfo()->SkinLootId)
+ if ( LootTemplates_Skinning.HaveLootFor(GetCreatureInfo()->SkinLootId) )
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+
+ Unit::setDeathState(CORPSE);
+ }
+ if(s == JUST_ALIVED)
+ {
+ SetHealth(GetMaxHealth());
+ SetLootRecipient(NULL);
+ Unit::setDeathState(ALIVE);
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
+ RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+ AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+ SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag);
+ clearUnitState(UNIT_STAT_ALL_STATE);
+ i_motionMaster.Clear();
+ SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
+ LoadCreaturesAddon(true);
+ }
+}
+
+void Creature::Respawn()
+{
+ RemoveCorpse();
+
+ // forced recreate creature object at clients
+ UnitVisibility currentVis = GetVisibility();
+ SetVisibility(VISIBILITY_RESPAWN);
+ ObjectAccessor::UpdateObjectVisibility(this);
+ SetVisibility(currentVis); // restore visibility state
+ ObjectAccessor::UpdateObjectVisibility(this);
+
+ if(getDeathState()==DEAD)
+ {
+ if (m_DBTableGuid)
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ m_respawnTime = time(NULL); // respawn at next tick
+ }
+}
+
+bool Creature::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
+{
+ if (!spellInfo)
+ return false;
+
+ if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))
+ return true;
+
+ return Unit::IsImmunedToSpell(spellInfo, useCharges);
+}
+
+bool Creature::IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const
+{
+ if (GetCreatureInfo()->MechanicImmuneMask & (1 << (mechanic-1)))
+ return true;
+
+ return Unit::IsImmunedToSpellEffect(effect, mechanic);
+}
+
+SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim)
+{
+ if(!pVictim)
+ return NULL;
+
+ for(uint32 i=0; i < CREATURE_MAX_SPELLS; i++)
+ {
+ if(!m_spells[i])
+ continue;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i] );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]);
+ continue;
+ }
+
+ bool bcontinue = true;
+ for(uint32 j=0;j<3;j++)
+ {
+ if( (spellInfo->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE ) ||
+ (spellInfo->Effect[j] == SPELL_EFFECT_INSTAKILL) ||
+ (spellInfo->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE) ||
+ (spellInfo->Effect[j] == SPELL_EFFECT_HEALTH_LEECH )
+ )
+ {
+ bcontinue = false;
+ break;
+ }
+ }
+ if(bcontinue) continue;
+
+ if(spellInfo->manaCost > GetPower(POWER_MANA))
+ continue;
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
+ float range = GetSpellMaxRange(srange);
+ float minrange = GetSpellMinRange(srange);
+ float dist = GetDistance(pVictim);
+ //if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
+ // continue;
+ if( dist > range || dist < minrange )
+ continue;
+ if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
+ continue;
+ return spellInfo;
+ }
+ return NULL;
+}
+
+SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim)
+{
+ if(!pVictim)
+ return NULL;
+
+ for(uint32 i=0; i < CREATURE_MAX_SPELLS; i++)
+ {
+ if(!m_spells[i])
+ continue;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_spells[i] );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %i\n", m_spells[i]);
+ continue;
+ }
+
+ bool bcontinue = true;
+ for(uint32 j=0;j<3;j++)
+ {
+ if( (spellInfo->Effect[j] == SPELL_EFFECT_HEAL ) )
+ {
+ bcontinue = false;
+ break;
+ }
+ }
+ if(bcontinue) continue;
+
+ if(spellInfo->manaCost > GetPower(POWER_MANA))
+ continue;
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
+ float range = GetSpellMaxRange(srange);
+ float minrange = GetSpellMinRange(srange);
+ float dist = GetDistance(pVictim);
+ //if(!isInFront( pVictim, range ) && spellInfo->AttributesEx )
+ // continue;
+ if( dist > range || dist < minrange )
+ continue;
+ if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
+ continue;
+ return spellInfo;
+ }
+ return NULL;
+}
+
+bool Creature::IsVisibleInGridForPlayer(Player* pl) const
+{
+ // gamemaster in GM mode see all, including ghosts
+ if(pl->isGameMaster())
+ return true;
+
+ // Live player (or with not release body see live creatures or death creatures with corpse disappearing time > 0
+ if(pl->isAlive() || pl->GetDeathTimer() > 0)
+ {
+ if( GetEntry() == VISUAL_WAYPOINT && !pl->isGameMaster() )
+ return false;
+ return isAlive() || m_deathTimer > 0 || m_isDeadByDefault && m_deathState==CORPSE;
+ }
+
+ // Dead player see live creatures near own corpse
+ if(isAlive())
+ {
+ Corpse *corpse = pl->GetCorpse();
+ if(corpse)
+ {
+ // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level
+ if(corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO)))
+ return true;
+ }
+ }
+
+ // Dead player see Spirit Healer or Spirit Guide
+ if(isSpiritService())
+ return true;
+
+ // and not see any other
+ return false;
+}
+
+void Creature::DoFleeToGetAssistance(float radius) // Optional parameter
+{
+ if (!getVictim())
+ return;
+
+ Creature* pCreature = NULL;
+
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::NearestAssistCreatureInCreatureRangeCheck u_check(this,getVictim(),radius);
+ MaNGOS::CreatureLastSearcher<MaNGOS::NearestAssistCreatureInCreatureRangeCheck> searcher(pCreature, u_check);
+
+ TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestAssistCreatureInCreatureRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_creature_searcher, *(GetMap()));
+
+ if(!GetMotionMaster()->empty() && (GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE))
+ GetMotionMaster()->Clear(false);
+ if(pCreature == NULL)
+ {
+ GetMotionMaster()->MoveIdle();
+ GetMotionMaster()->MoveFleeing(getVictim());
+ }
+ else
+ {
+ GetMotionMaster()->MoveIdle();
+ GetMotionMaster()->MovePoint(0,pCreature->GetPositionX(),pCreature->GetPositionY(),pCreature->GetPositionZ());
+ }
+}
+
+void Creature::CallAssistence()
+{
+ if( !m_AlreadyCallAssistence && getVictim() && !isPet() && !isCharmed())
+ {
+ SetNoCallAssistence(true);
+
+ float radius = sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS);
+ if(radius > 0)
+ {
+ std::list<Creature*> assistList;
+
+ {
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::AnyAssistCreatureInRangeCheck u_check(this, getVictim(), radius);
+ MaNGOS::CreatureListSearcher<MaNGOS::AnyAssistCreatureInRangeCheck> searcher(assistList, u_check);
+
+ TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::AnyAssistCreatureInRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_creature_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
+ }
+
+ for(std::list<Creature*>::iterator iter = assistList.begin(); iter != assistList.end(); ++iter)
+ {
+ (*iter)->SetNoCallAssistence(true);
+ if((*iter)->AI())
+ (*iter)->AI()->AttackStart(getVictim());
+ }
+ }
+ }
+}
+
+void Creature::SaveRespawnTime()
+{
+ if(isPet() || !m_DBTableGuid)
+ return;
+
+ if(m_respawnTime > time(NULL)) // dead (no corpse)
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime);
+ else if(m_deathTimer > 0) // dead (corpse)
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),time(NULL)+m_respawnDelay+m_deathTimer/1000);
+}
+
+bool Creature::IsOutOfThreatArea(Unit* pVictim) const
+{
+ if(!pVictim)
+ return true;
+
+ if(!pVictim->IsInMap(this))
+ return true;
+
+ if(!pVictim->isTargetableForAttack())
+ return true;
+
+ if(!pVictim->isInAccessablePlaceFor(this))
+ return true;
+
+ if(sMapStore.LookupEntry(GetMapId())->Instanceable())
+ return false;
+
+ float length = pVictim->GetDistance(CombatStartX,CombatStartY,CombatStartZ);
+ float AttackDist = GetAttackDistance(pVictim);
+ uint32 ThreatRadius = sWorld.getConfig(CONFIG_THREAT_RADIUS);
+
+ //Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and ouf of combat every update tick.
+ return ( length > (ThreatRadius > AttackDist ? ThreatRadius : AttackDist));
+}
+
+CreatureDataAddon const* Creature::GetCreatureAddon() const
+{
+ if (m_DBTableGuid)
+ {
+ if(CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(m_DBTableGuid))
+ return addon;
+ }
+
+ // dependent from heroic mode entry
+ return ObjectMgr::GetCreatureTemplateAddon(GetCreatureInfo()->Entry);
+}
+
+//creature_addon table
+bool Creature::LoadCreaturesAddon(bool reload)
+{
+ CreatureDataAddon const *cainfo = GetCreatureAddon();
+ if(!cainfo)
+ return false;
+
+ if (cainfo->mount != 0)
+ Mount(cainfo->mount);
+
+ if (cainfo->bytes0 != 0)
+ SetUInt32Value(UNIT_FIELD_BYTES_0, cainfo->bytes0);
+
+ if (cainfo->bytes1 != 0)
+ SetUInt32Value(UNIT_FIELD_BYTES_1, cainfo->bytes1);
+
+ if (cainfo->bytes2 != 0)
+ SetUInt32Value(UNIT_FIELD_BYTES_2, cainfo->bytes2);
+
+ if (cainfo->emote != 0)
+ SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote);
+
+ if (cainfo->move_flags != 0)
+ SetUnitMovementFlags(cainfo->move_flags);
+
+ if(cainfo->auras)
+ {
+ for (CreatureDataAddonAura const* cAura = cainfo->auras; cAura->spell_id; ++cAura)
+ {
+ SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura->spell_id);
+ if (!AdditionalSpellInfo)
+ {
+ sLog.outErrorDb("Creature (GUIDLow: %u Entry: %u ) has wrong spell %u defined in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id);
+ continue;
+ }
+
+ // skip already applied aura
+ if(HasAura(cAura->spell_id,cAura->effect_idx))
+ {
+ if(!reload)
+ sLog.outErrorDb("Creature (GUIDLow: %u Entry: %u ) has duplicate aura (spell %u effect %u) in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id,cAura->effect_idx);
+
+ continue;
+ }
+
+ Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, cAura->effect_idx, NULL, this, this, 0);
+ AddAura(AdditionalAura);
+ sLog.outDebug("Spell: %u with Aura %u added to creature (GUIDLow: %u Entry: %u )", cAura->spell_id, AdditionalSpellInfo->EffectApplyAuraName[0],GetGUIDLow(),GetEntry());
+ }
+ }
+ return true;
+}
+
+/// Send a message to LocalDefense channel for players oposition team in the zone
+void Creature::SendZoneUnderAttackMessage(Player* attacker)
+{
+ uint32 enemy_team = attacker->GetTeam();
+
+ WorldPacket data(SMSG_ZONE_UNDER_ATTACK,4);
+ data << (uint32)GetZoneId();
+ sWorld.SendGlobalMessage(&data,NULL,(enemy_team==ALLIANCE ? HORDE : ALLIANCE));
+}
+
+void Creature::_AddCreatureSpellCooldown(uint32 spell_id, time_t end_time)
+{
+ m_CreatureSpellCooldowns[spell_id] = end_time;
+}
+
+void Creature::_AddCreatureCategoryCooldown(uint32 category, time_t apply_time)
+{
+ m_CreatureCategoryCooldowns[category] = apply_time;
+}
+
+void Creature::AddCreatureSpellCooldown(uint32 spellid)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
+ if(!spellInfo)
+ return;
+
+ uint32 cooldown = GetSpellRecoveryTime(spellInfo);
+ if(cooldown)
+ _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/1000);
+
+ if(spellInfo->Category)
+ _AddCreatureCategoryCooldown(spellInfo->Category, time(NULL));
+
+ m_GlobalCooldown = spellInfo->StartRecoveryTime;
+}
+
+bool Creature::HasCategoryCooldown(uint32 spell_id) const
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+ if(!spellInfo)
+ return false;
+
+ // check global cooldown if spell affected by it
+ if (spellInfo->StartRecoveryCategory > 0 && m_GlobalCooldown > 0)
+ return true;
+
+ CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->Category);
+ return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / 1000)) > time(NULL));
+}
+
+bool Creature::HasSpellCooldown(uint32 spell_id) const
+{
+ CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id);
+ return (itr != m_CreatureSpellCooldowns.end() && itr->second > time(NULL)) || HasCategoryCooldown(spell_id);
+}
+
+bool Creature::IsInEvadeMode() const
+{
+ return !i_motionMaster.empty() && i_motionMaster.GetCurrentMovementGeneratorType() == HOME_MOTION_TYPE;
+}
+
+bool Creature::HasSpell(uint32 spellID) const
+{
+ uint8 i;
+ for(i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ if(spellID == m_spells[i])
+ break;
+ return i < CREATURE_MAX_SPELLS; //broke before end of iteration of known spells
+}
+
+time_t Creature::GetRespawnTimeEx() const
+{
+ time_t now = time(NULL);
+ if(m_respawnTime > now) // dead (no corpse)
+ return m_respawnTime;
+ else if(m_deathTimer > 0) // dead (corpse)
+ return now+m_respawnDelay+m_deathTimer/1000;
+ else
+ return now;
+}
+
+void Creature::GetRespawnCoord( float &x, float &y, float &z, float* ori, float* dist ) const
+{
+ if (m_DBTableGuid)
+ {
+ 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()
+{
+ if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE))
+ {
+ uint32 nDeathTimer;
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+
+ // corpse was not skinnable -> apply corpse looted timer
+ if (!cinfo || !cinfo->SkinLootId)
+ nDeathTimer = (uint32)((m_corpseDelay * 1000) * sWorld.getRate(RATE_CORPSE_DECAY_LOOTED));
+ // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
+ else
+ nDeathTimer = 0;
+
+ // update death timer only if looted timer is shorter
+ if (m_deathTimer > nDeathTimer)
+ m_deathTimer = nDeathTimer;
+ }
+}
+
+uint32 Creature::getLevelForTarget( Unit const* target ) const
+{
+ if(!isWorldBoss())
+ return Unit::getLevelForTarget(target);
+
+ uint32 level = target->getLevel()+sWorld.getConfig(CONFIG_WORLD_BOSS_LEVEL_DIFF);
+ if(level < 1)
+ return 1;
+ if(level > 255)
+ return 255;
+ return level;
+}
+
+char const* Creature::GetScriptName() const
+{
+ return ObjectMgr::GetCreatureTemplate(GetEntry())->ScriptName;
+}
+
+
+VendorItemData const* Creature::GetVendorItems() const
+{
+ return objmgr.GetNpcVendorItemList(GetEntry());
+}
+
+uint32 Creature::GetVendorItemCurrentCount(VendorItem const* vItem)
+{
+ if(!vItem->maxcount)
+ return vItem->maxcount;
+
+ VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
+ for(; itr != m_vendorItemCounts.end(); ++itr)
+ if(itr->itemId==vItem->item)
+ break;
+
+ if(itr == m_vendorItemCounts.end())
+ return vItem->maxcount;
+
+ VendorItemCount* vCount = &*itr;
+
+ time_t ptime = time(NULL);
+
+ if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
+ {
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
+
+ uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
+ if((vCount->count + diff * pProto->BuyCount) >= vItem->maxcount )
+ {
+ m_vendorItemCounts.erase(itr);
+ return vItem->maxcount;
+ }
+
+ vCount->count += diff * pProto->BuyCount;
+ vCount->lastIncrementTime = ptime;
+ }
+
+ return vCount->count;
+}
+
+uint32 Creature::UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count)
+{
+ if(!vItem->maxcount)
+ return 0;
+
+ VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
+ for(; itr != m_vendorItemCounts.end(); ++itr)
+ if(itr->itemId==vItem->item)
+ break;
+
+ if(itr == m_vendorItemCounts.end())
+ {
+ uint32 new_count = vItem->maxcount > used_count ? vItem->maxcount-used_count : 0;
+ m_vendorItemCounts.push_back(VendorItemCount(vItem->item,new_count));
+ return new_count;
+ }
+
+ VendorItemCount* vCount = &*itr;
+
+ time_t ptime = time(NULL);
+
+ if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
+ {
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
+
+ uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
+ if((vCount->count + diff * pProto->BuyCount) < vItem->maxcount )
+ vCount->count += diff * pProto->BuyCount;
+ else
+ vCount->count = vItem->maxcount;
+ }
+
+ vCount->count = vCount->count > used_count ? vCount->count-used_count : 0;
+ vCount->lastIncrementTime = ptime;
+ return vCount->count;
+}
+
+TrainerSpellData const* Creature::GetTrainerSpells() const
+{
+ return objmgr.GetNpcTrainerSpells(GetEntry());
+}
diff --git a/src/game/Creature.h b/src/game/Creature.h
index c32ac7327b9..372692ec24a 100644
--- a/src/game/Creature.h
+++ b/src/game/Creature.h
@@ -1,617 +1,617 @@
-/*
- * 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
- */
-
-#ifndef MANGOSSERVER_CREATURE_H
-#define MANGOSSERVER_CREATURE_H
-
-#include "Common.h"
-#include "Unit.h"
-#include "UpdateMask.h"
-#include "ItemPrototype.h"
-#include "LootMgr.h"
-#include "Database/DatabaseEnv.h"
-#include "Cell.h"
-
-struct SpellEntry;
-
-class CreatureAI;
-class Quest;
-class Player;
-class WorldSession;
-
-enum Gossip_Option
-{
- GOSSIP_OPTION_NONE = 0, //UNIT_NPC_FLAG_NONE = 0,
- GOSSIP_OPTION_GOSSIP = 1, //UNIT_NPC_FLAG_GOSSIP = 1,
- GOSSIP_OPTION_QUESTGIVER = 2, //UNIT_NPC_FLAG_QUESTGIVER = 2,
- GOSSIP_OPTION_VENDOR = 3, //UNIT_NPC_FLAG_VENDOR = 4,
- GOSSIP_OPTION_TAXIVENDOR = 4, //UNIT_NPC_FLAG_TAXIVENDOR = 8,
- GOSSIP_OPTION_TRAINER = 5, //UNIT_NPC_FLAG_TRAINER = 16,
- GOSSIP_OPTION_SPIRITHEALER = 6, //UNIT_NPC_FLAG_SPIRITHEALER = 32,
- GOSSIP_OPTION_SPIRITGUIDE = 7, //UNIT_NPC_FLAG_SPIRITGUIDE = 64,
- GOSSIP_OPTION_INNKEEPER = 8, //UNIT_NPC_FLAG_INNKEEPER = 128,
- GOSSIP_OPTION_BANKER = 9, //UNIT_NPC_FLAG_BANKER = 256,
- GOSSIP_OPTION_PETITIONER = 10, //UNIT_NPC_FLAG_PETITIONER = 512,
- GOSSIP_OPTION_TABARDDESIGNER = 11, //UNIT_NPC_FLAG_TABARDDESIGNER = 1024,
- GOSSIP_OPTION_BATTLEFIELD = 12, //UNIT_NPC_FLAG_BATTLEFIELDPERSON = 2048,
- GOSSIP_OPTION_AUCTIONEER = 13, //UNIT_NPC_FLAG_AUCTIONEER = 4096,
- GOSSIP_OPTION_STABLEPET = 14, //UNIT_NPC_FLAG_STABLE = 8192,
- GOSSIP_OPTION_ARMORER = 15, //UNIT_NPC_FLAG_ARMORER = 16384,
- GOSSIP_OPTION_UNLEARNTALENTS = 16, //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
- GOSSIP_OPTION_UNLEARNPETSKILLS = 17 //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
-};
-
-enum Gossip_Guard
-{
- GOSSIP_GUARD_BANK = 32,
- GOSSIP_GUARD_RIDE = 33,
- GOSSIP_GUARD_GUILD = 34,
- GOSSIP_GUARD_INN = 35,
- GOSSIP_GUARD_MAIL = 36,
- GOSSIP_GUARD_AUCTION = 37,
- GOSSIP_GUARD_WEAPON = 38,
- GOSSIP_GUARD_STABLE = 39,
- GOSSIP_GUARD_BATTLE = 40,
- GOSSIP_GUARD_SPELLTRAINER = 41,
- GOSSIP_GUARD_SKILLTRAINER = 42
-};
-
-enum Gossip_Guard_Spell
-{
- GOSSIP_GUARD_SPELL_WARRIOR = 64,
- GOSSIP_GUARD_SPELL_PALADIN = 65,
- GOSSIP_GUARD_SPELL_HUNTER = 66,
- GOSSIP_GUARD_SPELL_ROGUE = 67,
- GOSSIP_GUARD_SPELL_PRIEST = 68,
- GOSSIP_GUARD_SPELL_UNKNOWN1 = 69,
- GOSSIP_GUARD_SPELL_SHAMAN = 70,
- GOSSIP_GUARD_SPELL_MAGE = 71,
- GOSSIP_GUARD_SPELL_WARLOCK = 72,
- GOSSIP_GUARD_SPELL_UNKNOWN2 = 73,
- GOSSIP_GUARD_SPELL_DRUID = 74
-};
-
-enum Gossip_Guard_Skill
-{
- GOSSIP_GUARD_SKILL_ALCHEMY = 80,
- GOSSIP_GUARD_SKILL_BLACKSMITH = 81,
- GOSSIP_GUARD_SKILL_COOKING = 82,
- GOSSIP_GUARD_SKILL_ENCHANT = 83,
- GOSSIP_GUARD_SKILL_FIRSTAID = 84,
- GOSSIP_GUARD_SKILL_FISHING = 85,
- GOSSIP_GUARD_SKILL_HERBALISM = 86,
- GOSSIP_GUARD_SKILL_LEATHER = 87,
- GOSSIP_GUARD_SKILL_MINING = 88,
- GOSSIP_GUARD_SKILL_SKINNING = 89,
- GOSSIP_GUARD_SKILL_TAILORING = 90,
- GOSSIP_GUARD_SKILL_ENGINERING = 91
-};
-
-struct GossipOption
-{
- uint32 Id;
- uint32 GossipId;
- uint32 NpcFlag;
- uint32 Icon;
- uint32 Action;
- std::string Option;
-};
-
-enum CreatureFlagsExtra
-{
- CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
- CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility)
- CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry
- CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry
- CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block
- CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks
- CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP
-};
-
-// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
-#if defined( __GNUC__ )
-#pragma pack(1)
-#else
-#pragma pack(push,1)
-#endif
-
-// from `creature_template` table
-struct CreatureInfo
-{
- uint32 Entry;
- uint32 HeroicEntry;
- uint32 DisplayID_A;
- uint32 DisplayID_A2;
- uint32 DisplayID_H;
- uint32 DisplayID_H2;
- char* Name;
- char* SubName;
- char* IconName;
- uint32 minlevel;
- uint32 maxlevel;
- uint32 minhealth;
- uint32 maxhealth;
- uint32 minmana;
- uint32 maxmana;
- uint32 armor;
- uint32 faction_A;
- uint32 faction_H;
- uint32 npcflag;
- float speed;
- float scale;
- uint32 rank;
- float mindmg;
- float maxdmg;
- uint32 dmgschool;
- uint32 attackpower;
- uint32 baseattacktime;
- uint32 rangeattacktime;
- uint32 Flags;
- uint32 dynamicflags;
- uint32 family;
- uint32 trainer_type;
- uint32 trainer_spell;
- uint32 classNum;
- uint32 race;
- float minrangedmg;
- float maxrangedmg;
- uint32 rangedattackpower;
- uint32 type;
- uint32 flag1;
- uint32 lootid;
- uint32 pickpocketLootId;
- uint32 SkinLootId;
- int32 resistance1;
- int32 resistance2;
- int32 resistance3;
- int32 resistance4;
- int32 resistance5;
- int32 resistance6;
- uint32 spell1;
- uint32 spell2;
- uint32 spell3;
- uint32 spell4;
- uint32 PetSpellDataId;
- uint32 mingold;
- uint32 maxgold;
- char const* AIName;
- uint32 MovementType;
- uint32 InhabitType;
- bool RacialLeader;
- bool RegenHealth;
- uint32 equipmentId;
- uint32 MechanicImmuneMask;
- uint32 flags_extra;
- char const* ScriptName;
-};
-
-struct CreatureLocale
-{
- std::vector<std::string> Name;
- std::vector<std::string> SubName;
-};
-
-struct EquipmentInfo
-{
- uint32 entry;
- uint32 equipmodel[3];
- uint32 equipinfo[3];
- uint32 equipslot[3];
-};
-
-// from `creature` table
-struct CreatureData
-{
- uint32 id; // entry in creature_template
- uint16 mapid;
- uint32 displayid;
- int32 equipmentId;
- float posX;
- float posY;
- float posZ;
- float orientation;
- uint32 spawntimesecs;
- float spawndist;
- uint32 currentwaypoint;
- uint32 curhealth;
- uint32 curmana;
- bool is_dead;
- uint8 movementType;
- uint8 spawnMask;
-};
-
-struct CreatureDataAddonAura
-{
- uint16 spell_id;
- uint8 effect_idx;
-};
-
-// from `creature_addon` table
-struct CreatureDataAddon
-{
- uint32 guidOrEntry;
- uint32 mount;
- uint32 bytes0;
- uint32 bytes1;
- uint32 bytes2;
- uint32 emote;
- uint32 move_flags;
- CreatureDataAddonAura const* auras; // loaded as char* "spell1 eff1 spell2 eff2 ... "
-};
-
-struct CreatureModelInfo
-{
- uint32 modelid;
- float bounding_radius;
- float combat_reach;
- uint8 gender;
- uint32 modelid_other_gender;
-};
-
-enum InhabitTypeValues
-{
- INHABIT_GROUND = 1,
- INHABIT_WATER = 2,
- INHABIT_AIR = 4,
- INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR
-};
-
-// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
-#if defined( __GNUC__ )
-#pragma pack()
-#else
-#pragma pack(pop)
-#endif
-
-// Vendors
-struct VendorItem
-{
- VendorItem(uint32 _item, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost)
- : item(_item), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost) {}
-
- uint32 item;
- uint32 maxcount; // 0 for infinity item amount
- uint32 incrtime; // time for restore items amount if maxcount != 0
- uint32 ExtendedCost;
-};
-typedef std::vector<VendorItem*> VendorItemList;
-
-struct VendorItemData
-{
- VendorItemList m_items;
-
- VendorItem* GetItem(uint32 slot) const
- {
- if(slot>=m_items.size()) return NULL;
- return m_items[slot];
- }
- bool Empty() const { return m_items.empty(); }
- uint8 GetItemCount() const { return m_items.size(); }
- void AddItem( uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost)
- {
- m_items.push_back(new VendorItem(item, maxcount, ptime, ExtendedCost));
- }
- bool RemoveItem( uint32 item_id );
- VendorItem const* FindItem(uint32 item_id) const;
- size_t FindItemSlot(uint32 item_id) const;
-
- void Clear()
- {
- for (VendorItemList::iterator itr = m_items.begin(); itr != m_items.end(); ++itr)
- delete (*itr);
- }
-};
-
-struct VendorItemCount
-{
- explicit VendorItemCount(uint32 _item, uint32 _count)
- : itemId(_item), count(_count), lastIncrementTime(time(NULL)) {}
-
- uint32 itemId;
- uint32 count;
- time_t lastIncrementTime;
-};
-
-typedef std::list<VendorItemCount> VendorItemCounts;
-
-struct TrainerSpell
-{
- uint32 spell;
- uint32 spellcost;
- uint32 reqskill;
- uint32 reqskillvalue;
- uint32 reqlevel;
-};
-
-typedef std::vector<TrainerSpell*> TrainerSpellList;
-
-struct TrainerSpellData
-{
- TrainerSpellData() : trainerType(0) {}
-
- TrainerSpellList spellList;
- uint32 trainerType; // trainer type based at trainer spells, can be different from creature_template value.
- // req. for correct show non-prof. trainers like weaponmaster, allowed values 0 and 2.
-
- void Clear();
- TrainerSpell const* Find(uint32 spell_id) const;
-};
-
-typedef std::list<GossipOption> GossipOptionList;
-
-typedef std::map<uint32,time_t> CreatureSpellCooldowns;
-
-// max different by z coordinate for creature aggro reaction
-#define CREATURE_Z_ATTACK_RANGE 3
-
-#define MAX_VENDOR_ITEMS 255 // Limitation in item count field size in SMSG_LIST_INVENTORY
-
-class MANGOS_DLL_SPEC Creature : public Unit
-{
- CreatureAI *i_AI;
-
- public:
-
- explicit Creature();
- virtual ~Creature();
-
- void AddToWorld();
- void RemoveFromWorld();
-
- bool Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, const CreatureData *data = NULL);
- bool LoadCreaturesAddon(bool reload = false);
- void SelectLevel(const CreatureInfo *cinfo);
- void LoadEquipment(uint32 equip_entry, bool force=false);
-
- uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; }
- char const* GetSubName() const { return GetCreatureInfo()->SubName; }
-
- void Update( uint32 time ); // overwrited Unit::Update
- void GetRespawnCoord(float &x, float &y, float &z, float* ori = NULL, float* dist =NULL) const;
- uint32 GetEquipmentId() const { return m_equipmentId; }
-
- bool isPet() const { return m_isPet; }
- void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; }
- bool isTotem() const { return m_isTotem; }
- bool isRacialLeader() const { return GetCreatureInfo()->RacialLeader; }
- bool isCivilian() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN; }
- bool canWalk() const { return GetCreatureInfo()->InhabitType & INHABIT_GROUND; }
- bool canSwim() const { return GetCreatureInfo()->InhabitType & INHABIT_WATER; }
- bool canFly() const { return GetCreatureInfo()->InhabitType & INHABIT_AIR; }
- ///// TODO RENAME THIS!!!!!
- bool isCanTrainingOf(Player* player, bool msg) const;
- bool isCanIneractWithBattleMaster(Player* player, bool msg) const;
- bool isCanTrainingAndResetTalentsOf(Player* pPlayer) const;
- bool IsOutOfThreatArea(Unit* pVictim) const;
- bool IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges = false);
- // redefine Unit::IsImmunedToSpell
- bool IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const;
- // redefine Unit::IsImmunedToSpellEffect
- bool isElite() const
- {
- if(isPet())
- return false;
-
- uint32 rank = GetCreatureInfo()->rank;
- return rank != CREATURE_ELITE_NORMAL && rank != CREATURE_ELITE_RARE;
- }
-
- bool isWorldBoss() const
- {
- if(isPet())
- return false;
-
- return GetCreatureInfo()->rank == CREATURE_ELITE_WORLDBOSS;
- }
-
- uint32 getLevelForTarget(Unit const* target) const; // overwrite Unit::getLevelForTarget for boss level support
-
- bool IsInEvadeMode() const;
-
- bool AIM_Initialize();
-
- void AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type);
- CreatureAI* AI() { return i_AI; }
-
- uint32 GetShieldBlockValue() const //dunno mob block value
- {
- return (getLevel()/2 + uint32(GetStat(STAT_STRENGTH)/20));
- }
-
- SpellSchoolMask GetMeleeDamageSchoolMask() const { return m_meleeDamageSchoolMask; }
- void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); }
-
- void _AddCreatureSpellCooldown(uint32 spell_id, time_t end_time);
- void _AddCreatureCategoryCooldown(uint32 category, time_t apply_time);
- void AddCreatureSpellCooldown(uint32 spellid);
- bool HasSpellCooldown(uint32 spell_id) const;
- bool HasCategoryCooldown(uint32 spell_id) const;
-
- bool HasSpell(uint32 spellID) const;
-
- bool UpdateEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
- bool UpdateStats(Stats stat);
- bool UpdateAllStats();
- void UpdateResistances(uint32 school);
- void UpdateArmor();
- void UpdateMaxHealth();
- void UpdateMaxPower(Powers power);
- void UpdateAttackPowerAndDamage(bool ranged = false);
- void UpdateDamagePhysical(WeaponAttackType attType);
- uint32 GetCurrentEquipmentId() { return m_equipmentId; }
- float GetSpellDamageMod(int32 Rank);
-
- VendorItemData const* GetVendorItems() const;
- uint32 GetVendorItemCurrentCount(VendorItem const* vItem);
- uint32 UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count);
-
- TrainerSpellData const* GetTrainerSpells() const;
-
- CreatureInfo const *GetCreatureInfo() const { return m_creatureInfo; }
- CreatureDataAddon const* GetCreatureAddon() const;
- char const* GetScriptName() const;
-
- void prepareGossipMenu( Player *pPlayer,uint32 gossipid );
- void sendPreparedGossip( Player* player);
- void OnGossipSelect(Player* player, uint32 option);
- void OnPoiSelect(Player* player, GossipOption const *gossip);
-
- uint32 GetGossipTextId(uint32 action, uint32 zoneid);
- uint32 GetNpcTextId();
- void LoadGossipOptions();
- GossipOption const* GetGossipOption( uint32 id ) const;
- void addGossipOption(GossipOption const& gso) { m_goptions.push_back(gso); }
-
- void setEmoteState(uint8 emote) { m_emoteState = emote; };
- void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); }
- void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); }
- void TextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(text,TargetGuid,IsBossEmote); }
- void Whisper(const char* text, uint64 receiver, bool IsBossWhisper = false) { MonsterWhisper(text,receiver,IsBossWhisper); }
- void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
- void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
- void TextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(textId,TargetGuid,IsBossEmote); }
- void Whisper(int32 textId, uint64 receiver, bool IsBossWhisper = false) { MonsterWhisper(textId,receiver,IsBossWhisper); }
-
- void setDeathState(DeathState s); // overwrite virtual Unit::setDeathState
-
- bool LoadFromDB(uint32 guid, Map *map);
- void SaveToDB();
- // overwrited in Pet
- virtual void SaveToDB(uint32 mapid, uint8 spawnMask);
- virtual void DeleteFromDB(); // overwrited in Pet
-
- Loot loot;
- bool lootForPickPocketed;
- bool lootForBody;
- Player *GetLootRecipient() const;
- bool hasLootRecipient() const { return m_lootRecipient!=0; }
-
- void SetLootRecipient (Unit* unit);
- void AllLootRemovedFromCorpse();
-
- SpellEntry const *reachWithSpellAttack(Unit *pVictim);
- SpellEntry const *reachWithSpellCure(Unit *pVictim);
-
- uint32 m_spells[CREATURE_MAX_SPELLS];
- CreatureSpellCooldowns m_CreatureSpellCooldowns;
- CreatureSpellCooldowns m_CreatureCategoryCooldowns;
- uint32 m_GlobalCooldown;
-
- float GetAttackDistance(Unit const* pl) const;
-
- void CallAssistence();
- void SetNoCallAssistence(bool val) { m_AlreadyCallAssistence = val; }
- void DoFleeToGetAssistance(float radius = 50);
-
- MovementGeneratorType GetDefaultMovementType() const { return m_defaultMovementType; }
- void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; }
-
- // for use only in LoadHelper, Map::Add Map::CreatureCellRelocation
- Cell const& GetCurrentCell() const { return m_currentCell; }
- void SetCurrentCell(Cell const& cell) { m_currentCell = cell; }
-
- bool IsVisibleInGridForPlayer(Player* pl) const;
-
- void RemoveCorpse();
-
- time_t const& GetRespawnTime() const { return m_respawnTime; }
- time_t GetRespawnTimeEx() const;
- void SetRespawnTime(uint32 respawn) { m_respawnTime = respawn ? time(NULL) + respawn : 0; }
- void Respawn();
- void SaveRespawnTime();
-
- uint32 GetRespawnDelay() const { return m_respawnDelay; }
- void SetRespawnDelay(uint32 delay) { m_respawnDelay = delay; }
-
- float GetRespawnRadius() const { return m_respawnradius; }
- void SetRespawnRadius(float dist) { m_respawnradius = dist; }
-
- uint32 m_groupLootTimer; // (msecs)timer used for group loot
- uint64 lootingGroupLeaderGUID; // used to find group which is looting corpse
-
- void SendZoneUnderAttackMessage(Player* attacker);
-
- bool hasQuest(uint32 quest_id) const;
- bool hasInvolvedQuest(uint32 quest_id) const;
-
- GridReference<Creature> &GetGridRef() { return m_gridRef; }
- bool isRegeneratingHealth() { return m_regenHealth; }
- virtual uint8 GetPetAutoSpellSize() const { return CREATURE_MAX_SPELLS; }
- virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const
- {
- if (pos >= CREATURE_MAX_SPELLS || m_charmInfo->GetCharmSpell(pos)->active != ACT_ENABLED)
- return 0;
- else
- return m_charmInfo->GetCharmSpell(pos)->spellId;
- }
-
- void SetCombatStartPosition(float x, float y, float z) { CombatStartX = x; CombatStartY = y; CombatStartZ = z; }
- void GetCombatStartPosition(float &x, float &y, float &z) { x = CombatStartX; y = CombatStartY; z = CombatStartZ; }
-
- protected:
- bool CreateFromProto(uint32 guidlow,uint32 Entry,uint32 team, const CreatureData *data = NULL);
- bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
-
- // vendor items
- VendorItemCounts m_vendorItemCounts;
-
- void _RealtimeSetCreatureInfo();
-
- static float _GetHealthMod(int32 Rank);
- static float _GetDamageMod(int32 Rank);
-
- uint32 m_lootMoney;
- uint64 m_lootRecipient;
-
- /// Timers
- uint32 m_deathTimer; // (msecs)timer for death or corpse disappearance
- time_t m_respawnTime; // (secs) time of next respawn
- uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning
- uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance
- float m_respawnradius;
-
- bool m_gossipOptionLoaded;
- GossipOptionList m_goptions;
-
- uint8 m_emoteState;
- bool m_isPet; // set only in Pet::Pet
- bool m_isTotem; // set only in Totem::Totem
- void RegenerateMana();
- void RegenerateHealth();
- uint32 m_regenTimer;
- MovementGeneratorType m_defaultMovementType;
- Cell m_currentCell; // store current cell where creature listed
- uint32 m_DBTableGuid; ///< For new or temporary creatures is 0 for saved it is lowguid
- uint32 m_equipmentId;
-
- bool m_AlreadyCallAssistence;
- bool m_regenHealth;
- bool m_AI_locked;
- bool m_isDeadByDefault;
-
- SpellSchoolMask m_meleeDamageSchoolMask;
- uint32 m_originalEntry;
-
- float CombatStartX;
- float CombatStartY;
- float CombatStartZ;
- private:
- GridReference<Creature> m_gridRef;
- CreatureInfo const* m_creatureInfo; // in heroic mode can different from ObjMgr::GetCreatureTemplate(GetEntry())
-};
-#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
+ */
+
+#ifndef MANGOSSERVER_CREATURE_H
+#define MANGOSSERVER_CREATURE_H
+
+#include "Common.h"
+#include "Unit.h"
+#include "UpdateMask.h"
+#include "ItemPrototype.h"
+#include "LootMgr.h"
+#include "Database/DatabaseEnv.h"
+#include "Cell.h"
+
+struct SpellEntry;
+
+class CreatureAI;
+class Quest;
+class Player;
+class WorldSession;
+
+enum Gossip_Option
+{
+ GOSSIP_OPTION_NONE = 0, //UNIT_NPC_FLAG_NONE = 0,
+ GOSSIP_OPTION_GOSSIP = 1, //UNIT_NPC_FLAG_GOSSIP = 1,
+ GOSSIP_OPTION_QUESTGIVER = 2, //UNIT_NPC_FLAG_QUESTGIVER = 2,
+ GOSSIP_OPTION_VENDOR = 3, //UNIT_NPC_FLAG_VENDOR = 4,
+ GOSSIP_OPTION_TAXIVENDOR = 4, //UNIT_NPC_FLAG_TAXIVENDOR = 8,
+ GOSSIP_OPTION_TRAINER = 5, //UNIT_NPC_FLAG_TRAINER = 16,
+ GOSSIP_OPTION_SPIRITHEALER = 6, //UNIT_NPC_FLAG_SPIRITHEALER = 32,
+ GOSSIP_OPTION_SPIRITGUIDE = 7, //UNIT_NPC_FLAG_SPIRITGUIDE = 64,
+ GOSSIP_OPTION_INNKEEPER = 8, //UNIT_NPC_FLAG_INNKEEPER = 128,
+ GOSSIP_OPTION_BANKER = 9, //UNIT_NPC_FLAG_BANKER = 256,
+ GOSSIP_OPTION_PETITIONER = 10, //UNIT_NPC_FLAG_PETITIONER = 512,
+ GOSSIP_OPTION_TABARDDESIGNER = 11, //UNIT_NPC_FLAG_TABARDDESIGNER = 1024,
+ GOSSIP_OPTION_BATTLEFIELD = 12, //UNIT_NPC_FLAG_BATTLEFIELDPERSON = 2048,
+ GOSSIP_OPTION_AUCTIONEER = 13, //UNIT_NPC_FLAG_AUCTIONEER = 4096,
+ GOSSIP_OPTION_STABLEPET = 14, //UNIT_NPC_FLAG_STABLE = 8192,
+ GOSSIP_OPTION_ARMORER = 15, //UNIT_NPC_FLAG_ARMORER = 16384,
+ GOSSIP_OPTION_UNLEARNTALENTS = 16, //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
+ GOSSIP_OPTION_UNLEARNPETSKILLS = 17 //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
+};
+
+enum Gossip_Guard
+{
+ GOSSIP_GUARD_BANK = 32,
+ GOSSIP_GUARD_RIDE = 33,
+ GOSSIP_GUARD_GUILD = 34,
+ GOSSIP_GUARD_INN = 35,
+ GOSSIP_GUARD_MAIL = 36,
+ GOSSIP_GUARD_AUCTION = 37,
+ GOSSIP_GUARD_WEAPON = 38,
+ GOSSIP_GUARD_STABLE = 39,
+ GOSSIP_GUARD_BATTLE = 40,
+ GOSSIP_GUARD_SPELLTRAINER = 41,
+ GOSSIP_GUARD_SKILLTRAINER = 42
+};
+
+enum Gossip_Guard_Spell
+{
+ GOSSIP_GUARD_SPELL_WARRIOR = 64,
+ GOSSIP_GUARD_SPELL_PALADIN = 65,
+ GOSSIP_GUARD_SPELL_HUNTER = 66,
+ GOSSIP_GUARD_SPELL_ROGUE = 67,
+ GOSSIP_GUARD_SPELL_PRIEST = 68,
+ GOSSIP_GUARD_SPELL_UNKNOWN1 = 69,
+ GOSSIP_GUARD_SPELL_SHAMAN = 70,
+ GOSSIP_GUARD_SPELL_MAGE = 71,
+ GOSSIP_GUARD_SPELL_WARLOCK = 72,
+ GOSSIP_GUARD_SPELL_UNKNOWN2 = 73,
+ GOSSIP_GUARD_SPELL_DRUID = 74
+};
+
+enum Gossip_Guard_Skill
+{
+ GOSSIP_GUARD_SKILL_ALCHEMY = 80,
+ GOSSIP_GUARD_SKILL_BLACKSMITH = 81,
+ GOSSIP_GUARD_SKILL_COOKING = 82,
+ GOSSIP_GUARD_SKILL_ENCHANT = 83,
+ GOSSIP_GUARD_SKILL_FIRSTAID = 84,
+ GOSSIP_GUARD_SKILL_FISHING = 85,
+ GOSSIP_GUARD_SKILL_HERBALISM = 86,
+ GOSSIP_GUARD_SKILL_LEATHER = 87,
+ GOSSIP_GUARD_SKILL_MINING = 88,
+ GOSSIP_GUARD_SKILL_SKINNING = 89,
+ GOSSIP_GUARD_SKILL_TAILORING = 90,
+ GOSSIP_GUARD_SKILL_ENGINERING = 91
+};
+
+struct GossipOption
+{
+ uint32 Id;
+ uint32 GossipId;
+ uint32 NpcFlag;
+ uint32 Icon;
+ uint32 Action;
+ std::string Option;
+};
+
+enum CreatureFlagsExtra
+{
+ CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
+ CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility)
+ CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry
+ CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry
+ CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block
+ CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks
+ CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP
+};
+
+// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack(1)
+#else
+#pragma pack(push,1)
+#endif
+
+// from `creature_template` table
+struct CreatureInfo
+{
+ uint32 Entry;
+ uint32 HeroicEntry;
+ uint32 DisplayID_A;
+ uint32 DisplayID_A2;
+ uint32 DisplayID_H;
+ uint32 DisplayID_H2;
+ char* Name;
+ char* SubName;
+ char* IconName;
+ uint32 minlevel;
+ uint32 maxlevel;
+ uint32 minhealth;
+ uint32 maxhealth;
+ uint32 minmana;
+ uint32 maxmana;
+ uint32 armor;
+ uint32 faction_A;
+ uint32 faction_H;
+ uint32 npcflag;
+ float speed;
+ float scale;
+ uint32 rank;
+ float mindmg;
+ float maxdmg;
+ uint32 dmgschool;
+ uint32 attackpower;
+ uint32 baseattacktime;
+ uint32 rangeattacktime;
+ uint32 Flags;
+ uint32 dynamicflags;
+ uint32 family;
+ uint32 trainer_type;
+ uint32 trainer_spell;
+ uint32 classNum;
+ uint32 race;
+ float minrangedmg;
+ float maxrangedmg;
+ uint32 rangedattackpower;
+ uint32 type;
+ uint32 flag1;
+ uint32 lootid;
+ uint32 pickpocketLootId;
+ uint32 SkinLootId;
+ int32 resistance1;
+ int32 resistance2;
+ int32 resistance3;
+ int32 resistance4;
+ int32 resistance5;
+ int32 resistance6;
+ uint32 spell1;
+ uint32 spell2;
+ uint32 spell3;
+ uint32 spell4;
+ uint32 PetSpellDataId;
+ uint32 mingold;
+ uint32 maxgold;
+ char const* AIName;
+ uint32 MovementType;
+ uint32 InhabitType;
+ bool RacialLeader;
+ bool RegenHealth;
+ uint32 equipmentId;
+ uint32 MechanicImmuneMask;
+ uint32 flags_extra;
+ char const* ScriptName;
+};
+
+struct CreatureLocale
+{
+ std::vector<std::string> Name;
+ std::vector<std::string> SubName;
+};
+
+struct EquipmentInfo
+{
+ uint32 entry;
+ uint32 equipmodel[3];
+ uint32 equipinfo[3];
+ uint32 equipslot[3];
+};
+
+// from `creature` table
+struct CreatureData
+{
+ uint32 id; // entry in creature_template
+ uint16 mapid;
+ uint32 displayid;
+ int32 equipmentId;
+ float posX;
+ float posY;
+ float posZ;
+ float orientation;
+ uint32 spawntimesecs;
+ float spawndist;
+ uint32 currentwaypoint;
+ uint32 curhealth;
+ uint32 curmana;
+ bool is_dead;
+ uint8 movementType;
+ uint8 spawnMask;
+};
+
+struct CreatureDataAddonAura
+{
+ uint16 spell_id;
+ uint8 effect_idx;
+};
+
+// from `creature_addon` table
+struct CreatureDataAddon
+{
+ uint32 guidOrEntry;
+ uint32 mount;
+ uint32 bytes0;
+ uint32 bytes1;
+ uint32 bytes2;
+ uint32 emote;
+ uint32 move_flags;
+ CreatureDataAddonAura const* auras; // loaded as char* "spell1 eff1 spell2 eff2 ... "
+};
+
+struct CreatureModelInfo
+{
+ uint32 modelid;
+ float bounding_radius;
+ float combat_reach;
+ uint8 gender;
+ uint32 modelid_other_gender;
+};
+
+enum InhabitTypeValues
+{
+ INHABIT_GROUND = 1,
+ INHABIT_WATER = 2,
+ INHABIT_AIR = 4,
+ INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR
+};
+
+// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack()
+#else
+#pragma pack(pop)
+#endif
+
+// Vendors
+struct VendorItem
+{
+ VendorItem(uint32 _item, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost)
+ : item(_item), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost) {}
+
+ uint32 item;
+ uint32 maxcount; // 0 for infinity item amount
+ uint32 incrtime; // time for restore items amount if maxcount != 0
+ uint32 ExtendedCost;
+};
+typedef std::vector<VendorItem*> VendorItemList;
+
+struct VendorItemData
+{
+ VendorItemList m_items;
+
+ VendorItem* GetItem(uint32 slot) const
+ {
+ if(slot>=m_items.size()) return NULL;
+ return m_items[slot];
+ }
+ bool Empty() const { return m_items.empty(); }
+ uint8 GetItemCount() const { return m_items.size(); }
+ void AddItem( uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost)
+ {
+ m_items.push_back(new VendorItem(item, maxcount, ptime, ExtendedCost));
+ }
+ bool RemoveItem( uint32 item_id );
+ VendorItem const* FindItem(uint32 item_id) const;
+ size_t FindItemSlot(uint32 item_id) const;
+
+ void Clear()
+ {
+ for (VendorItemList::iterator itr = m_items.begin(); itr != m_items.end(); ++itr)
+ delete (*itr);
+ }
+};
+
+struct VendorItemCount
+{
+ explicit VendorItemCount(uint32 _item, uint32 _count)
+ : itemId(_item), count(_count), lastIncrementTime(time(NULL)) {}
+
+ uint32 itemId;
+ uint32 count;
+ time_t lastIncrementTime;
+};
+
+typedef std::list<VendorItemCount> VendorItemCounts;
+
+struct TrainerSpell
+{
+ uint32 spell;
+ uint32 spellcost;
+ uint32 reqskill;
+ uint32 reqskillvalue;
+ uint32 reqlevel;
+};
+
+typedef std::vector<TrainerSpell*> TrainerSpellList;
+
+struct TrainerSpellData
+{
+ TrainerSpellData() : trainerType(0) {}
+
+ TrainerSpellList spellList;
+ uint32 trainerType; // trainer type based at trainer spells, can be different from creature_template value.
+ // req. for correct show non-prof. trainers like weaponmaster, allowed values 0 and 2.
+
+ void Clear();
+ TrainerSpell const* Find(uint32 spell_id) const;
+};
+
+typedef std::list<GossipOption> GossipOptionList;
+
+typedef std::map<uint32,time_t> CreatureSpellCooldowns;
+
+// max different by z coordinate for creature aggro reaction
+#define CREATURE_Z_ATTACK_RANGE 3
+
+#define MAX_VENDOR_ITEMS 255 // Limitation in item count field size in SMSG_LIST_INVENTORY
+
+class MANGOS_DLL_SPEC Creature : public Unit
+{
+ CreatureAI *i_AI;
+
+ public:
+
+ explicit Creature();
+ virtual ~Creature();
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ bool Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, const CreatureData *data = NULL);
+ bool LoadCreaturesAddon(bool reload = false);
+ void SelectLevel(const CreatureInfo *cinfo);
+ void LoadEquipment(uint32 equip_entry, bool force=false);
+
+ uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; }
+ char const* GetSubName() const { return GetCreatureInfo()->SubName; }
+
+ void Update( uint32 time ); // overwrited Unit::Update
+ void GetRespawnCoord(float &x, float &y, float &z, float* ori = NULL, float* dist =NULL) const;
+ uint32 GetEquipmentId() const { return m_equipmentId; }
+
+ bool isPet() const { return m_isPet; }
+ void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; }
+ bool isTotem() const { return m_isTotem; }
+ bool isRacialLeader() const { return GetCreatureInfo()->RacialLeader; }
+ bool isCivilian() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN; }
+ bool canWalk() const { return GetCreatureInfo()->InhabitType & INHABIT_GROUND; }
+ bool canSwim() const { return GetCreatureInfo()->InhabitType & INHABIT_WATER; }
+ bool canFly() const { return GetCreatureInfo()->InhabitType & INHABIT_AIR; }
+ ///// TODO RENAME THIS!!!!!
+ bool isCanTrainingOf(Player* player, bool msg) const;
+ bool isCanIneractWithBattleMaster(Player* player, bool msg) const;
+ bool isCanTrainingAndResetTalentsOf(Player* pPlayer) const;
+ bool IsOutOfThreatArea(Unit* pVictim) const;
+ bool IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges = false);
+ // redefine Unit::IsImmunedToSpell
+ bool IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const;
+ // redefine Unit::IsImmunedToSpellEffect
+ bool isElite() const
+ {
+ if(isPet())
+ return false;
+
+ uint32 rank = GetCreatureInfo()->rank;
+ return rank != CREATURE_ELITE_NORMAL && rank != CREATURE_ELITE_RARE;
+ }
+
+ bool isWorldBoss() const
+ {
+ if(isPet())
+ return false;
+
+ return GetCreatureInfo()->rank == CREATURE_ELITE_WORLDBOSS;
+ }
+
+ uint32 getLevelForTarget(Unit const* target) const; // overwrite Unit::getLevelForTarget for boss level support
+
+ bool IsInEvadeMode() const;
+
+ bool AIM_Initialize();
+
+ void AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type);
+ CreatureAI* AI() { return i_AI; }
+
+ uint32 GetShieldBlockValue() const //dunno mob block value
+ {
+ return (getLevel()/2 + uint32(GetStat(STAT_STRENGTH)/20));
+ }
+
+ SpellSchoolMask GetMeleeDamageSchoolMask() const { return m_meleeDamageSchoolMask; }
+ void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); }
+
+ void _AddCreatureSpellCooldown(uint32 spell_id, time_t end_time);
+ void _AddCreatureCategoryCooldown(uint32 category, time_t apply_time);
+ void AddCreatureSpellCooldown(uint32 spellid);
+ bool HasSpellCooldown(uint32 spell_id) const;
+ bool HasCategoryCooldown(uint32 spell_id) const;
+
+ bool HasSpell(uint32 spellID) const;
+
+ bool UpdateEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
+ bool UpdateStats(Stats stat);
+ bool UpdateAllStats();
+ void UpdateResistances(uint32 school);
+ void UpdateArmor();
+ void UpdateMaxHealth();
+ void UpdateMaxPower(Powers power);
+ void UpdateAttackPowerAndDamage(bool ranged = false);
+ void UpdateDamagePhysical(WeaponAttackType attType);
+ uint32 GetCurrentEquipmentId() { return m_equipmentId; }
+ float GetSpellDamageMod(int32 Rank);
+
+ VendorItemData const* GetVendorItems() const;
+ uint32 GetVendorItemCurrentCount(VendorItem const* vItem);
+ uint32 UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count);
+
+ TrainerSpellData const* GetTrainerSpells() const;
+
+ CreatureInfo const *GetCreatureInfo() const { return m_creatureInfo; }
+ CreatureDataAddon const* GetCreatureAddon() const;
+ char const* GetScriptName() const;
+
+ void prepareGossipMenu( Player *pPlayer,uint32 gossipid );
+ void sendPreparedGossip( Player* player);
+ void OnGossipSelect(Player* player, uint32 option);
+ void OnPoiSelect(Player* player, GossipOption const *gossip);
+
+ uint32 GetGossipTextId(uint32 action, uint32 zoneid);
+ uint32 GetNpcTextId();
+ void LoadGossipOptions();
+ GossipOption const* GetGossipOption( uint32 id ) const;
+ void addGossipOption(GossipOption const& gso) { m_goptions.push_back(gso); }
+
+ void setEmoteState(uint8 emote) { m_emoteState = emote; };
+ void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); }
+ void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); }
+ void TextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(text,TargetGuid,IsBossEmote); }
+ void Whisper(const char* text, uint64 receiver, bool IsBossWhisper = false) { MonsterWhisper(text,receiver,IsBossWhisper); }
+ void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
+ void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
+ void TextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(textId,TargetGuid,IsBossEmote); }
+ void Whisper(int32 textId, uint64 receiver, bool IsBossWhisper = false) { MonsterWhisper(textId,receiver,IsBossWhisper); }
+
+ void setDeathState(DeathState s); // overwrite virtual Unit::setDeathState
+
+ bool LoadFromDB(uint32 guid, Map *map);
+ void SaveToDB();
+ // overwrited in Pet
+ virtual void SaveToDB(uint32 mapid, uint8 spawnMask);
+ virtual void DeleteFromDB(); // overwrited in Pet
+
+ Loot loot;
+ bool lootForPickPocketed;
+ bool lootForBody;
+ Player *GetLootRecipient() const;
+ bool hasLootRecipient() const { return m_lootRecipient!=0; }
+
+ void SetLootRecipient (Unit* unit);
+ void AllLootRemovedFromCorpse();
+
+ SpellEntry const *reachWithSpellAttack(Unit *pVictim);
+ SpellEntry const *reachWithSpellCure(Unit *pVictim);
+
+ uint32 m_spells[CREATURE_MAX_SPELLS];
+ CreatureSpellCooldowns m_CreatureSpellCooldowns;
+ CreatureSpellCooldowns m_CreatureCategoryCooldowns;
+ uint32 m_GlobalCooldown;
+
+ float GetAttackDistance(Unit const* pl) const;
+
+ void CallAssistence();
+ void SetNoCallAssistence(bool val) { m_AlreadyCallAssistence = val; }
+ void DoFleeToGetAssistance(float radius = 50);
+
+ MovementGeneratorType GetDefaultMovementType() const { return m_defaultMovementType; }
+ void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; }
+
+ // for use only in LoadHelper, Map::Add Map::CreatureCellRelocation
+ Cell const& GetCurrentCell() const { return m_currentCell; }
+ void SetCurrentCell(Cell const& cell) { m_currentCell = cell; }
+
+ bool IsVisibleInGridForPlayer(Player* pl) const;
+
+ void RemoveCorpse();
+
+ time_t const& GetRespawnTime() const { return m_respawnTime; }
+ time_t GetRespawnTimeEx() const;
+ void SetRespawnTime(uint32 respawn) { m_respawnTime = respawn ? time(NULL) + respawn : 0; }
+ void Respawn();
+ void SaveRespawnTime();
+
+ uint32 GetRespawnDelay() const { return m_respawnDelay; }
+ void SetRespawnDelay(uint32 delay) { m_respawnDelay = delay; }
+
+ float GetRespawnRadius() const { return m_respawnradius; }
+ void SetRespawnRadius(float dist) { m_respawnradius = dist; }
+
+ uint32 m_groupLootTimer; // (msecs)timer used for group loot
+ uint64 lootingGroupLeaderGUID; // used to find group which is looting corpse
+
+ void SendZoneUnderAttackMessage(Player* attacker);
+
+ bool hasQuest(uint32 quest_id) const;
+ bool hasInvolvedQuest(uint32 quest_id) const;
+
+ GridReference<Creature> &GetGridRef() { return m_gridRef; }
+ bool isRegeneratingHealth() { return m_regenHealth; }
+ virtual uint8 GetPetAutoSpellSize() const { return CREATURE_MAX_SPELLS; }
+ virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const
+ {
+ if (pos >= CREATURE_MAX_SPELLS || m_charmInfo->GetCharmSpell(pos)->active != ACT_ENABLED)
+ return 0;
+ else
+ return m_charmInfo->GetCharmSpell(pos)->spellId;
+ }
+
+ void SetCombatStartPosition(float x, float y, float z) { CombatStartX = x; CombatStartY = y; CombatStartZ = z; }
+ void GetCombatStartPosition(float &x, float &y, float &z) { x = CombatStartX; y = CombatStartY; z = CombatStartZ; }
+
+ protected:
+ bool CreateFromProto(uint32 guidlow,uint32 Entry,uint32 team, const CreatureData *data = NULL);
+ bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
+
+ // vendor items
+ VendorItemCounts m_vendorItemCounts;
+
+ void _RealtimeSetCreatureInfo();
+
+ static float _GetHealthMod(int32 Rank);
+ static float _GetDamageMod(int32 Rank);
+
+ uint32 m_lootMoney;
+ uint64 m_lootRecipient;
+
+ /// Timers
+ uint32 m_deathTimer; // (msecs)timer for death or corpse disappearance
+ time_t m_respawnTime; // (secs) time of next respawn
+ uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning
+ uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance
+ float m_respawnradius;
+
+ bool m_gossipOptionLoaded;
+ GossipOptionList m_goptions;
+
+ uint8 m_emoteState;
+ bool m_isPet; // set only in Pet::Pet
+ bool m_isTotem; // set only in Totem::Totem
+ void RegenerateMana();
+ void RegenerateHealth();
+ uint32 m_regenTimer;
+ MovementGeneratorType m_defaultMovementType;
+ Cell m_currentCell; // store current cell where creature listed
+ uint32 m_DBTableGuid; ///< For new or temporary creatures is 0 for saved it is lowguid
+ uint32 m_equipmentId;
+
+ bool m_AlreadyCallAssistence;
+ bool m_regenHealth;
+ bool m_AI_locked;
+ bool m_isDeadByDefault;
+
+ SpellSchoolMask m_meleeDamageSchoolMask;
+ uint32 m_originalEntry;
+
+ float CombatStartX;
+ float CombatStartY;
+ float CombatStartZ;
+ private:
+ GridReference<Creature> m_gridRef;
+ CreatureInfo const* m_creatureInfo; // in heroic mode can different from ObjMgr::GetCreatureTemplate(GetEntry())
+};
+#endif
diff --git a/src/game/CreatureAISelector.cpp b/src/game/CreatureAISelector.cpp
index a113a3f1c7e..b59ee35c1d8 100644
--- a/src/game/CreatureAISelector.cpp
+++ b/src/game/CreatureAISelector.cpp
@@ -1,118 +1,118 @@
-/*
- * 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 "CreatureAIImpl.h"
-#include "CreatureAISelector.h"
-#include "NullCreatureAI.h"
-#include "Policies/SingletonImp.h"
-#include "MovementGenerator.h"
-#include "ScriptCalls.h"
-#include "Pet.h"
-
-INSTANTIATE_SINGLETON_1(CreatureAIRegistry);
-INSTANTIATE_SINGLETON_1(MovementGeneratorRegistry);
-
-namespace FactorySelector
-{
- CreatureAI* selectAI(Creature *creature)
- {
- // Allow scripting AI for normal creatures and not controlled pets (guardians and mini-pets)
- if((!creature->isPet() || !((Pet*)creature)->isControlled()) && !creature->isCharmed())
- if(CreatureAI* scriptedAI = Script->GetAI(creature))
- return scriptedAI;
-
- CreatureAIRegistry &ai_registry(CreatureAIRepository::Instance());
- assert( creature->GetCreatureInfo() != NULL );
- CreatureInfo const *cinfo=creature->GetCreatureInfo();
-
- const CreatureAICreator *ai_factory = NULL;
-
- std::string ainame=cinfo->AIName;
-
- // select by script name
- if( !ainame.empty())
- ai_factory = ai_registry.GetRegistryItem( ainame.c_str() );
-
- // select by NPC flags
- if(!ai_factory)
- {
- if( creature->isGuard() )
- ai_factory = ai_registry.GetRegistryItem("GuardAI");
- else if(creature->isPet() || creature->isCharmed())
- ai_factory = ai_registry.GetRegistryItem("PetAI");
- else if(creature->isTotem())
- ai_factory = ai_registry.GetRegistryItem("TotemAI");
- }
-
- // select by permit check
- if(!ai_factory)
- {
- int best_val = -1;
- typedef CreatureAIRegistry::RegistryMapType RMT;
- RMT const &l = ai_registry.GetRegisteredItems();
- for( RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter)
- {
- const CreatureAICreator *factory = iter->second;
- const SelectableAI *p = dynamic_cast<const SelectableAI *>(factory);
- assert( p != NULL );
- int val = p->Permit(creature);
- if( val > best_val )
- {
- best_val = val;
- ai_factory = p;
- }
- }
- }
-
- // select NullCreatureAI if not another cases
- ainame = (ai_factory == NULL) ? "NullCreatureAI" : ai_factory->key();
-
- DEBUG_LOG("Creature %u used AI is %s.", creature->GetGUIDLow(), ainame.c_str() );
- return ( ai_factory == NULL ? new NullCreatureAI : ai_factory->Create(creature) );
- }
-
- MovementGenerator* selectMovementGenerator(Creature *creature)
- {
- MovementGeneratorRegistry &mv_registry(MovementGeneratorRepository::Instance());
- assert( creature->GetCreatureInfo() != NULL );
- const MovementGeneratorCreator *mv_factory = mv_registry.GetRegistryItem( creature->GetDefaultMovementType());
-
- /* if( mv_factory == NULL )
- {
- int best_val = -1;
- std::vector<std::string> l;
- mv_registry.GetRegisteredItems(l);
- for( std::vector<std::string>::iterator iter = l.begin(); iter != l.end(); ++iter)
- {
- const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str());
- const SelectableMovement *p = dynamic_cast<const SelectableMovement *>(factory);
- assert( p != NULL );
- int val = p->Permit(creature);
- if( val > best_val )
- {
- best_val = val;
- mv_factory = p;
- }
- }
- }*/
-
- return ( mv_factory == NULL ? NULL : mv_factory->Create(creature) );
-
- }
-}
+/*
+ * 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 "CreatureAIImpl.h"
+#include "CreatureAISelector.h"
+#include "NullCreatureAI.h"
+#include "Policies/SingletonImp.h"
+#include "MovementGenerator.h"
+#include "ScriptCalls.h"
+#include "Pet.h"
+
+INSTANTIATE_SINGLETON_1(CreatureAIRegistry);
+INSTANTIATE_SINGLETON_1(MovementGeneratorRegistry);
+
+namespace FactorySelector
+{
+ CreatureAI* selectAI(Creature *creature)
+ {
+ // Allow scripting AI for normal creatures and not controlled pets (guardians and mini-pets)
+ if((!creature->isPet() || !((Pet*)creature)->isControlled()) && !creature->isCharmed())
+ if(CreatureAI* scriptedAI = Script->GetAI(creature))
+ return scriptedAI;
+
+ CreatureAIRegistry &ai_registry(CreatureAIRepository::Instance());
+ assert( creature->GetCreatureInfo() != NULL );
+ CreatureInfo const *cinfo=creature->GetCreatureInfo();
+
+ const CreatureAICreator *ai_factory = NULL;
+
+ std::string ainame=cinfo->AIName;
+
+ // select by script name
+ if( !ainame.empty())
+ ai_factory = ai_registry.GetRegistryItem( ainame.c_str() );
+
+ // select by NPC flags
+ if(!ai_factory)
+ {
+ if( creature->isGuard() )
+ ai_factory = ai_registry.GetRegistryItem("GuardAI");
+ else if(creature->isPet() || creature->isCharmed())
+ ai_factory = ai_registry.GetRegistryItem("PetAI");
+ else if(creature->isTotem())
+ ai_factory = ai_registry.GetRegistryItem("TotemAI");
+ }
+
+ // select by permit check
+ if(!ai_factory)
+ {
+ int best_val = -1;
+ typedef CreatureAIRegistry::RegistryMapType RMT;
+ RMT const &l = ai_registry.GetRegisteredItems();
+ for( RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter)
+ {
+ const CreatureAICreator *factory = iter->second;
+ const SelectableAI *p = dynamic_cast<const SelectableAI *>(factory);
+ assert( p != NULL );
+ int val = p->Permit(creature);
+ if( val > best_val )
+ {
+ best_val = val;
+ ai_factory = p;
+ }
+ }
+ }
+
+ // select NullCreatureAI if not another cases
+ ainame = (ai_factory == NULL) ? "NullCreatureAI" : ai_factory->key();
+
+ DEBUG_LOG("Creature %u used AI is %s.", creature->GetGUIDLow(), ainame.c_str() );
+ return ( ai_factory == NULL ? new NullCreatureAI : ai_factory->Create(creature) );
+ }
+
+ MovementGenerator* selectMovementGenerator(Creature *creature)
+ {
+ MovementGeneratorRegistry &mv_registry(MovementGeneratorRepository::Instance());
+ assert( creature->GetCreatureInfo() != NULL );
+ const MovementGeneratorCreator *mv_factory = mv_registry.GetRegistryItem( creature->GetDefaultMovementType());
+
+ /* if( mv_factory == NULL )
+ {
+ int best_val = -1;
+ std::vector<std::string> l;
+ mv_registry.GetRegisteredItems(l);
+ for( std::vector<std::string>::iterator iter = l.begin(); iter != l.end(); ++iter)
+ {
+ const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str());
+ const SelectableMovement *p = dynamic_cast<const SelectableMovement *>(factory);
+ assert( p != NULL );
+ int val = p->Permit(creature);
+ if( val > best_val )
+ {
+ best_val = val;
+ mv_factory = p;
+ }
+ }
+ }*/
+
+ return ( mv_factory == NULL ? NULL : mv_factory->Create(creature) );
+
+ }
+}
diff --git a/src/game/FleeingMovementGenerator.cpp b/src/game/FleeingMovementGenerator.cpp
index 97862eac567..1f5d72c5ff8 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_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 &);
+/*
+ * 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/GameObject.cpp b/src/game/GameObject.cpp
index dc69b830760..586f4f0bf66 100644
--- a/src/game/GameObject.cpp
+++ b/src/game/GameObject.cpp
@@ -1,1247 +1,1247 @@
-/*
- * 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 "QuestDef.h"
-#include "GameObject.h"
-#include "ObjectMgr.h"
-#include "SpellMgr.h"
-#include "Spell.h"
-#include "UpdateMask.h"
-#include "Opcodes.h"
-#include "WorldPacket.h"
-#include "WorldSession.h"
-#include "World.h"
-#include "Database/DatabaseEnv.h"
-#include "MapManager.h"
-#include "LootMgr.h"
-#include "GridNotifiers.h"
-#include "GridNotifiersImpl.h"
-#include "CellImpl.h"
-#include "InstanceData.h"
-#include "BattleGround.h"
-#include "Util.h"
-
-GameObject::GameObject() : WorldObject()
-{
- m_objectType |= TYPEMASK_GAMEOBJECT;
- m_objectTypeId = TYPEID_GAMEOBJECT;
- // 2.3.2 - 0x58
- m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION);
-
- m_valuesCount = GAMEOBJECT_END;
- m_respawnTime = 0;
- m_respawnDelayTime = 25;
- m_lootState = GO_NOT_READY;
- m_spawnedByDefault = true;
- m_usetimes = 0;
- m_spellId = 0;
- m_charges = 5;
- m_cooldownTime = 0;
- m_goInfo = NULL;
-
- m_DBTableGuid = 0;
-}
-
-GameObject::~GameObject()
-{
- if(m_uint32Values) // field array can be not exist if GameOBject not loaded
- {
- // crash possable at access to deleted GO in Unit::m_gameobj
- uint64 owner_guid = GetOwnerGUID();
- if(owner_guid)
- {
- Unit* owner = ObjectAccessor::GetUnit(*this,owner_guid);
- if(owner)
- owner->RemoveGameObject(this,false);
- else if(!IS_PLAYER_GUID(owner_guid))
- sLog.outError("Delete GameObject (GUID: %u Entry: %u ) that have references in not found creature %u GO list. Crash possable later.",GetGUIDLow(),GetGOInfo()->id,GUID_LOPART(owner_guid));
- }
- }
-}
-
-void GameObject::AddToWorld()
-{
- ///- Register the gameobject for guid lookup
- if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
- Object::AddToWorld();
-}
-
-void GameObject::RemoveFromWorld()
-{
- ///- Remove the gameobject from the accessor
- if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
- Object::RemoveFromWorld();
-}
-
-bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state)
-{
- Relocate(x,y,z,ang);
- SetMapId(map->GetId());
- SetInstanceId(map->GetInstanceId());
-
- if(!IsPositionValid())
- {
- sLog.outError("ERROR: Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y);
- return false;
- }
-
- GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id);
- if (!goinfo)
- {
- sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f) ang: %f rotation0: %f rotation1: %f rotation2: %f rotation3: %f",guidlow, name_id, map->GetId(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3);
- return false;
- }
-
- Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT);
-
- m_goInfo = goinfo;
-
- if (goinfo->type >= MAX_GAMEOBJECT_TYPE)
- {
- sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist GO type '%u' in `gameobject_template`. It's will crash client if created.",guidlow,name_id,goinfo->type);
- return false;
- }
-
- SetFloatValue(GAMEOBJECT_POS_X, x);
- SetFloatValue(GAMEOBJECT_POS_Y, y);
- SetFloatValue(GAMEOBJECT_POS_Z, z);
- SetFloatValue(GAMEOBJECT_FACING, ang); //this is not facing angle
-
- SetFloatValue (GAMEOBJECT_ROTATION, rotation0);
- SetFloatValue (GAMEOBJECT_ROTATION+1, rotation1);
- SetFloatValue (GAMEOBJECT_ROTATION+2, rotation2);
- SetFloatValue (GAMEOBJECT_ROTATION+3, rotation3);
-
- SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
-
- SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
- SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
-
- SetUInt32Value(OBJECT_FIELD_ENTRY, goinfo->id);
-
- SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
-
- SetGoState(go_state);
- SetGoType(GameobjectTypes(goinfo->type));
-
- SetGoAnimProgress(animprogress);
-
- // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22)
- if (goinfo->type == GAMEOBJECT_TYPE_SPELLCASTER)
- m_charges = goinfo->spellcaster.charges;
-
- //Notify the map's instance data.
- //Only works if you create the object in it, not if it is moves to that map.
- //Normally non-players do not teleport to other maps.
- if(map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData())
- {
- ((InstanceMap*)map)->GetInstanceData()->OnObjectCreate(this);
- }
-
- return true;
-}
-
-void GameObject::Update(uint32 /*p_time*/)
-{
- if (IS_MO_TRANSPORT(GetGUID()))
- {
- //((Transport*)this)->Update(p_time);
- return;
- }
-
- switch (m_lootState)
- {
- case GO_NOT_READY:
- {
- switch(GetGoType())
- {
- case GAMEOBJECT_TYPE_TRAP:
- {
- // Arming Time for GAMEOBJECT_TYPE_TRAP (6)
- Unit* owner = GetOwner();
- if (owner && ((Player*)owner)->isInCombat())
- m_cooldownTime = time(NULL) + GetGOInfo()->trap.startDelay;
- m_lootState = GO_READY;
- break;
- }
- case GAMEOBJECT_TYPE_FISHINGNODE:
- {
- // fishing code (bobber ready)
- if( time(NULL) > m_respawnTime - FISHING_BOBBER_READY_TIME )
- {
- // splash bobber (bobber ready now)
- Unit* caster = GetOwner();
- if(caster && caster->GetTypeId()==TYPEID_PLAYER)
- {
- SetGoState(0);
- SetUInt32Value(GAMEOBJECT_FLAGS, 32);
-
- UpdateData udata;
- WorldPacket packet;
- BuildValuesUpdateBlockForPlayer(&udata,((Player*)caster));
- udata.BuildPacket(&packet);
- ((Player*)caster)->GetSession()->SendPacket(&packet);
-
- WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM,8+4);
- data << GetGUID();
- data << (uint32)(0);
- ((Player*)caster)->SendMessageToSet(&data,true);
- }
-
- m_lootState = GO_READY; // can be succesfully open with some chance
- }
- return;
- }
- default:
- m_lootState = GO_READY; // for other GOis same switched without delay to GO_READY
- break;
- }
- // NO BREAK for switch (m_lootState)
- }
- case GO_READY:
- {
- if (m_respawnTime > 0) // timer on
- {
- if (m_respawnTime <= time(NULL)) // timer expired
- {
- m_respawnTime = 0;
- m_SkillupList.clear();
- m_usetimes = 0;
-
- switch (GetGoType())
- {
- case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
- {
- Unit* caster = GetOwner();
- if(caster && caster->GetTypeId()==TYPEID_PLAYER)
- {
- if(caster->m_currentSpells[CURRENT_CHANNELED_SPELL])
- {
- caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
- caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(false);
- }
-
- WorldPacket data(SMSG_FISH_NOT_HOOKED,0);
- ((Player*)caster)->GetSession()->SendPacket(&data);
- }
- // can be delete
- m_lootState = GO_JUST_DEACTIVATED;
- return;
- }
- case GAMEOBJECT_TYPE_DOOR:
- case GAMEOBJECT_TYPE_BUTTON:
- //we need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds)
- if( !GetGoState() )
- SwitchDoorOrButton(false);
- //flags in AB are type_button and we need to add them here so no break!
- default:
- if(!m_spawnedByDefault) // despawn timer
- {
- // can be despawned or destroyed
- SetLootState(GO_JUST_DEACTIVATED);
- return;
- }
- // respawn timer
- MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
- break;
- }
- }
- }
-
- // traps can have time and can not have
- GameObjectInfo const* goInfo = GetGOInfo();
- if(goInfo->type == GAMEOBJECT_TYPE_TRAP)
- {
- // traps
- Unit* owner = GetOwner();
- Unit* ok = NULL; // pointer to appropriate target if found any
-
- if(m_cooldownTime >= time(NULL))
- return;
-
- bool IsBattleGroundTrap = false;
- //FIXME: this is activation radius (in different casting radius that must be selected from spell data)
- //TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state
- float radius = goInfo->trap.radius;
- if(!radius)
- {
- if(goInfo->trap.cooldown != 3) // cast in other case (at some triggring/linked go/etc explicit call)
- return;
- else
- {
- if(m_respawnTime > 0)
- break;
-
- radius = goInfo->trap.cooldown; // battlegrounds gameobjects has data2 == 0 && data5 == 3
- IsBattleGroundTrap = true;
- }
- }
-
- bool NeedDespawn = (goInfo->trap.charges != 0);
-
- CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
-
- // Note: this hack with search required until GO casting not implemented
- // search unfriendly creature
- if(owner && NeedDespawn) // hunter trap
- {
- MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, owner, radius);
- MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck> checker(ok, u_check);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
-
- TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker);
- cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
-
- // or unfriendly player/pet
- if(!ok)
- {
- TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
- cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
- }
- }
- else // environmental trap
- {
- // environmental damage spells already have around enemies targeting but this not help in case not existed GO casting support
-
- // affect only players
- Player* p_ok = NULL;
- MaNGOS::AnyPlayerInObjectRangeCheck p_check(this, radius);
- MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck> checker(p_ok, p_check);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
-
- TypeContainerVisitor<MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
- cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
- ok = p_ok;
- }
-
- if (ok)
- {
- Unit *caster = owner ? owner : ok;
-
- caster->CastSpell(ok, goInfo->trap.spellId, true);
- m_cooldownTime = time(NULL) + 4; // 4 seconds
-
- if(NeedDespawn)
- SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
-
- if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER)
- {
- //BattleGround gameobjects case
- if(((Player*)ok)->InBattleGround())
- if(BattleGround *bg = ((Player*)ok)->GetBattleGround())
- bg->HandleTriggerBuff(GetGUID());
- }
- }
- }
-
- if (m_charges && m_usetimes >= m_charges)
- SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
-
- break;
- }
- case GO_ACTIVATED:
- {
- switch(GetGoType())
- {
- case GAMEOBJECT_TYPE_DOOR:
- case GAMEOBJECT_TYPE_BUTTON:
- if(GetAutoCloseTime() && (m_cooldownTime < time(NULL)))
- {
- SwitchDoorOrButton(false);
- SetLootState(GO_JUST_DEACTIVATED);
- }
- break;
- }
- break;
- }
- case GO_JUST_DEACTIVATED:
- {
- //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
- if (GetGoType() == GAMEOBJECT_TYPE_GOOBER)
- {
- uint32 spellId = GetGOInfo()->goober.spellId;
-
- if(spellId)
- {
- std::set<uint32>::iterator it = m_unique_users.begin();
- std::set<uint32>::iterator end = m_unique_users.end();
- for (; it != end; it++)
- {
- Unit* owner = Unit::GetUnit(*this, uint64(*it));
- if (owner) owner->CastSpell(owner, spellId, false);
- }
-
- m_unique_users.clear();
- m_usetimes = 0;
- }
- //any return here in case battleground traps
- }
-
- if(GetOwnerGUID())
- {
- m_respawnTime = 0;
- Delete();
- return;
- }
-
- //burning flags in some battlegrounds, if you find better condition, just add it
- if (GetGoAnimProgress() > 0)
- {
- SendObjectDeSpawnAnim(this->GetGUID());
- //reset flags
- SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
- }
-
- loot.clear();
- SetLootState(GO_READY);
-
- if(!m_respawnDelayTime)
- return;
-
- if(!m_spawnedByDefault)
- {
- m_respawnTime = 0;
- return;
- }
-
- m_respawnTime = time(NULL) + m_respawnDelayTime;
-
- // if option not set then object will be saved at grid unload
- if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
- SaveRespawnTime();
-
- ObjectAccessor::UpdateObjectVisibility(this);
-
- break;
- }
- }
-}
-
-void GameObject::Refresh()
-{
- // not refresh despawned not casted GO (despawned casted GO destroyed in all cases anyway)
- if(m_respawnTime > 0 && m_spawnedByDefault)
- return;
-
- if(isSpawned())
- MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
-}
-
-void GameObject::AddUniqueUse(Player* player)
-{
- AddUse();
- m_unique_users.insert(player->GetGUIDLow());
-}
-
-void GameObject::Delete()
-{
- SendObjectDeSpawnAnim(GetGUID());
-
- SetGoState(1);
- SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
-
- AddObjectToRemoveList();
-}
-
-void GameObject::getFishLoot(Loot *fishloot)
-{
- fishloot->clear();
-
- uint32 subzone = GetAreaId();
-
- // if subzone loot exist use it
- if(LootTemplates_Fishing.HaveLootFor(subzone))
- fishloot->FillLoot(subzone, LootTemplates_Fishing, NULL);
- // else use zone loot
- else
- fishloot->FillLoot(GetZoneId(), LootTemplates_Fishing, NULL);
-}
-
-void GameObject::SaveToDB()
-{
- // this should only be used when the gameobject has already been loaded
- // perferably after adding to map, because mapid may not be valid otherwise
- GameObjectData const *data = objmgr.GetGOData(m_DBTableGuid);
- if(!data)
- {
- sLog.outError("GameObject::SaveToDB failed, cannot get gameobject data!");
- return;
- }
-
- SaveToDB(GetMapId(), data->spawnMask);
-}
-
-void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask)
-{
- const GameObjectInfo *goI = GetGOInfo();
-
- if (!goI)
- return;
-
- if (!m_DBTableGuid)
- m_DBTableGuid = GetGUIDLow();
- // update in loaded data (changing data only in this place)
- GameObjectData& data = objmgr.NewGOData(m_DBTableGuid);
-
- // data->guid = guid don't must be update at save
- data.id = GetEntry();
- data.mapid = mapid;
- data.posX = GetFloatValue(GAMEOBJECT_POS_X);
- data.posY = GetFloatValue(GAMEOBJECT_POS_Y);
- data.posZ = GetFloatValue(GAMEOBJECT_POS_Z);
- data.orientation = GetFloatValue(GAMEOBJECT_FACING);
- data.rotation0 = GetFloatValue(GAMEOBJECT_ROTATION+0);
- data.rotation1 = GetFloatValue(GAMEOBJECT_ROTATION+1);
- data.rotation2 = GetFloatValue(GAMEOBJECT_ROTATION+2);
- data.rotation3 = GetFloatValue(GAMEOBJECT_ROTATION+3);
- data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
- data.animprogress = GetGoAnimProgress();
- data.go_state = GetGoState();
- data.spawnMask = spawnMask;
-
- // updated in DB
- std::ostringstream ss;
- ss << "INSERT INTO gameobject VALUES ( "
- << m_DBTableGuid << ", "
- << GetUInt32Value (OBJECT_FIELD_ENTRY) << ", "
- << mapid << ", "
- << (uint32)spawnMask << ", "
- << GetFloatValue(GAMEOBJECT_POS_X) << ", "
- << GetFloatValue(GAMEOBJECT_POS_Y) << ", "
- << GetFloatValue(GAMEOBJECT_POS_Z) << ", "
- << GetFloatValue(GAMEOBJECT_FACING) << ", "
- << GetFloatValue(GAMEOBJECT_ROTATION) << ", "
- << GetFloatValue(GAMEOBJECT_ROTATION+1) << ", "
- << GetFloatValue(GAMEOBJECT_ROTATION+2) << ", "
- << GetFloatValue(GAMEOBJECT_ROTATION+3) << ", "
- << m_respawnDelayTime << ", "
- << GetGoAnimProgress() << ", "
- << GetGoState() << ")";
-
- WorldDatabase.BeginTransaction();
- WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
- WorldDatabase.PExecuteLog( ss.str( ).c_str( ) );
- WorldDatabase.CommitTransaction();
-}
-
-bool GameObject::LoadFromDB(uint32 guid, Map *map)
-{
- GameObjectData const* data = objmgr.GetGOData(guid);
-
- if( !data )
- {
- sLog.outErrorDb("ERROR: Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid);
- return false;
- }
-
- uint32 entry = data->id;
- uint32 map_id = data->mapid;
- float x = data->posX;
- float y = data->posY;
- float z = data->posZ;
- float ang = data->orientation;
-
- float rotation0 = data->rotation0;
- float rotation1 = data->rotation1;
- float rotation2 = data->rotation2;
- float rotation3 = data->rotation3;
-
- uint32 animprogress = data->animprogress;
- uint32 go_state = data->go_state;
-
- m_DBTableGuid = guid;
- if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
-
- if (!Create(guid,entry, map, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state) )
- return false;
-
- switch(GetGOInfo()->type)
- {
- case GAMEOBJECT_TYPE_DOOR:
- case GAMEOBJECT_TYPE_BUTTON:
- /* this code (in comment) isn't correct because in battlegrounds we need despawnable doors and buttons, pls remove
- SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
- m_spawnedByDefault = true;
- m_respawnDelayTime = 0;
- m_respawnTime = 0;
- break;*/
- default:
- if(data->spawntimesecs >= 0)
- {
- m_spawnedByDefault = true;
- m_respawnDelayTime = data->spawntimesecs;
- m_respawnTime = objmgr.GetGORespawnTime(m_DBTableGuid, map->GetInstanceId());
-
- // ready to respawn
- if(m_respawnTime && m_respawnTime <= time(NULL))
- {
- m_respawnTime = 0;
- objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
- }
- }
- else
- {
- m_spawnedByDefault = false;
- m_respawnDelayTime = -data->spawntimesecs;
- m_respawnTime = 0;
- }
- break;
- }
-
- return true;
-}
-
-void GameObject::DeleteFromDB()
-{
- objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
- objmgr.DeleteGOData(m_DBTableGuid);
- WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
- WorldDatabase.PExecuteLog("DELETE FROM game_event_gameobject WHERE guid = '%u'", m_DBTableGuid);
-}
-
-GameObject* GameObject::GetGameObject(WorldObject& object, uint64 guid)
-{
- return ObjectAccessor::GetGameObject(object,guid);
-}
-
-GameObjectInfo const *GameObject::GetGOInfo() const
-{
- return m_goInfo;
-}
-
-uint32 GameObject::GetLootId(GameObjectInfo const* ginfo)
-{
- if (!ginfo)
- return 0;
-
- switch(ginfo->type)
- {
- case GAMEOBJECT_TYPE_CHEST:
- return ginfo->chest.lootId;
- case GAMEOBJECT_TYPE_FISHINGHOLE:
- return ginfo->fishinghole.lootId;
- case GAMEOBJECT_TYPE_FISHINGNODE:
- return ginfo->fishnode.lootId;
- default:
- return 0;
- }
-}
-
-/*********************************************************/
-/*** QUEST SYSTEM ***/
-/*********************************************************/
-bool GameObject::hasQuest(uint32 quest_id) const
-{
- QuestRelations const& qr = objmgr.mGOQuestRelations;
- for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
- {
- if(itr->second==quest_id)
- return true;
- }
- return false;
-}
-
-bool GameObject::hasInvolvedQuest(uint32 quest_id) const
-{
- QuestRelations const& qr = objmgr.mGOQuestInvolvedRelations;
- for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
- {
- if(itr->second==quest_id)
- return true;
- }
- return false;
-}
-
-bool GameObject::IsTransport() const
-{
- // If something is marked as a transport, don't transmit an out of range packet for it.
- GameObjectInfo const * gInfo = GetGOInfo();
- if(!gInfo) return false;
- return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT;
-}
-
-Unit* GameObject::GetOwner() const
-{
- return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
-}
-
-void GameObject::SaveRespawnTime()
-{
- if(m_respawnTime > time(NULL) && m_spawnedByDefault)
- objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime);
-}
-
-bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const
-{
- // Not in world
- if(!IsInWorld() || !u->IsInWorld())
- return false;
-
- // Transport always visible at this step implementation
- if(IsTransport() && IsInMap(u))
- return true;
-
- // quick check visibility false cases for non-GM-mode
- if(!u->isGameMaster())
- {
- // despawned and then not visible for non-GM in GM-mode
- if(!isSpawned())
- return false;
-
- // special invisibility cases
- /* TODO: implement trap stealth, take look at spell 2836
- if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed && u->IsHostileTo(GetOwner()))
- {
- if(check stuff here)
- return false;
- }*/
-
- // Smuggled Mana Cell required 10 invisibility type detection/state
- if(GetEntry()==187039 && ((u->m_detectInvisibilityMask | u->m_invisibilityMask) & (1<<10))==0)
- return false;
- }
-
- // check distance
- return IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject() +
- (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f) );
-}
-
-void GameObject::Respawn()
-{
- if(m_spawnedByDefault && m_respawnTime > 0)
- {
- m_respawnTime = time(NULL);
- objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
- }
-}
-
-bool GameObject::ActivateToQuest( Player *pTarget)const
-{
- if(!objmgr.IsGameObjectForQuests(GetEntry()))
- return false;
-
- switch(GetGoType())
- {
- // scan GO chest with loot including quest items
- case GAMEOBJECT_TYPE_CHEST:
- {
- if(LootTemplates_Gameobject.HaveQuestLootForPlayer(GetLootId(), pTarget))
- return true;
- break;
- }
- case GAMEOBJECT_TYPE_GOOBER:
- {
- if(pTarget->GetQuestStatus(GetGOInfo()->goober.questId) == QUEST_STATUS_INCOMPLETE)
- return true;
- break;
- }
- default:
- break;
- }
-
- return false;
-}
-
-void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target)
-{
- GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(trapEntry);
- if(!trapInfo || trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
- return;
-
- SpellEntry const* trapSpell = sSpellStore.LookupEntry(trapInfo->trap.spellId);
- if(!trapSpell) // checked at load already
- return;
-
- float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(trapSpell->rangeIndex));
-
- // search nearest linked GO
- GameObject* trapGO = NULL;
- {
- // using original GO distance
- CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
-
- MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range);
- MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(trapGO,go_check);
-
- TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
- }
-
- // found correct GO
- // FIXME: when GO casting will be implemented trap must cast spell to target
- if(trapGO)
- target->CastSpell(target,trapSpell,true);
-}
-
-GameObject* GameObject::LookupFishingHoleAround(float range)
-{
- GameObject* ok = NULL;
-
- CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- MaNGOS::NearestGameObjectFishingHole u_check(*this, range);
- MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole> checker(ok, u_check);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
-
- TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker);
- cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
-
- return ok;
-}
-
-void GameObject::UseDoorOrButton(uint32 time_to_restore)
-{
- if(m_lootState != GO_READY)
- return;
-
- if(!time_to_restore)
- time_to_restore = GetAutoCloseTime();
-
- SwitchDoorOrButton(true);
- SetLootState(GO_ACTIVATED);
-
- m_cooldownTime = time(NULL) + time_to_restore;
-
-}
-
-void GameObject::SwitchDoorOrButton(bool activate)
-{
- if(activate)
- SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
- else
- RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
-
- if(GetGoState()) //if closed -> open
- SetGoState(0);
- else //if open -> close
- SetGoState(1);
-}
-
-void GameObject::Use(Unit* user)
-{
- // by default spell caster is user
- Unit* spellCaster = user;
- uint32 spellId = 0;
-
- switch(GetGoType())
- {
- case GAMEOBJECT_TYPE_DOOR: //0
- case GAMEOBJECT_TYPE_BUTTON: //1
- //doors/buttons never really despawn, only reset to default state/flags
- UseDoorOrButton();
-
- // activate script
- sWorld.ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this);
- return;
-
- case GAMEOBJECT_TYPE_QUESTGIVER: //2
- {
- if(user->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- Player* player = (Player*)user;
-
- player->PrepareQuestMenu( GetGUID() );
- player->SendPreparedQuest( GetGUID() );
- return;
- }
- //Sitting: Wooden bench, chairs enzz
- case GAMEOBJECT_TYPE_CHAIR: //7
- {
- GameObjectInfo const* info = GetGOInfo();
- if(!info)
- return;
-
- if(user->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- Player* player = (Player*)user;
-
- // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
-
- // check if the db is sane
- if(info->chair.slots > 0)
- {
- float lowestDist = DEFAULT_VISIBILITY_DISTANCE;
-
- float x_lowest = GetPositionX();
- float y_lowest = GetPositionY();
-
- // the object orientation + 1/2 pi
- // every slot will be on that straight line
- float orthogonalOrientation = GetOrientation()+M_PI*0.5f;
- // find nearest slot
- for(uint32 i=0; i<info->chair.slots; i++)
- {
- // the distance between this slot and the center of the go - imagine a 1D space
- float relativeDistance = (info->size*i)-(info->size*(info->chair.slots-1)/2.0f);
-
- float x_i = GetPositionX() + relativeDistance * cos(orthogonalOrientation);
- float y_i = GetPositionY() + relativeDistance * sin(orthogonalOrientation);
-
- // calculate the distance between the player and this slot
- float thisDistance = player->GetDistance2d(x_i, y_i);
-
- /* debug code. It will spawn a npc on each slot to visualize them.
- Creature* helper = player->SummonCreature(14496, x_i, y_i, GetPositionZ(), GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10000);
- std::ostringstream output;
- output << i << ": thisDist: " << thisDistance;
- helper->MonsterSay(output.str().c_str(), LANG_UNIVERSAL, 0);
- */
-
- if(thisDistance <= lowestDist)
- {
- lowestDist = thisDistance;
- x_lowest = x_i;
- y_lowest = y_i;
- }
- }
- player->TeleportTo(GetMapId(), x_lowest, y_lowest, GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
- }
- else
- {
- // fallback, will always work
- player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
- }
- player->SetStandState(PLAYER_STATE_SIT_LOW_CHAIR+info->chair.height);
- return;
- }
- //big gun, its a spell/aura
- case GAMEOBJECT_TYPE_GOOBER: //10
- {
- GameObjectInfo const* info = GetGOInfo();
-
- if(user->GetTypeId()==TYPEID_PLAYER)
- {
- Player* player = (Player*)user;
-
- // show page
- if(info->goober.pageId)
- {
- WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8);
- data << GetGUID();
- player->GetSession()->SendPacket(&data);
- }
-
- // possible quest objective for active quests
- player->CastedCreatureOrGO(info->id, GetGUID(), 0);
- }
-
- // cast this spell later if provided
- spellId = info->goober.spellId;
-
- break;
- }
- case GAMEOBJECT_TYPE_CAMERA: //13
- {
- GameObjectInfo const* info = GetGOInfo();
- if(!info)
- return;
-
- if(user->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- Player* player = (Player*)user;
-
- if(info->camera.cinematicId)
- {
- WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
- data << info->camera.cinematicId;
- player->GetSession()->SendPacket(&data);
- }
- return;
- }
- //fishing bobber
- case GAMEOBJECT_TYPE_FISHINGNODE: //17
- {
- if(user->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- Player* player = (Player*)user;
-
- if(player->GetGUID() != GetOwnerGUID())
- return;
-
- switch(getLootState())
- {
- case GO_READY: // ready for loot
- {
- // 1) skill must be >= base_zone_skill
- // 2) if skill == base_zone_skill => 5% chance
- // 3) chance is linear dependence from (base_zone_skill-skill)
-
- uint32 subzone = GetAreaId();
-
- int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone );
- if(!zone_skill)
- zone_skill = objmgr.GetFishingBaseSkillLevel( GetZoneId() );
-
- //provide error, no fishable zone or area should be 0
- if(!zone_skill)
- sLog.outErrorDb("Fishable areaId %u are not properly defined in `skill_fishing_base_level`.",subzone);
-
- int32 skill = player->GetSkillValue(SKILL_FISHING);
- int32 chance = skill - zone_skill + 5;
- int32 roll = irand(1,100);
-
- DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll);
-
- if(skill >= zone_skill && chance >= roll)
- {
- // prevent removing GO at spell cancel
- player->RemoveGameObject(this,false);
- SetOwnerGUID(player->GetGUID());
-
- //fish catched
- player->UpdateFishingSkill();
-
- GameObject* ok = LookupFishingHoleAround(DEFAULT_VISIBILITY_DISTANCE);
- if (ok)
- {
- player->SendLoot(ok->GetGUID(),LOOT_FISHINGHOLE);
- SetLootState(GO_JUST_DEACTIVATED);
- }
- else
- player->SendLoot(GetGUID(),LOOT_FISHING);
- }
- else
- {
- // fish escaped, can be deleted now
- SetLootState(GO_JUST_DEACTIVATED);
-
- WorldPacket data(SMSG_FISH_ESCAPED, 0);
- player->GetSession()->SendPacket(&data);
- }
- break;
- }
- case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update
- break;
- default:
- {
- SetLootState(GO_JUST_DEACTIVATED);
-
- WorldPacket data(SMSG_FISH_NOT_HOOKED, 0);
- player->GetSession()->SendPacket(&data);
- break;
- }
- }
-
- if(player->m_currentSpells[CURRENT_CHANNELED_SPELL])
- {
- player->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
- player->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
- }
- return;
- }
-
- case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
- {
- if(user->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- Player* player = (Player*)user;
-
- Unit* caster = GetOwner();
-
- GameObjectInfo const* info = GetGOInfo();
-
- if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
- return;
-
- // accept only use by player from same group for caster except caster itself
- if(((Player*)caster)==player || !((Player*)caster)->IsInSameRaidWith(player))
- return;
-
- AddUniqueUse(player);
-
- // full amount unique participants including original summoner
- if(GetUniqueUseCount() < info->summoningRitual.reqParticipants)
- return;
-
- // in case summoning ritual caster is GO creator
- spellCaster = caster;
-
- if(!caster->m_currentSpells[CURRENT_CHANNELED_SPELL])
- return;
-
- spellId = info->summoningRitual.spellId;
-
- // finish spell
- caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
- caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
-
- // can be deleted now
- SetLootState(GO_JUST_DEACTIVATED);
-
- // go to end function to spell casting
- break;
- }
- case GAMEOBJECT_TYPE_SPELLCASTER: //22
- {
- SetUInt32Value(GAMEOBJECT_FLAGS,2);
-
- GameObjectInfo const* info = GetGOInfo();
- if(!info)
- return;
-
- if(info->spellcaster.partyOnly)
- {
- Unit* caster = GetOwner();
- if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
- return;
-
- if(user->GetTypeId()!=TYPEID_PLAYER || !((Player*)user)->IsInSameRaidWith((Player*)caster))
- return;
- }
-
- spellId = info->spellcaster.spellId;
-
- AddUse();
- break;
- }
- case GAMEOBJECT_TYPE_MEETINGSTONE: //23
- {
- GameObjectInfo const* info = GetGOInfo();
-
- if(user->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- Player* player = (Player*)user;
-
- Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetSelection());
-
- // accept only use by player from same group for caster except caster itself
- if(!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameGroupWith(player))
- return;
-
- //required lvl checks!
- uint8 level = player->getLevel();
- if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
- return;
- level = targetPlayer->getLevel();
- if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
- return;
-
- spellId = 23598;
-
- break;
- }
-
- case GAMEOBJECT_TYPE_FLAGSTAND: // 24
- {
- if(user->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- Player* player = (Player*)user;
-
- if( player->isAllowUseBattleGroundObject() )
- {
- // in battleground check
- BattleGround *bg = player->GetBattleGround();
- if(!bg)
- return;
- // BG flag click
- // AB:
- // 15001
- // 15002
- // 15003
- // 15004
- // 15005
- bg->EventPlayerClickedOnFlag(player, this);
- return; //we don;t need to delete flag ... it is despawned!
- }
- break;
- }
- case GAMEOBJECT_TYPE_FLAGDROP: // 26
- {
- if(user->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- Player* player = (Player*)user;
-
- if( player->isAllowUseBattleGroundObject() )
- {
- // in battleground check
- BattleGround *bg = player->GetBattleGround();
- if(!bg)
- return;
- // BG flag dropped
- // WS:
- // 179785 - Silverwing Flag
- // 179786 - Warsong Flag
- // EotS:
- // 184142 - Netherstorm Flag
- GameObjectInfo const* info = GetGOInfo();
- if(info)
- {
- switch(info->id)
- {
- case 179785: // Silverwing Flag
- // check if it's correct bg
- if(bg->GetTypeID() == BATTLEGROUND_WS)
- bg->EventPlayerClickedOnFlag(player, this);
- break;
- case 179786: // Warsong Flag
- if(bg->GetTypeID() == BATTLEGROUND_WS)
- bg->EventPlayerClickedOnFlag(player, this);
- break;
- case 184142: // Netherstorm Flag
- if(bg->GetTypeID() == BATTLEGROUND_EY)
- bg->EventPlayerClickedOnFlag(player, this);
- break;
- }
- }
- //this cause to call return, all flags must be deleted here!!
- spellId = 0;
- Delete();
- }
- break;
- }
- default:
- sLog.outDebug("Unknown Object Type %u", GetGoType());
- break;
- }
-
- if(!spellId)
- return;
-
- SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId );
- if(!spellInfo)
- {
- sLog.outError("WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u )", spellId,GetEntry(),GetGoType());
- return;
- }
-
- Spell *spell = new Spell(spellCaster, spellInfo, false);
-
- // spell target is user of GO
- SpellCastTargets targets;
- targets.setUnitTarget( user );
-
- spell->prepare(&targets);
-}
+/*
+ * 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 "QuestDef.h"
+#include "GameObject.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Spell.h"
+#include "UpdateMask.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Database/DatabaseEnv.h"
+#include "MapManager.h"
+#include "LootMgr.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "InstanceData.h"
+#include "BattleGround.h"
+#include "Util.h"
+
+GameObject::GameObject() : WorldObject()
+{
+ m_objectType |= TYPEMASK_GAMEOBJECT;
+ m_objectTypeId = TYPEID_GAMEOBJECT;
+ // 2.3.2 - 0x58
+ m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION);
+
+ m_valuesCount = GAMEOBJECT_END;
+ m_respawnTime = 0;
+ m_respawnDelayTime = 25;
+ m_lootState = GO_NOT_READY;
+ m_spawnedByDefault = true;
+ m_usetimes = 0;
+ m_spellId = 0;
+ m_charges = 5;
+ m_cooldownTime = 0;
+ m_goInfo = NULL;
+
+ m_DBTableGuid = 0;
+}
+
+GameObject::~GameObject()
+{
+ if(m_uint32Values) // field array can be not exist if GameOBject not loaded
+ {
+ // crash possable at access to deleted GO in Unit::m_gameobj
+ uint64 owner_guid = GetOwnerGUID();
+ if(owner_guid)
+ {
+ Unit* owner = ObjectAccessor::GetUnit(*this,owner_guid);
+ if(owner)
+ owner->RemoveGameObject(this,false);
+ else if(!IS_PLAYER_GUID(owner_guid))
+ sLog.outError("Delete GameObject (GUID: %u Entry: %u ) that have references in not found creature %u GO list. Crash possable later.",GetGUIDLow(),GetGOInfo()->id,GUID_LOPART(owner_guid));
+ }
+ }
+}
+
+void GameObject::AddToWorld()
+{
+ ///- Register the gameobject for guid lookup
+ if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
+ Object::AddToWorld();
+}
+
+void GameObject::RemoveFromWorld()
+{
+ ///- Remove the gameobject from the accessor
+ if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
+ Object::RemoveFromWorld();
+}
+
+bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state)
+{
+ Relocate(x,y,z,ang);
+ SetMapId(map->GetId());
+ SetInstanceId(map->GetInstanceId());
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y);
+ return false;
+ }
+
+ GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id);
+ if (!goinfo)
+ {
+ sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f) ang: %f rotation0: %f rotation1: %f rotation2: %f rotation3: %f",guidlow, name_id, map->GetId(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3);
+ return false;
+ }
+
+ Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT);
+
+ m_goInfo = goinfo;
+
+ if (goinfo->type >= MAX_GAMEOBJECT_TYPE)
+ {
+ sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist GO type '%u' in `gameobject_template`. It's will crash client if created.",guidlow,name_id,goinfo->type);
+ return false;
+ }
+
+ SetFloatValue(GAMEOBJECT_POS_X, x);
+ SetFloatValue(GAMEOBJECT_POS_Y, y);
+ SetFloatValue(GAMEOBJECT_POS_Z, z);
+ SetFloatValue(GAMEOBJECT_FACING, ang); //this is not facing angle
+
+ SetFloatValue (GAMEOBJECT_ROTATION, rotation0);
+ SetFloatValue (GAMEOBJECT_ROTATION+1, rotation1);
+ SetFloatValue (GAMEOBJECT_ROTATION+2, rotation2);
+ SetFloatValue (GAMEOBJECT_ROTATION+3, rotation3);
+
+ SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
+
+ SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
+ SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
+
+ SetUInt32Value(OBJECT_FIELD_ENTRY, goinfo->id);
+
+ SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
+
+ SetGoState(go_state);
+ SetGoType(GameobjectTypes(goinfo->type));
+
+ SetGoAnimProgress(animprogress);
+
+ // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22)
+ if (goinfo->type == GAMEOBJECT_TYPE_SPELLCASTER)
+ m_charges = goinfo->spellcaster.charges;
+
+ //Notify the map's instance data.
+ //Only works if you create the object in it, not if it is moves to that map.
+ //Normally non-players do not teleport to other maps.
+ if(map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData())
+ {
+ ((InstanceMap*)map)->GetInstanceData()->OnObjectCreate(this);
+ }
+
+ return true;
+}
+
+void GameObject::Update(uint32 /*p_time*/)
+{
+ if (IS_MO_TRANSPORT(GetGUID()))
+ {
+ //((Transport*)this)->Update(p_time);
+ return;
+ }
+
+ switch (m_lootState)
+ {
+ case GO_NOT_READY:
+ {
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_TRAP:
+ {
+ // Arming Time for GAMEOBJECT_TYPE_TRAP (6)
+ Unit* owner = GetOwner();
+ if (owner && ((Player*)owner)->isInCombat())
+ m_cooldownTime = time(NULL) + GetGOInfo()->trap.startDelay;
+ m_lootState = GO_READY;
+ break;
+ }
+ case GAMEOBJECT_TYPE_FISHINGNODE:
+ {
+ // fishing code (bobber ready)
+ if( time(NULL) > m_respawnTime - FISHING_BOBBER_READY_TIME )
+ {
+ // splash bobber (bobber ready now)
+ Unit* caster = GetOwner();
+ if(caster && caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ SetGoState(0);
+ SetUInt32Value(GAMEOBJECT_FLAGS, 32);
+
+ UpdateData udata;
+ WorldPacket packet;
+ BuildValuesUpdateBlockForPlayer(&udata,((Player*)caster));
+ udata.BuildPacket(&packet);
+ ((Player*)caster)->GetSession()->SendPacket(&packet);
+
+ WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM,8+4);
+ data << GetGUID();
+ data << (uint32)(0);
+ ((Player*)caster)->SendMessageToSet(&data,true);
+ }
+
+ m_lootState = GO_READY; // can be succesfully open with some chance
+ }
+ return;
+ }
+ default:
+ m_lootState = GO_READY; // for other GOis same switched without delay to GO_READY
+ break;
+ }
+ // NO BREAK for switch (m_lootState)
+ }
+ case GO_READY:
+ {
+ if (m_respawnTime > 0) // timer on
+ {
+ if (m_respawnTime <= time(NULL)) // timer expired
+ {
+ m_respawnTime = 0;
+ m_SkillupList.clear();
+ m_usetimes = 0;
+
+ switch (GetGoType())
+ {
+ case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
+ {
+ Unit* caster = GetOwner();
+ if(caster && caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(caster->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ {
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(false);
+ }
+
+ WorldPacket data(SMSG_FISH_NOT_HOOKED,0);
+ ((Player*)caster)->GetSession()->SendPacket(&data);
+ }
+ // can be delete
+ m_lootState = GO_JUST_DEACTIVATED;
+ return;
+ }
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ //we need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds)
+ if( !GetGoState() )
+ SwitchDoorOrButton(false);
+ //flags in AB are type_button and we need to add them here so no break!
+ default:
+ if(!m_spawnedByDefault) // despawn timer
+ {
+ // can be despawned or destroyed
+ SetLootState(GO_JUST_DEACTIVATED);
+ return;
+ }
+ // respawn timer
+ MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
+ break;
+ }
+ }
+ }
+
+ // traps can have time and can not have
+ GameObjectInfo const* goInfo = GetGOInfo();
+ if(goInfo->type == GAMEOBJECT_TYPE_TRAP)
+ {
+ // traps
+ Unit* owner = GetOwner();
+ Unit* ok = NULL; // pointer to appropriate target if found any
+
+ if(m_cooldownTime >= time(NULL))
+ return;
+
+ bool IsBattleGroundTrap = false;
+ //FIXME: this is activation radius (in different casting radius that must be selected from spell data)
+ //TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state
+ float radius = goInfo->trap.radius;
+ if(!radius)
+ {
+ if(goInfo->trap.cooldown != 3) // cast in other case (at some triggring/linked go/etc explicit call)
+ return;
+ else
+ {
+ if(m_respawnTime > 0)
+ break;
+
+ radius = goInfo->trap.cooldown; // battlegrounds gameobjects has data2 == 0 && data5 == 3
+ IsBattleGroundTrap = true;
+ }
+ }
+
+ bool NeedDespawn = (goInfo->trap.charges != 0);
+
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ // Note: this hack with search required until GO casting not implemented
+ // search unfriendly creature
+ if(owner && NeedDespawn) // hunter trap
+ {
+ MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, owner, radius);
+ MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck> checker(ok, u_check);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+
+ TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker);
+ cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+
+ // or unfriendly player/pet
+ if(!ok)
+ {
+ TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
+ cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+ }
+ }
+ else // environmental trap
+ {
+ // environmental damage spells already have around enemies targeting but this not help in case not existed GO casting support
+
+ // affect only players
+ Player* p_ok = NULL;
+ MaNGOS::AnyPlayerInObjectRangeCheck p_check(this, radius);
+ MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck> checker(p_ok, p_check);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+
+ TypeContainerVisitor<MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
+ cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+ ok = p_ok;
+ }
+
+ if (ok)
+ {
+ Unit *caster = owner ? owner : ok;
+
+ caster->CastSpell(ok, goInfo->trap.spellId, true);
+ m_cooldownTime = time(NULL) + 4; // 4 seconds
+
+ if(NeedDespawn)
+ SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
+
+ if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER)
+ {
+ //BattleGround gameobjects case
+ if(((Player*)ok)->InBattleGround())
+ if(BattleGround *bg = ((Player*)ok)->GetBattleGround())
+ bg->HandleTriggerBuff(GetGUID());
+ }
+ }
+ }
+
+ if (m_charges && m_usetimes >= m_charges)
+ SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
+
+ break;
+ }
+ case GO_ACTIVATED:
+ {
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ if(GetAutoCloseTime() && (m_cooldownTime < time(NULL)))
+ {
+ SwitchDoorOrButton(false);
+ SetLootState(GO_JUST_DEACTIVATED);
+ }
+ break;
+ }
+ break;
+ }
+ case GO_JUST_DEACTIVATED:
+ {
+ //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
+ if (GetGoType() == GAMEOBJECT_TYPE_GOOBER)
+ {
+ uint32 spellId = GetGOInfo()->goober.spellId;
+
+ if(spellId)
+ {
+ std::set<uint32>::iterator it = m_unique_users.begin();
+ std::set<uint32>::iterator end = m_unique_users.end();
+ for (; it != end; it++)
+ {
+ Unit* owner = Unit::GetUnit(*this, uint64(*it));
+ if (owner) owner->CastSpell(owner, spellId, false);
+ }
+
+ m_unique_users.clear();
+ m_usetimes = 0;
+ }
+ //any return here in case battleground traps
+ }
+
+ if(GetOwnerGUID())
+ {
+ m_respawnTime = 0;
+ Delete();
+ return;
+ }
+
+ //burning flags in some battlegrounds, if you find better condition, just add it
+ if (GetGoAnimProgress() > 0)
+ {
+ SendObjectDeSpawnAnim(this->GetGUID());
+ //reset flags
+ SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
+ }
+
+ loot.clear();
+ SetLootState(GO_READY);
+
+ if(!m_respawnDelayTime)
+ return;
+
+ if(!m_spawnedByDefault)
+ {
+ m_respawnTime = 0;
+ return;
+ }
+
+ m_respawnTime = time(NULL) + m_respawnDelayTime;
+
+ // if option not set then object will be saved at grid unload
+ if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
+ SaveRespawnTime();
+
+ ObjectAccessor::UpdateObjectVisibility(this);
+
+ break;
+ }
+ }
+}
+
+void GameObject::Refresh()
+{
+ // not refresh despawned not casted GO (despawned casted GO destroyed in all cases anyway)
+ if(m_respawnTime > 0 && m_spawnedByDefault)
+ return;
+
+ if(isSpawned())
+ MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
+}
+
+void GameObject::AddUniqueUse(Player* player)
+{
+ AddUse();
+ m_unique_users.insert(player->GetGUIDLow());
+}
+
+void GameObject::Delete()
+{
+ SendObjectDeSpawnAnim(GetGUID());
+
+ SetGoState(1);
+ SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
+
+ AddObjectToRemoveList();
+}
+
+void GameObject::getFishLoot(Loot *fishloot)
+{
+ fishloot->clear();
+
+ uint32 subzone = GetAreaId();
+
+ // if subzone loot exist use it
+ if(LootTemplates_Fishing.HaveLootFor(subzone))
+ fishloot->FillLoot(subzone, LootTemplates_Fishing, NULL);
+ // else use zone loot
+ else
+ fishloot->FillLoot(GetZoneId(), LootTemplates_Fishing, NULL);
+}
+
+void GameObject::SaveToDB()
+{
+ // this should only be used when the gameobject has already been loaded
+ // perferably after adding to map, because mapid may not be valid otherwise
+ GameObjectData const *data = objmgr.GetGOData(m_DBTableGuid);
+ if(!data)
+ {
+ sLog.outError("GameObject::SaveToDB failed, cannot get gameobject data!");
+ return;
+ }
+
+ SaveToDB(GetMapId(), data->spawnMask);
+}
+
+void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask)
+{
+ const GameObjectInfo *goI = GetGOInfo();
+
+ if (!goI)
+ return;
+
+ if (!m_DBTableGuid)
+ m_DBTableGuid = GetGUIDLow();
+ // update in loaded data (changing data only in this place)
+ GameObjectData& data = objmgr.NewGOData(m_DBTableGuid);
+
+ // data->guid = guid don't must be update at save
+ data.id = GetEntry();
+ data.mapid = mapid;
+ data.posX = GetFloatValue(GAMEOBJECT_POS_X);
+ data.posY = GetFloatValue(GAMEOBJECT_POS_Y);
+ data.posZ = GetFloatValue(GAMEOBJECT_POS_Z);
+ data.orientation = GetFloatValue(GAMEOBJECT_FACING);
+ data.rotation0 = GetFloatValue(GAMEOBJECT_ROTATION+0);
+ data.rotation1 = GetFloatValue(GAMEOBJECT_ROTATION+1);
+ data.rotation2 = GetFloatValue(GAMEOBJECT_ROTATION+2);
+ data.rotation3 = GetFloatValue(GAMEOBJECT_ROTATION+3);
+ data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
+ data.animprogress = GetGoAnimProgress();
+ data.go_state = GetGoState();
+ data.spawnMask = spawnMask;
+
+ // updated in DB
+ std::ostringstream ss;
+ ss << "INSERT INTO gameobject VALUES ( "
+ << m_DBTableGuid << ", "
+ << GetUInt32Value (OBJECT_FIELD_ENTRY) << ", "
+ << mapid << ", "
+ << (uint32)spawnMask << ", "
+ << GetFloatValue(GAMEOBJECT_POS_X) << ", "
+ << GetFloatValue(GAMEOBJECT_POS_Y) << ", "
+ << GetFloatValue(GAMEOBJECT_POS_Z) << ", "
+ << GetFloatValue(GAMEOBJECT_FACING) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION+1) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION+2) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION+3) << ", "
+ << m_respawnDelayTime << ", "
+ << GetGoAnimProgress() << ", "
+ << GetGoState() << ")";
+
+ WorldDatabase.BeginTransaction();
+ WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog( ss.str( ).c_str( ) );
+ WorldDatabase.CommitTransaction();
+}
+
+bool GameObject::LoadFromDB(uint32 guid, Map *map)
+{
+ GameObjectData const* data = objmgr.GetGOData(guid);
+
+ if( !data )
+ {
+ sLog.outErrorDb("ERROR: Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid);
+ return false;
+ }
+
+ uint32 entry = data->id;
+ uint32 map_id = data->mapid;
+ float x = data->posX;
+ float y = data->posY;
+ float z = data->posZ;
+ float ang = data->orientation;
+
+ float rotation0 = data->rotation0;
+ float rotation1 = data->rotation1;
+ float rotation2 = data->rotation2;
+ float rotation3 = data->rotation3;
+
+ uint32 animprogress = data->animprogress;
+ uint32 go_state = data->go_state;
+
+ m_DBTableGuid = guid;
+ if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
+
+ if (!Create(guid,entry, map, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state) )
+ return false;
+
+ switch(GetGOInfo()->type)
+ {
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ /* this code (in comment) isn't correct because in battlegrounds we need despawnable doors and buttons, pls remove
+ SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
+ m_spawnedByDefault = true;
+ m_respawnDelayTime = 0;
+ m_respawnTime = 0;
+ break;*/
+ default:
+ if(data->spawntimesecs >= 0)
+ {
+ m_spawnedByDefault = true;
+ m_respawnDelayTime = data->spawntimesecs;
+ m_respawnTime = objmgr.GetGORespawnTime(m_DBTableGuid, map->GetInstanceId());
+
+ // ready to respawn
+ if(m_respawnTime && m_respawnTime <= time(NULL))
+ {
+ m_respawnTime = 0;
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ }
+ }
+ else
+ {
+ m_spawnedByDefault = false;
+ m_respawnDelayTime = -data->spawntimesecs;
+ m_respawnTime = 0;
+ }
+ break;
+ }
+
+ return true;
+}
+
+void GameObject::DeleteFromDB()
+{
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ objmgr.DeleteGOData(m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM game_event_gameobject WHERE guid = '%u'", m_DBTableGuid);
+}
+
+GameObject* GameObject::GetGameObject(WorldObject& object, uint64 guid)
+{
+ return ObjectAccessor::GetGameObject(object,guid);
+}
+
+GameObjectInfo const *GameObject::GetGOInfo() const
+{
+ return m_goInfo;
+}
+
+uint32 GameObject::GetLootId(GameObjectInfo const* ginfo)
+{
+ if (!ginfo)
+ return 0;
+
+ switch(ginfo->type)
+ {
+ case GAMEOBJECT_TYPE_CHEST:
+ return ginfo->chest.lootId;
+ case GAMEOBJECT_TYPE_FISHINGHOLE:
+ return ginfo->fishinghole.lootId;
+ case GAMEOBJECT_TYPE_FISHINGNODE:
+ return ginfo->fishnode.lootId;
+ default:
+ return 0;
+ }
+}
+
+/*********************************************************/
+/*** QUEST SYSTEM ***/
+/*********************************************************/
+bool GameObject::hasQuest(uint32 quest_id) const
+{
+ QuestRelations const& qr = objmgr.mGOQuestRelations;
+ for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
+ {
+ if(itr->second==quest_id)
+ return true;
+ }
+ return false;
+}
+
+bool GameObject::hasInvolvedQuest(uint32 quest_id) const
+{
+ QuestRelations const& qr = objmgr.mGOQuestInvolvedRelations;
+ for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
+ {
+ if(itr->second==quest_id)
+ return true;
+ }
+ return false;
+}
+
+bool GameObject::IsTransport() const
+{
+ // If something is marked as a transport, don't transmit an out of range packet for it.
+ GameObjectInfo const * gInfo = GetGOInfo();
+ if(!gInfo) return false;
+ return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT;
+}
+
+Unit* GameObject::GetOwner() const
+{
+ return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
+}
+
+void GameObject::SaveRespawnTime()
+{
+ if(m_respawnTime > time(NULL) && m_spawnedByDefault)
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime);
+}
+
+bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const
+{
+ // Not in world
+ if(!IsInWorld() || !u->IsInWorld())
+ return false;
+
+ // Transport always visible at this step implementation
+ if(IsTransport() && IsInMap(u))
+ return true;
+
+ // quick check visibility false cases for non-GM-mode
+ if(!u->isGameMaster())
+ {
+ // despawned and then not visible for non-GM in GM-mode
+ if(!isSpawned())
+ return false;
+
+ // special invisibility cases
+ /* TODO: implement trap stealth, take look at spell 2836
+ if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed && u->IsHostileTo(GetOwner()))
+ {
+ if(check stuff here)
+ return false;
+ }*/
+
+ // Smuggled Mana Cell required 10 invisibility type detection/state
+ if(GetEntry()==187039 && ((u->m_detectInvisibilityMask | u->m_invisibilityMask) & (1<<10))==0)
+ return false;
+ }
+
+ // check distance
+ return IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject() +
+ (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f) );
+}
+
+void GameObject::Respawn()
+{
+ if(m_spawnedByDefault && m_respawnTime > 0)
+ {
+ m_respawnTime = time(NULL);
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ }
+}
+
+bool GameObject::ActivateToQuest( Player *pTarget)const
+{
+ if(!objmgr.IsGameObjectForQuests(GetEntry()))
+ return false;
+
+ switch(GetGoType())
+ {
+ // scan GO chest with loot including quest items
+ case GAMEOBJECT_TYPE_CHEST:
+ {
+ if(LootTemplates_Gameobject.HaveQuestLootForPlayer(GetLootId(), pTarget))
+ return true;
+ break;
+ }
+ case GAMEOBJECT_TYPE_GOOBER:
+ {
+ if(pTarget->GetQuestStatus(GetGOInfo()->goober.questId) == QUEST_STATUS_INCOMPLETE)
+ return true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target)
+{
+ GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(trapEntry);
+ if(!trapInfo || trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
+ return;
+
+ SpellEntry const* trapSpell = sSpellStore.LookupEntry(trapInfo->trap.spellId);
+ if(!trapSpell) // checked at load already
+ return;
+
+ float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(trapSpell->rangeIndex));
+
+ // search nearest linked GO
+ GameObject* trapGO = NULL;
+ {
+ // using original GO distance
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range);
+ MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(trapGO,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+ }
+
+ // found correct GO
+ // FIXME: when GO casting will be implemented trap must cast spell to target
+ if(trapGO)
+ target->CastSpell(target,trapSpell,true);
+}
+
+GameObject* GameObject::LookupFishingHoleAround(float range)
+{
+ GameObject* ok = NULL;
+
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ MaNGOS::NearestGameObjectFishingHole u_check(*this, range);
+ MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole> checker(ok, u_check);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+
+ TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker);
+ cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+
+ return ok;
+}
+
+void GameObject::UseDoorOrButton(uint32 time_to_restore)
+{
+ if(m_lootState != GO_READY)
+ return;
+
+ if(!time_to_restore)
+ time_to_restore = GetAutoCloseTime();
+
+ SwitchDoorOrButton(true);
+ SetLootState(GO_ACTIVATED);
+
+ m_cooldownTime = time(NULL) + time_to_restore;
+
+}
+
+void GameObject::SwitchDoorOrButton(bool activate)
+{
+ if(activate)
+ SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
+ else
+ RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
+
+ if(GetGoState()) //if closed -> open
+ SetGoState(0);
+ else //if open -> close
+ SetGoState(1);
+}
+
+void GameObject::Use(Unit* user)
+{
+ // by default spell caster is user
+ Unit* spellCaster = user;
+ uint32 spellId = 0;
+
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR: //0
+ case GAMEOBJECT_TYPE_BUTTON: //1
+ //doors/buttons never really despawn, only reset to default state/flags
+ UseDoorOrButton();
+
+ // activate script
+ sWorld.ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this);
+ return;
+
+ case GAMEOBJECT_TYPE_QUESTGIVER: //2
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ player->PrepareQuestMenu( GetGUID() );
+ player->SendPreparedQuest( GetGUID() );
+ return;
+ }
+ //Sitting: Wooden bench, chairs enzz
+ case GAMEOBJECT_TYPE_CHAIR: //7
+ {
+ GameObjectInfo const* info = GetGOInfo();
+ if(!info)
+ return;
+
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
+
+ // check if the db is sane
+ if(info->chair.slots > 0)
+ {
+ float lowestDist = DEFAULT_VISIBILITY_DISTANCE;
+
+ float x_lowest = GetPositionX();
+ float y_lowest = GetPositionY();
+
+ // the object orientation + 1/2 pi
+ // every slot will be on that straight line
+ float orthogonalOrientation = GetOrientation()+M_PI*0.5f;
+ // find nearest slot
+ for(uint32 i=0; i<info->chair.slots; i++)
+ {
+ // the distance between this slot and the center of the go - imagine a 1D space
+ float relativeDistance = (info->size*i)-(info->size*(info->chair.slots-1)/2.0f);
+
+ float x_i = GetPositionX() + relativeDistance * cos(orthogonalOrientation);
+ float y_i = GetPositionY() + relativeDistance * sin(orthogonalOrientation);
+
+ // calculate the distance between the player and this slot
+ float thisDistance = player->GetDistance2d(x_i, y_i);
+
+ /* debug code. It will spawn a npc on each slot to visualize them.
+ Creature* helper = player->SummonCreature(14496, x_i, y_i, GetPositionZ(), GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10000);
+ std::ostringstream output;
+ output << i << ": thisDist: " << thisDistance;
+ helper->MonsterSay(output.str().c_str(), LANG_UNIVERSAL, 0);
+ */
+
+ if(thisDistance <= lowestDist)
+ {
+ lowestDist = thisDistance;
+ x_lowest = x_i;
+ y_lowest = y_i;
+ }
+ }
+ player->TeleportTo(GetMapId(), x_lowest, y_lowest, GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
+ }
+ else
+ {
+ // fallback, will always work
+ player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
+ }
+ player->SetStandState(PLAYER_STATE_SIT_LOW_CHAIR+info->chair.height);
+ return;
+ }
+ //big gun, its a spell/aura
+ case GAMEOBJECT_TYPE_GOOBER: //10
+ {
+ GameObjectInfo const* info = GetGOInfo();
+
+ if(user->GetTypeId()==TYPEID_PLAYER)
+ {
+ Player* player = (Player*)user;
+
+ // show page
+ if(info->goober.pageId)
+ {
+ WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8);
+ data << GetGUID();
+ player->GetSession()->SendPacket(&data);
+ }
+
+ // possible quest objective for active quests
+ player->CastedCreatureOrGO(info->id, GetGUID(), 0);
+ }
+
+ // cast this spell later if provided
+ spellId = info->goober.spellId;
+
+ break;
+ }
+ case GAMEOBJECT_TYPE_CAMERA: //13
+ {
+ GameObjectInfo const* info = GetGOInfo();
+ if(!info)
+ return;
+
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if(info->camera.cinematicId)
+ {
+ WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
+ data << info->camera.cinematicId;
+ player->GetSession()->SendPacket(&data);
+ }
+ return;
+ }
+ //fishing bobber
+ case GAMEOBJECT_TYPE_FISHINGNODE: //17
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if(player->GetGUID() != GetOwnerGUID())
+ return;
+
+ switch(getLootState())
+ {
+ case GO_READY: // ready for loot
+ {
+ // 1) skill must be >= base_zone_skill
+ // 2) if skill == base_zone_skill => 5% chance
+ // 3) chance is linear dependence from (base_zone_skill-skill)
+
+ uint32 subzone = GetAreaId();
+
+ int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone );
+ if(!zone_skill)
+ zone_skill = objmgr.GetFishingBaseSkillLevel( GetZoneId() );
+
+ //provide error, no fishable zone or area should be 0
+ if(!zone_skill)
+ sLog.outErrorDb("Fishable areaId %u are not properly defined in `skill_fishing_base_level`.",subzone);
+
+ int32 skill = player->GetSkillValue(SKILL_FISHING);
+ int32 chance = skill - zone_skill + 5;
+ int32 roll = irand(1,100);
+
+ DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll);
+
+ if(skill >= zone_skill && chance >= roll)
+ {
+ // prevent removing GO at spell cancel
+ player->RemoveGameObject(this,false);
+ SetOwnerGUID(player->GetGUID());
+
+ //fish catched
+ player->UpdateFishingSkill();
+
+ GameObject* ok = LookupFishingHoleAround(DEFAULT_VISIBILITY_DISTANCE);
+ if (ok)
+ {
+ player->SendLoot(ok->GetGUID(),LOOT_FISHINGHOLE);
+ SetLootState(GO_JUST_DEACTIVATED);
+ }
+ else
+ player->SendLoot(GetGUID(),LOOT_FISHING);
+ }
+ else
+ {
+ // fish escaped, can be deleted now
+ SetLootState(GO_JUST_DEACTIVATED);
+
+ WorldPacket data(SMSG_FISH_ESCAPED, 0);
+ player->GetSession()->SendPacket(&data);
+ }
+ break;
+ }
+ case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update
+ break;
+ default:
+ {
+ SetLootState(GO_JUST_DEACTIVATED);
+
+ WorldPacket data(SMSG_FISH_NOT_HOOKED, 0);
+ player->GetSession()->SendPacket(&data);
+ break;
+ }
+ }
+
+ if(player->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ {
+ player->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ player->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
+ }
+ return;
+ }
+
+ case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ Unit* caster = GetOwner();
+
+ GameObjectInfo const* info = GetGOInfo();
+
+ if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
+ return;
+
+ // accept only use by player from same group for caster except caster itself
+ if(((Player*)caster)==player || !((Player*)caster)->IsInSameRaidWith(player))
+ return;
+
+ AddUniqueUse(player);
+
+ // full amount unique participants including original summoner
+ if(GetUniqueUseCount() < info->summoningRitual.reqParticipants)
+ return;
+
+ // in case summoning ritual caster is GO creator
+ spellCaster = caster;
+
+ if(!caster->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ return;
+
+ spellId = info->summoningRitual.spellId;
+
+ // finish spell
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
+
+ // can be deleted now
+ SetLootState(GO_JUST_DEACTIVATED);
+
+ // go to end function to spell casting
+ break;
+ }
+ case GAMEOBJECT_TYPE_SPELLCASTER: //22
+ {
+ SetUInt32Value(GAMEOBJECT_FLAGS,2);
+
+ GameObjectInfo const* info = GetGOInfo();
+ if(!info)
+ return;
+
+ if(info->spellcaster.partyOnly)
+ {
+ Unit* caster = GetOwner();
+ if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
+ return;
+
+ if(user->GetTypeId()!=TYPEID_PLAYER || !((Player*)user)->IsInSameRaidWith((Player*)caster))
+ return;
+ }
+
+ spellId = info->spellcaster.spellId;
+
+ AddUse();
+ break;
+ }
+ case GAMEOBJECT_TYPE_MEETINGSTONE: //23
+ {
+ GameObjectInfo const* info = GetGOInfo();
+
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetSelection());
+
+ // accept only use by player from same group for caster except caster itself
+ if(!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameGroupWith(player))
+ return;
+
+ //required lvl checks!
+ uint8 level = player->getLevel();
+ if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
+ return;
+ level = targetPlayer->getLevel();
+ if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
+ return;
+
+ spellId = 23598;
+
+ break;
+ }
+
+ case GAMEOBJECT_TYPE_FLAGSTAND: // 24
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if( player->isAllowUseBattleGroundObject() )
+ {
+ // in battleground check
+ BattleGround *bg = player->GetBattleGround();
+ if(!bg)
+ return;
+ // BG flag click
+ // AB:
+ // 15001
+ // 15002
+ // 15003
+ // 15004
+ // 15005
+ bg->EventPlayerClickedOnFlag(player, this);
+ return; //we don;t need to delete flag ... it is despawned!
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_FLAGDROP: // 26
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if( player->isAllowUseBattleGroundObject() )
+ {
+ // in battleground check
+ BattleGround *bg = player->GetBattleGround();
+ if(!bg)
+ return;
+ // BG flag dropped
+ // WS:
+ // 179785 - Silverwing Flag
+ // 179786 - Warsong Flag
+ // EotS:
+ // 184142 - Netherstorm Flag
+ GameObjectInfo const* info = GetGOInfo();
+ if(info)
+ {
+ switch(info->id)
+ {
+ case 179785: // Silverwing Flag
+ // check if it's correct bg
+ if(bg->GetTypeID() == BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag(player, this);
+ break;
+ case 179786: // Warsong Flag
+ if(bg->GetTypeID() == BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag(player, this);
+ break;
+ case 184142: // Netherstorm Flag
+ if(bg->GetTypeID() == BATTLEGROUND_EY)
+ bg->EventPlayerClickedOnFlag(player, this);
+ break;
+ }
+ }
+ //this cause to call return, all flags must be deleted here!!
+ spellId = 0;
+ Delete();
+ }
+ break;
+ }
+ default:
+ sLog.outDebug("Unknown Object Type %u", GetGoType());
+ break;
+ }
+
+ if(!spellId)
+ return;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u )", spellId,GetEntry(),GetGoType());
+ return;
+ }
+
+ Spell *spell = new Spell(spellCaster, spellInfo, false);
+
+ // spell target is user of GO
+ SpellCastTargets targets;
+ targets.setUnitTarget( user );
+
+ spell->prepare(&targets);
+}
diff --git a/src/game/GameObject.h b/src/game/GameObject.h
index bab73fb1968..ddc47fdac67 100644
--- a/src/game/GameObject.h
+++ b/src/game/GameObject.h
@@ -1,592 +1,592 @@
-/*
- * 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
- */
-
-#ifndef MANGOSSERVER_GAMEOBJECT_H
-#define MANGOSSERVER_GAMEOBJECT_H
-
-#include "Common.h"
-#include "SharedDefines.h"
-#include "Object.h"
-#include "LootMgr.h"
-#include "Database/DatabaseEnv.h"
-
-// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
-#if defined( __GNUC__ )
-#pragma pack(1)
-#else
-#pragma pack(push,1)
-#endif
-
-// from `gameobject_template`
-struct GameObjectInfo
-{
- uint32 id;
- uint32 type;
- uint32 displayId;
- char *name;
- char *castBarCaption;
- uint32 faction;
- uint32 flags;
- float size;
- union // different GO types have different data field
- {
- //0 GAMEOBJECT_TYPE_DOOR
- struct
- {
- uint32 startOpen; //0 used client side to determine GO_ACTIVATED means open/closed
- uint32 lockId; //1 -> Lock.dbc
- uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
- uint32 noDamageImmune; //3 break opening whenever you recieve damage?
- uint32 openTextID; //4 can be used to replace castBarCaption?
- uint32 closeTextID; //5
- } door;
- //1 GAMEOBJECT_TYPE_BUTTON
- struct
- {
- uint32 startOpen; //0
- uint32 lockId; //1 -> Lock.dbc
- uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
- uint32 linkedTrap; //3
- uint32 noDamageImmune; //4 isBattlegroundObject
- uint32 large; //5
- uint32 openTextID; //6 can be used to replace castBarCaption?
- uint32 closeTextID; //7
- uint32 losOK; //8
- } button;
- //2 GAMEOBJECT_TYPE_QUESTGIVER
- struct
- {
- uint32 lockId; //0 -> Lock.dbc
- uint32 questList; //1
- uint32 pageMaterial; //2
- uint32 gossipID; //3
- uint32 customAnim; //4
- uint32 noDamageImmune; //5
- uint32 openTextID; //6 can be used to replace castBarCaption?
- uint32 losOK; //7
- uint32 allowMounted; //8
- uint32 large; //9
- } questgiver;
- //3 GAMEOBJECT_TYPE_CHEST
- struct
- {
- uint32 lockId; //0 -> Lock.dbc
- uint32 lootId; //1
- uint32 chestRestockTime; //2
- uint32 consumable; //3
- uint32 minSuccessOpens; //4
- uint32 maxSuccessOpens; //5
- uint32 eventId; //6 lootedEvent
- uint32 linkedTrapId; //7
- uint32 questId; //8 not used currently but store quest required for GO activation for player
- uint32 level; //9
- uint32 losOK; //10
- uint32 leaveLoot; //11
- uint32 notInCombat; //12
- uint32 logLoot; //13
- uint32 openTextID; //14 can be used to replace castBarCaption?
- uint32 groupLootRules; //15
- } chest;
- //5 GAMEOBJECT_TYPE_GENERIC
- struct
- {
- uint32 floatingTooltip; //0
- uint32 highlight; //1
- uint32 serverOnly; //2
- uint32 large; //3
- uint32 floatOnWater; //4
- uint32 questID; //5
- } _generic;
- //6 GAMEOBJECT_TYPE_TRAP
- struct
- {
- uint32 lockId; //0 -> Lock.dbc
- uint32 level; //1
- uint32 radius; //2 radius for trap activation
- uint32 spellId; //3
- uint32 charges; //4 need respawn (if > 0)
- uint32 cooldown; //5 time in secs
- uint32 autoCloseTime; //6
- uint32 startDelay; //7
- uint32 serverOnly; //8
- uint32 stealthed; //9
- uint32 large; //10
- uint32 stealthAffected; //11
- uint32 openTextID; //12 can be used to replace castBarCaption?
- uint32 closeTextID; //13
- } trap;
- //7 GAMEOBJECT_TYPE_CHAIR
- struct
- {
- uint32 slots; //0
- uint32 height; //1
- uint32 onlyCreatorUse; //2
- } chair;
- //8 GAMEOBJECT_TYPE_SPELL_FOCUS
- struct
- {
- uint32 focusId; //0
- uint32 dist; //1
- uint32 linkedTrapId; //2
- uint32 serverOnly; //3
- uint32 questID; //4
- uint32 large; //5
- } spellFocus;
- //9 GAMEOBJECT_TYPE_TEXT
- struct
- {
- uint32 pageID; //0
- uint32 language; //1
- uint32 pageMaterial; //2
- uint32 allowMounted; //3
- } text;
- //10 GAMEOBJECT_TYPE_GOOBER
- struct
- {
- uint32 lockId; //0 -> Lock.dbc
- uint32 questId; //1
- uint32 eventId; //2
- uint32 autoCloseTime; //3
- uint32 customAnim; //4
- uint32 consumable; //5
- uint32 cooldown; //6
- uint32 pageId; //7
- uint32 language; //8
- uint32 pageMaterial; //9
- uint32 spellId; //10
- uint32 noDamageImmune; //11
- uint32 linkedTrapId; //12
- uint32 large; //13
- uint32 openTextID; //14 can be used to replace castBarCaption?
- uint32 closeTextID; //15
- uint32 losOK; //16 isBattlegroundObject
- uint32 allowMounted; //17
- } goober;
- //11 GAMEOBJECT_TYPE_TRANSPORT
- struct
- {
- uint32 pause; //0
- uint32 startOpen; //1
- uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
- } transport;
- //12 GAMEOBJECT_TYPE_AREADAMAGE
- struct
- {
- uint32 lockId; //0
- uint32 radius; //1
- uint32 damageMin; //2
- uint32 damageMax; //3
- uint32 damageSchool; //4
- uint32 autoCloseTime; //5 secs till autoclose = autoCloseTime / 0x10000
- uint32 openTextID; //6
- uint32 closeTextID; //7
- } areadamage;
- //13 GAMEOBJECT_TYPE_CAMERA
- struct
- {
- uint32 lockId; //0 -> Lock.dbc
- uint32 cinematicId; //1
- uint32 eventID; //2
- uint32 openTextID; //3 can be used to replace castBarCaption?
- } camera;
- //15 GAMEOBJECT_TYPE_MO_TRANSPORT
- struct
- {
- uint32 taxiPathId; //0
- uint32 moveSpeed; //1
- uint32 accelRate; //2
- uint32 startEventID; //3
- uint32 stopEventID; //4
- uint32 transportPhysics; //5
- uint32 mapID; //6
- } moTransport;
- //17 GAMEOBJECT_TYPE_FISHINGNODE
- struct
- {
- uint32 _data0; //0
- uint32 lootId; //1
- } fishnode;
- //18 GAMEOBJECT_TYPE_SUMMONING_RITUAL
- struct
- {
- uint32 reqParticipants; //0
- uint32 spellId; //1
- uint32 animSpell; //2
- uint32 ritualPersistent; //3
- uint32 casterTargetSpell; //4
- uint32 casterTargetSpellTargets; //5
- uint32 castersGrouped; //6
- uint32 ritualNoTargetCheck; //7
- } summoningRitual;
- //20 GAMEOBJECT_TYPE_AUCTIONHOUSE
- struct
- {
- uint32 actionHouseID; //0
- } auctionhouse;
- //21 GAMEOBJECT_TYPE_GUARDPOST
- struct
- {
- uint32 creatureID; //0
- uint32 charges; //1
- } guardpost;
- //22 GAMEOBJECT_TYPE_SPELLCASTER
- struct
- {
- uint32 spellId; //0
- uint32 charges; //1
- uint32 partyOnly; //2
- } spellcaster;
- //23 GAMEOBJECT_TYPE_MEETINGSTONE
- struct
- {
- uint32 minLevel; //0
- uint32 maxLevel; //1
- uint32 areaID; //2
- } meetingstone;
- //24 GAMEOBJECT_TYPE_FLAGSTAND
- struct
- {
- uint32 lockId; //0
- uint32 pickupSpell; //1
- uint32 radius; //2
- uint32 returnAura; //3
- uint32 returnSpell; //4
- uint32 noDamageImmune; //5
- uint32 openTextID; //6
- uint32 losOK; //7
- } flagstand;
- //25 GAMEOBJECT_TYPE_FISHINGHOLE // not implemented yet
- struct
- {
- uint32 radius; //0 how close bobber must land for sending loot
- uint32 lootId; //1
- uint32 minSuccessOpens; //2
- uint32 maxSuccessOpens; //3
- uint32 lockId; //4 -> Lock.dbc; possibly 1628 for all?
- } fishinghole;
- //26 GAMEOBJECT_TYPE_FLAGDROP
- struct
- {
- uint32 lockId; //0
- uint32 eventID; //1
- uint32 pickupSpell; //2
- uint32 noDamageImmune; //3
- uint32 openTextID; //4
- } flagdrop;
- //27 GAMEOBJECT_TYPE_MINI_GAME
- struct
- {
- uint32 gameType; //0
- } miniGame;
- //29 GAMEOBJECT_TYPE_CAPTURE_POINT
- struct
- {
- uint32 radius; //0
- uint32 spell; //1
- uint32 worldState1; //2
- uint32 worldstate2; //3
- uint32 winEventID1; //4
- uint32 winEventID2; //5
- uint32 contestedEventID1; //6
- uint32 contestedEventID2; //7
- uint32 progressEventID1; //8
- uint32 progressEventID2; //9
- uint32 neutralEventID1; //10
- uint32 neutralEventID2; //11
- uint32 neutralPercent; //12
- uint32 worldstate3; //13
- uint32 minSuperiority; //14
- uint32 maxSuperiority; //15
- uint32 minTime; //16
- uint32 maxTime; //17
- uint32 large; //18
- uint32 highlight; //19
- } capturePoint;
- //30 GAMEOBJECT_TYPE_AURA_GENERATOR
- struct
- {
- uint32 startOpen; //0
- uint32 radius; //1
- uint32 auraID1; //2
- uint32 conditionID1; //3
- uint32 auraID2; //4
- uint32 conditionID2; //5
- uint32 serverOnly; //6
- } auraGenerator;
- //31 GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY
- struct
- {
- uint32 mapID; //0
- uint32 difficulty; //1
- } dungeonDifficulty;
- //32 GAMEOBJECT_TYPE_DO_NOT_USE_YET
- struct
- {
- uint32 mapID; //0
- uint32 difficulty; //1
- } doNotUseYet;
- //33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
- struct
- {
- uint32 dmgPctState1; //0
- uint32 dmgPctState2; //1
- uint32 state1Name; //2
- uint32 state2Name; //3
- } destructibleBuilding;
-
- // not use for specific field access (only for output with loop by all filed), also this determinate max union size
- struct // GAMEOBJECT_TYPE_SPELLCASTER
- {
- uint32 data[24];
- } raw;
- };
- char *ScriptName;
-};
-
-struct GameObjectLocale
-{
- std::vector<std::string> Name;
- std::vector<std::string> CastBarCaption;
-};
-
-// from `gameobject`
-struct GameObjectData
-{
- uint32 id; // entry in gamobject_template
- uint32 mapid;
- float posX;
- float posY;
- float posZ;
- float orientation;
- float rotation0;
- float rotation1;
- float rotation2;
- float rotation3;
- int32 spawntimesecs;
- uint32 animprogress;
- uint32 go_state;
- uint8 spawnMask;
-};
-
-// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
-#if defined( __GNUC__ )
-#pragma pack()
-#else
-#pragma pack(pop)
-#endif
-
-// For containers: [GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY -> ...
-// For bobber: GO_NOT_READY ->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED-><deleted>
-// For door(closed):[GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY(close) -> ...
-// For door(open): [GO_NOT_READY]->GO_READY (open) ->GO_ACTIVATED (close)->GO_JUST_DEACTIVATED->GO_READY(open) -> ...
-enum LootState
-{
- GO_NOT_READY = 0,
- GO_READY, // can be ready but despawned, and then not possible activate until spawn
- GO_ACTIVATED,
- GO_JUST_DEACTIVATED
-};
-
-class Unit;
-
-// 5 sec for bobber catch
-#define FISHING_BOBBER_READY_TIME 5
-
-class MANGOS_DLL_SPEC GameObject : public WorldObject
-{
- public:
- explicit GameObject();
- ~GameObject();
-
- void AddToWorld();
- void RemoveFromWorld();
-
- bool Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state);
- void Update(uint32 p_time);
- static GameObject* GetGameObject(WorldObject& object, uint64 guid);
- GameObjectInfo const* GetGOInfo() const;
-
- bool IsTransport() const;
-
- void SetOwnerGUID(uint64 owner)
- {
- m_spawnedByDefault = false; // all object with owner is despawned after delay
- SetUInt64Value(OBJECT_FIELD_CREATED_BY, owner);
- }
- uint64 GetOwnerGUID() const { return GetUInt64Value(OBJECT_FIELD_CREATED_BY); }
- Unit* GetOwner() const;
-
- uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; }
-
- void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); }
- void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); }
- void TextEmote(const char* text, uint64 TargetGuid) { MonsterTextEmote(text,TargetGuid); }
- void Whisper(const char* text,uint64 receiver) { MonsterWhisper(text,receiver); }
- void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
- void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
- void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); }
- void Whisper(int32 textId, uint64 receiver) { MonsterWhisper(textId,receiver); }
-
- void SaveToDB();
- void SaveToDB(uint32 mapid, uint8 spawnMask);
- bool LoadFromDB(uint32 guid, Map *map);
- void DeleteFromDB();
- void SetLootState(LootState s) { m_lootState = s; }
- static uint32 GetLootId(GameObjectInfo const* info);
- uint32 GetLootId() const { return GetLootId(GetGOInfo()); }
- uint32 GetLockId() const
- {
- switch(GetGoType())
- {
- case GAMEOBJECT_TYPE_DOOR: return GetGOInfo()->door.lockId;
- case GAMEOBJECT_TYPE_BUTTON: return GetGOInfo()->button.lockId;
- case GAMEOBJECT_TYPE_QUESTGIVER: return GetGOInfo()->questgiver.lockId;
- case GAMEOBJECT_TYPE_CHEST: return GetGOInfo()->chest.lockId;
- case GAMEOBJECT_TYPE_TRAP: return GetGOInfo()->trap.lockId;
- case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.lockId;
- case GAMEOBJECT_TYPE_AREADAMAGE: return GetGOInfo()->areadamage.lockId;
- case GAMEOBJECT_TYPE_CAMERA: return GetGOInfo()->camera.lockId;
- case GAMEOBJECT_TYPE_FLAGSTAND: return GetGOInfo()->flagstand.lockId;
- case GAMEOBJECT_TYPE_FISHINGHOLE:return GetGOInfo()->fishinghole.lockId;
- case GAMEOBJECT_TYPE_FLAGDROP: return GetGOInfo()->flagdrop.lockId;
- default: return 0;
- }
- }
-
- time_t GetRespawnTime() const { return m_respawnTime; }
- time_t GetRespawnTimeEx() const
- {
- time_t now = time(NULL);
- if(m_respawnTime > now)
- return m_respawnTime;
- else
- return now;
- }
-
- void SetRespawnTime(int32 respawn)
- {
- m_respawnTime = respawn > 0 ? time(NULL) + respawn : 0;
- m_respawnDelayTime = respawn > 0 ? respawn : 0;
- }
- void Respawn();
- bool isSpawned() const
- {
- return m_respawnDelayTime == 0 ||
- (m_respawnTime > 0 && !m_spawnedByDefault) ||
- (m_respawnTime == 0 && m_spawnedByDefault);
- }
- bool isSpawnedByDefault() const { return m_spawnedByDefault; }
- uint32 GetRespawnDelay() const { return m_respawnDelayTime; }
- void Refresh();
- void Delete();
- void SetSpellId(uint32 id) { m_spellId = id;}
- uint32 GetSpellId() const { return m_spellId;}
- void getFishLoot(Loot *loot);
- GameobjectTypes GetGoType() const { return GameobjectTypes(GetUInt32Value(GAMEOBJECT_TYPE_ID)); }
- void SetGoType(GameobjectTypes type) { SetUInt32Value(GAMEOBJECT_TYPE_ID, type); }
- uint32 GetGoState() const { return GetUInt32Value(GAMEOBJECT_STATE); }
- void SetGoState(uint32 state) { SetUInt32Value(GAMEOBJECT_STATE, state); }
- uint32 GetGoArtKit() const { return GetUInt32Value(GAMEOBJECT_ARTKIT); }
- void SetGoArtKit(uint32 artkit) { SetUInt32Value(GAMEOBJECT_ARTKIT, artkit); }
- uint32 GetGoAnimProgress() const { return GetUInt32Value(GAMEOBJECT_ANIMPROGRESS); }
- void SetGoAnimProgress(uint32 animprogress) { SetUInt32Value(GAMEOBJECT_ANIMPROGRESS, animprogress); }
-
- void Use(Unit* user);
-
- LootState getLootState() const { return m_lootState; }
-
- void AddToSkillupList(uint32 PlayerGuidLow) { m_SkillupList.push_back(PlayerGuidLow); }
- bool IsInSkillupList(uint32 PlayerGuidLow) const
- {
- for (std::list<uint32>::const_iterator i = m_SkillupList.begin(); i != m_SkillupList.end(); ++i)
- if (*i == PlayerGuidLow) return true;
- return false;
- }
- void ClearSkillupList() { m_SkillupList.clear(); }
-
- void AddUniqueUse(Player* player);
- void AddUse() { ++m_usetimes; }
-
- uint32 GetUseCount() const { return m_usetimes; }
- uint32 GetUniqueUseCount() const { return m_unique_users.size(); }
-
- void SaveRespawnTime();
-
- Loot loot;
-
- bool hasQuest(uint32 quest_id) const;
- bool hasInvolvedQuest(uint32 quest_id) const;
- bool ActivateToQuest(Player *pTarget) const;
- void UseDoorOrButton(uint32 time_to_restore = 0); // 0 = use `gameobject`.`spawntimesecs`
-
- uint32 GetLinkedGameObjectEntry() const
- {
- switch(GetGoType())
- {
- case GAMEOBJECT_TYPE_CHEST: return GetGOInfo()->chest.linkedTrapId;
- case GAMEOBJECT_TYPE_SPELL_FOCUS: return GetGOInfo()->spellFocus.linkedTrapId;
- case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.linkedTrapId;
- default: return 0;
- }
- }
-
- uint32 GetAutoCloseTime() const
- {
- uint32 autoCloseTime = 0;
- switch(GetGoType())
- {
- case GAMEOBJECT_TYPE_DOOR: autoCloseTime = GetGOInfo()->door.autoCloseTime; break;
- case GAMEOBJECT_TYPE_BUTTON: autoCloseTime = GetGOInfo()->button.autoCloseTime; break;
- case GAMEOBJECT_TYPE_TRAP: autoCloseTime = GetGOInfo()->trap.autoCloseTime; break;
- case GAMEOBJECT_TYPE_GOOBER: autoCloseTime = GetGOInfo()->goober.autoCloseTime; break;
- case GAMEOBJECT_TYPE_TRANSPORT: autoCloseTime = GetGOInfo()->transport.autoCloseTime; break;
- case GAMEOBJECT_TYPE_AREADAMAGE: autoCloseTime = GetGOInfo()->areadamage.autoCloseTime; break;
- default: break;
- }
- return autoCloseTime / 0x10000;
- }
-
- void TriggeringLinkedGameObject( uint32 trapEntry, Unit* target);
-
- bool isVisibleForInState(Player const* u, bool inVisibleList) const;
-
- GameObject* LookupFishingHoleAround(float range);
-
- GridReference<GameObject> &GetGridRef() { return m_gridRef; }
- protected:
- uint32 m_charges; // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22)
- uint32 m_spellId;
- time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()),
- uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer
- LootState m_lootState;
- bool m_spawnedByDefault;
- time_t m_cooldownTime; // used as internal reaction delay time store (not state change reaction).
- // For traps this: spell casting cooldown, for doors/buttons: reset time.
- std::list<uint32> m_SkillupList;
-
- std::set<uint32> m_unique_users;
- uint32 m_usetimes;
-
- uint32 m_DBTableGuid; ///< For new or temporary gameobjects is 0 for saved it is lowguid
- GameObjectInfo const* m_goInfo;
- private:
- void SwitchDoorOrButton(bool activate);
-
- GridReference<GameObject> m_gridRef;
-};
-#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
+ */
+
+#ifndef MANGOSSERVER_GAMEOBJECT_H
+#define MANGOSSERVER_GAMEOBJECT_H
+
+#include "Common.h"
+#include "SharedDefines.h"
+#include "Object.h"
+#include "LootMgr.h"
+#include "Database/DatabaseEnv.h"
+
+// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack(1)
+#else
+#pragma pack(push,1)
+#endif
+
+// from `gameobject_template`
+struct GameObjectInfo
+{
+ uint32 id;
+ uint32 type;
+ uint32 displayId;
+ char *name;
+ char *castBarCaption;
+ uint32 faction;
+ uint32 flags;
+ float size;
+ union // different GO types have different data field
+ {
+ //0 GAMEOBJECT_TYPE_DOOR
+ struct
+ {
+ uint32 startOpen; //0 used client side to determine GO_ACTIVATED means open/closed
+ uint32 lockId; //1 -> Lock.dbc
+ uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
+ uint32 noDamageImmune; //3 break opening whenever you recieve damage?
+ uint32 openTextID; //4 can be used to replace castBarCaption?
+ uint32 closeTextID; //5
+ } door;
+ //1 GAMEOBJECT_TYPE_BUTTON
+ struct
+ {
+ uint32 startOpen; //0
+ uint32 lockId; //1 -> Lock.dbc
+ uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
+ uint32 linkedTrap; //3
+ uint32 noDamageImmune; //4 isBattlegroundObject
+ uint32 large; //5
+ uint32 openTextID; //6 can be used to replace castBarCaption?
+ uint32 closeTextID; //7
+ uint32 losOK; //8
+ } button;
+ //2 GAMEOBJECT_TYPE_QUESTGIVER
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 questList; //1
+ uint32 pageMaterial; //2
+ uint32 gossipID; //3
+ uint32 customAnim; //4
+ uint32 noDamageImmune; //5
+ uint32 openTextID; //6 can be used to replace castBarCaption?
+ uint32 losOK; //7
+ uint32 allowMounted; //8
+ uint32 large; //9
+ } questgiver;
+ //3 GAMEOBJECT_TYPE_CHEST
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 lootId; //1
+ uint32 chestRestockTime; //2
+ uint32 consumable; //3
+ uint32 minSuccessOpens; //4
+ uint32 maxSuccessOpens; //5
+ uint32 eventId; //6 lootedEvent
+ uint32 linkedTrapId; //7
+ uint32 questId; //8 not used currently but store quest required for GO activation for player
+ uint32 level; //9
+ uint32 losOK; //10
+ uint32 leaveLoot; //11
+ uint32 notInCombat; //12
+ uint32 logLoot; //13
+ uint32 openTextID; //14 can be used to replace castBarCaption?
+ uint32 groupLootRules; //15
+ } chest;
+ //5 GAMEOBJECT_TYPE_GENERIC
+ struct
+ {
+ uint32 floatingTooltip; //0
+ uint32 highlight; //1
+ uint32 serverOnly; //2
+ uint32 large; //3
+ uint32 floatOnWater; //4
+ uint32 questID; //5
+ } _generic;
+ //6 GAMEOBJECT_TYPE_TRAP
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 level; //1
+ uint32 radius; //2 radius for trap activation
+ uint32 spellId; //3
+ uint32 charges; //4 need respawn (if > 0)
+ uint32 cooldown; //5 time in secs
+ uint32 autoCloseTime; //6
+ uint32 startDelay; //7
+ uint32 serverOnly; //8
+ uint32 stealthed; //9
+ uint32 large; //10
+ uint32 stealthAffected; //11
+ uint32 openTextID; //12 can be used to replace castBarCaption?
+ uint32 closeTextID; //13
+ } trap;
+ //7 GAMEOBJECT_TYPE_CHAIR
+ struct
+ {
+ uint32 slots; //0
+ uint32 height; //1
+ uint32 onlyCreatorUse; //2
+ } chair;
+ //8 GAMEOBJECT_TYPE_SPELL_FOCUS
+ struct
+ {
+ uint32 focusId; //0
+ uint32 dist; //1
+ uint32 linkedTrapId; //2
+ uint32 serverOnly; //3
+ uint32 questID; //4
+ uint32 large; //5
+ } spellFocus;
+ //9 GAMEOBJECT_TYPE_TEXT
+ struct
+ {
+ uint32 pageID; //0
+ uint32 language; //1
+ uint32 pageMaterial; //2
+ uint32 allowMounted; //3
+ } text;
+ //10 GAMEOBJECT_TYPE_GOOBER
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 questId; //1
+ uint32 eventId; //2
+ uint32 autoCloseTime; //3
+ uint32 customAnim; //4
+ uint32 consumable; //5
+ uint32 cooldown; //6
+ uint32 pageId; //7
+ uint32 language; //8
+ uint32 pageMaterial; //9
+ uint32 spellId; //10
+ uint32 noDamageImmune; //11
+ uint32 linkedTrapId; //12
+ uint32 large; //13
+ uint32 openTextID; //14 can be used to replace castBarCaption?
+ uint32 closeTextID; //15
+ uint32 losOK; //16 isBattlegroundObject
+ uint32 allowMounted; //17
+ } goober;
+ //11 GAMEOBJECT_TYPE_TRANSPORT
+ struct
+ {
+ uint32 pause; //0
+ uint32 startOpen; //1
+ uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
+ } transport;
+ //12 GAMEOBJECT_TYPE_AREADAMAGE
+ struct
+ {
+ uint32 lockId; //0
+ uint32 radius; //1
+ uint32 damageMin; //2
+ uint32 damageMax; //3
+ uint32 damageSchool; //4
+ uint32 autoCloseTime; //5 secs till autoclose = autoCloseTime / 0x10000
+ uint32 openTextID; //6
+ uint32 closeTextID; //7
+ } areadamage;
+ //13 GAMEOBJECT_TYPE_CAMERA
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 cinematicId; //1
+ uint32 eventID; //2
+ uint32 openTextID; //3 can be used to replace castBarCaption?
+ } camera;
+ //15 GAMEOBJECT_TYPE_MO_TRANSPORT
+ struct
+ {
+ uint32 taxiPathId; //0
+ uint32 moveSpeed; //1
+ uint32 accelRate; //2
+ uint32 startEventID; //3
+ uint32 stopEventID; //4
+ uint32 transportPhysics; //5
+ uint32 mapID; //6
+ } moTransport;
+ //17 GAMEOBJECT_TYPE_FISHINGNODE
+ struct
+ {
+ uint32 _data0; //0
+ uint32 lootId; //1
+ } fishnode;
+ //18 GAMEOBJECT_TYPE_SUMMONING_RITUAL
+ struct
+ {
+ uint32 reqParticipants; //0
+ uint32 spellId; //1
+ uint32 animSpell; //2
+ uint32 ritualPersistent; //3
+ uint32 casterTargetSpell; //4
+ uint32 casterTargetSpellTargets; //5
+ uint32 castersGrouped; //6
+ uint32 ritualNoTargetCheck; //7
+ } summoningRitual;
+ //20 GAMEOBJECT_TYPE_AUCTIONHOUSE
+ struct
+ {
+ uint32 actionHouseID; //0
+ } auctionhouse;
+ //21 GAMEOBJECT_TYPE_GUARDPOST
+ struct
+ {
+ uint32 creatureID; //0
+ uint32 charges; //1
+ } guardpost;
+ //22 GAMEOBJECT_TYPE_SPELLCASTER
+ struct
+ {
+ uint32 spellId; //0
+ uint32 charges; //1
+ uint32 partyOnly; //2
+ } spellcaster;
+ //23 GAMEOBJECT_TYPE_MEETINGSTONE
+ struct
+ {
+ uint32 minLevel; //0
+ uint32 maxLevel; //1
+ uint32 areaID; //2
+ } meetingstone;
+ //24 GAMEOBJECT_TYPE_FLAGSTAND
+ struct
+ {
+ uint32 lockId; //0
+ uint32 pickupSpell; //1
+ uint32 radius; //2
+ uint32 returnAura; //3
+ uint32 returnSpell; //4
+ uint32 noDamageImmune; //5
+ uint32 openTextID; //6
+ uint32 losOK; //7
+ } flagstand;
+ //25 GAMEOBJECT_TYPE_FISHINGHOLE // not implemented yet
+ struct
+ {
+ uint32 radius; //0 how close bobber must land for sending loot
+ uint32 lootId; //1
+ uint32 minSuccessOpens; //2
+ uint32 maxSuccessOpens; //3
+ uint32 lockId; //4 -> Lock.dbc; possibly 1628 for all?
+ } fishinghole;
+ //26 GAMEOBJECT_TYPE_FLAGDROP
+ struct
+ {
+ uint32 lockId; //0
+ uint32 eventID; //1
+ uint32 pickupSpell; //2
+ uint32 noDamageImmune; //3
+ uint32 openTextID; //4
+ } flagdrop;
+ //27 GAMEOBJECT_TYPE_MINI_GAME
+ struct
+ {
+ uint32 gameType; //0
+ } miniGame;
+ //29 GAMEOBJECT_TYPE_CAPTURE_POINT
+ struct
+ {
+ uint32 radius; //0
+ uint32 spell; //1
+ uint32 worldState1; //2
+ uint32 worldstate2; //3
+ uint32 winEventID1; //4
+ uint32 winEventID2; //5
+ uint32 contestedEventID1; //6
+ uint32 contestedEventID2; //7
+ uint32 progressEventID1; //8
+ uint32 progressEventID2; //9
+ uint32 neutralEventID1; //10
+ uint32 neutralEventID2; //11
+ uint32 neutralPercent; //12
+ uint32 worldstate3; //13
+ uint32 minSuperiority; //14
+ uint32 maxSuperiority; //15
+ uint32 minTime; //16
+ uint32 maxTime; //17
+ uint32 large; //18
+ uint32 highlight; //19
+ } capturePoint;
+ //30 GAMEOBJECT_TYPE_AURA_GENERATOR
+ struct
+ {
+ uint32 startOpen; //0
+ uint32 radius; //1
+ uint32 auraID1; //2
+ uint32 conditionID1; //3
+ uint32 auraID2; //4
+ uint32 conditionID2; //5
+ uint32 serverOnly; //6
+ } auraGenerator;
+ //31 GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY
+ struct
+ {
+ uint32 mapID; //0
+ uint32 difficulty; //1
+ } dungeonDifficulty;
+ //32 GAMEOBJECT_TYPE_DO_NOT_USE_YET
+ struct
+ {
+ uint32 mapID; //0
+ uint32 difficulty; //1
+ } doNotUseYet;
+ //33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
+ struct
+ {
+ uint32 dmgPctState1; //0
+ uint32 dmgPctState2; //1
+ uint32 state1Name; //2
+ uint32 state2Name; //3
+ } destructibleBuilding;
+
+ // not use for specific field access (only for output with loop by all filed), also this determinate max union size
+ struct // GAMEOBJECT_TYPE_SPELLCASTER
+ {
+ uint32 data[24];
+ } raw;
+ };
+ char *ScriptName;
+};
+
+struct GameObjectLocale
+{
+ std::vector<std::string> Name;
+ std::vector<std::string> CastBarCaption;
+};
+
+// from `gameobject`
+struct GameObjectData
+{
+ uint32 id; // entry in gamobject_template
+ uint32 mapid;
+ float posX;
+ float posY;
+ float posZ;
+ float orientation;
+ float rotation0;
+ float rotation1;
+ float rotation2;
+ float rotation3;
+ int32 spawntimesecs;
+ uint32 animprogress;
+ uint32 go_state;
+ uint8 spawnMask;
+};
+
+// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack()
+#else
+#pragma pack(pop)
+#endif
+
+// For containers: [GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY -> ...
+// For bobber: GO_NOT_READY ->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED-><deleted>
+// For door(closed):[GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY(close) -> ...
+// For door(open): [GO_NOT_READY]->GO_READY (open) ->GO_ACTIVATED (close)->GO_JUST_DEACTIVATED->GO_READY(open) -> ...
+enum LootState
+{
+ GO_NOT_READY = 0,
+ GO_READY, // can be ready but despawned, and then not possible activate until spawn
+ GO_ACTIVATED,
+ GO_JUST_DEACTIVATED
+};
+
+class Unit;
+
+// 5 sec for bobber catch
+#define FISHING_BOBBER_READY_TIME 5
+
+class MANGOS_DLL_SPEC GameObject : public WorldObject
+{
+ public:
+ explicit GameObject();
+ ~GameObject();
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ bool Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state);
+ void Update(uint32 p_time);
+ static GameObject* GetGameObject(WorldObject& object, uint64 guid);
+ GameObjectInfo const* GetGOInfo() const;
+
+ bool IsTransport() const;
+
+ void SetOwnerGUID(uint64 owner)
+ {
+ m_spawnedByDefault = false; // all object with owner is despawned after delay
+ SetUInt64Value(OBJECT_FIELD_CREATED_BY, owner);
+ }
+ uint64 GetOwnerGUID() const { return GetUInt64Value(OBJECT_FIELD_CREATED_BY); }
+ Unit* GetOwner() const;
+
+ uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; }
+
+ void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); }
+ void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); }
+ void TextEmote(const char* text, uint64 TargetGuid) { MonsterTextEmote(text,TargetGuid); }
+ void Whisper(const char* text,uint64 receiver) { MonsterWhisper(text,receiver); }
+ void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
+ void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
+ void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); }
+ void Whisper(int32 textId, uint64 receiver) { MonsterWhisper(textId,receiver); }
+
+ void SaveToDB();
+ void SaveToDB(uint32 mapid, uint8 spawnMask);
+ bool LoadFromDB(uint32 guid, Map *map);
+ void DeleteFromDB();
+ void SetLootState(LootState s) { m_lootState = s; }
+ static uint32 GetLootId(GameObjectInfo const* info);
+ uint32 GetLootId() const { return GetLootId(GetGOInfo()); }
+ uint32 GetLockId() const
+ {
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR: return GetGOInfo()->door.lockId;
+ case GAMEOBJECT_TYPE_BUTTON: return GetGOInfo()->button.lockId;
+ case GAMEOBJECT_TYPE_QUESTGIVER: return GetGOInfo()->questgiver.lockId;
+ case GAMEOBJECT_TYPE_CHEST: return GetGOInfo()->chest.lockId;
+ case GAMEOBJECT_TYPE_TRAP: return GetGOInfo()->trap.lockId;
+ case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.lockId;
+ case GAMEOBJECT_TYPE_AREADAMAGE: return GetGOInfo()->areadamage.lockId;
+ case GAMEOBJECT_TYPE_CAMERA: return GetGOInfo()->camera.lockId;
+ case GAMEOBJECT_TYPE_FLAGSTAND: return GetGOInfo()->flagstand.lockId;
+ case GAMEOBJECT_TYPE_FISHINGHOLE:return GetGOInfo()->fishinghole.lockId;
+ case GAMEOBJECT_TYPE_FLAGDROP: return GetGOInfo()->flagdrop.lockId;
+ default: return 0;
+ }
+ }
+
+ time_t GetRespawnTime() const { return m_respawnTime; }
+ time_t GetRespawnTimeEx() const
+ {
+ time_t now = time(NULL);
+ if(m_respawnTime > now)
+ return m_respawnTime;
+ else
+ return now;
+ }
+
+ void SetRespawnTime(int32 respawn)
+ {
+ m_respawnTime = respawn > 0 ? time(NULL) + respawn : 0;
+ m_respawnDelayTime = respawn > 0 ? respawn : 0;
+ }
+ void Respawn();
+ bool isSpawned() const
+ {
+ return m_respawnDelayTime == 0 ||
+ (m_respawnTime > 0 && !m_spawnedByDefault) ||
+ (m_respawnTime == 0 && m_spawnedByDefault);
+ }
+ bool isSpawnedByDefault() const { return m_spawnedByDefault; }
+ uint32 GetRespawnDelay() const { return m_respawnDelayTime; }
+ void Refresh();
+ void Delete();
+ void SetSpellId(uint32 id) { m_spellId = id;}
+ uint32 GetSpellId() const { return m_spellId;}
+ void getFishLoot(Loot *loot);
+ GameobjectTypes GetGoType() const { return GameobjectTypes(GetUInt32Value(GAMEOBJECT_TYPE_ID)); }
+ void SetGoType(GameobjectTypes type) { SetUInt32Value(GAMEOBJECT_TYPE_ID, type); }
+ uint32 GetGoState() const { return GetUInt32Value(GAMEOBJECT_STATE); }
+ void SetGoState(uint32 state) { SetUInt32Value(GAMEOBJECT_STATE, state); }
+ uint32 GetGoArtKit() const { return GetUInt32Value(GAMEOBJECT_ARTKIT); }
+ void SetGoArtKit(uint32 artkit) { SetUInt32Value(GAMEOBJECT_ARTKIT, artkit); }
+ uint32 GetGoAnimProgress() const { return GetUInt32Value(GAMEOBJECT_ANIMPROGRESS); }
+ void SetGoAnimProgress(uint32 animprogress) { SetUInt32Value(GAMEOBJECT_ANIMPROGRESS, animprogress); }
+
+ void Use(Unit* user);
+
+ LootState getLootState() const { return m_lootState; }
+
+ void AddToSkillupList(uint32 PlayerGuidLow) { m_SkillupList.push_back(PlayerGuidLow); }
+ bool IsInSkillupList(uint32 PlayerGuidLow) const
+ {
+ for (std::list<uint32>::const_iterator i = m_SkillupList.begin(); i != m_SkillupList.end(); ++i)
+ if (*i == PlayerGuidLow) return true;
+ return false;
+ }
+ void ClearSkillupList() { m_SkillupList.clear(); }
+
+ void AddUniqueUse(Player* player);
+ void AddUse() { ++m_usetimes; }
+
+ uint32 GetUseCount() const { return m_usetimes; }
+ uint32 GetUniqueUseCount() const { return m_unique_users.size(); }
+
+ void SaveRespawnTime();
+
+ Loot loot;
+
+ bool hasQuest(uint32 quest_id) const;
+ bool hasInvolvedQuest(uint32 quest_id) const;
+ bool ActivateToQuest(Player *pTarget) const;
+ void UseDoorOrButton(uint32 time_to_restore = 0); // 0 = use `gameobject`.`spawntimesecs`
+
+ uint32 GetLinkedGameObjectEntry() const
+ {
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_CHEST: return GetGOInfo()->chest.linkedTrapId;
+ case GAMEOBJECT_TYPE_SPELL_FOCUS: return GetGOInfo()->spellFocus.linkedTrapId;
+ case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.linkedTrapId;
+ default: return 0;
+ }
+ }
+
+ uint32 GetAutoCloseTime() const
+ {
+ uint32 autoCloseTime = 0;
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR: autoCloseTime = GetGOInfo()->door.autoCloseTime; break;
+ case GAMEOBJECT_TYPE_BUTTON: autoCloseTime = GetGOInfo()->button.autoCloseTime; break;
+ case GAMEOBJECT_TYPE_TRAP: autoCloseTime = GetGOInfo()->trap.autoCloseTime; break;
+ case GAMEOBJECT_TYPE_GOOBER: autoCloseTime = GetGOInfo()->goober.autoCloseTime; break;
+ case GAMEOBJECT_TYPE_TRANSPORT: autoCloseTime = GetGOInfo()->transport.autoCloseTime; break;
+ case GAMEOBJECT_TYPE_AREADAMAGE: autoCloseTime = GetGOInfo()->areadamage.autoCloseTime; break;
+ default: break;
+ }
+ return autoCloseTime / 0x10000;
+ }
+
+ void TriggeringLinkedGameObject( uint32 trapEntry, Unit* target);
+
+ bool isVisibleForInState(Player const* u, bool inVisibleList) const;
+
+ GameObject* LookupFishingHoleAround(float range);
+
+ GridReference<GameObject> &GetGridRef() { return m_gridRef; }
+ protected:
+ uint32 m_charges; // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22)
+ uint32 m_spellId;
+ time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()),
+ uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer
+ LootState m_lootState;
+ bool m_spawnedByDefault;
+ time_t m_cooldownTime; // used as internal reaction delay time store (not state change reaction).
+ // For traps this: spell casting cooldown, for doors/buttons: reset time.
+ std::list<uint32> m_SkillupList;
+
+ std::set<uint32> m_unique_users;
+ uint32 m_usetimes;
+
+ uint32 m_DBTableGuid; ///< For new or temporary gameobjects is 0 for saved it is lowguid
+ GameObjectInfo const* m_goInfo;
+ private:
+ void SwitchDoorOrButton(bool activate);
+
+ GridReference<GameObject> m_gridRef;
+};
+#endif
diff --git a/src/game/GossipDef.cpp b/src/game/GossipDef.cpp
index 645cd962146..ae4e618fa1e 100644
--- a/src/game/GossipDef.cpp
+++ b/src/game/GossipDef.cpp
@@ -1,762 +1,762 @@
-/*
- * 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 "QuestDef.h"
-#include "GossipDef.h"
-#include "ObjectMgr.h"
-#include "Opcodes.h"
-#include "WorldPacket.h"
-#include "WorldSession.h"
-
-GossipMenu::GossipMenu()
-{
- m_gItems.reserve(16); // can be set for max from most often sizes to speedup push_back and less memory use
-}
-
-GossipMenu::~GossipMenu()
-{
- ClearMenu();
-}
-
-void GossipMenu::AddMenuItem(uint8 Icon, std::string Message, uint32 dtSender, uint32 dtAction, std::string BoxMessage, uint32 BoxMoney, bool Coded)
-{
- ASSERT( m_gItems.size() <= GOSSIP_MAX_MENU_ITEMS );
-
- GossipMenuItem gItem;
-
- gItem.m_gIcon = Icon;
- gItem.m_gMessage = Message;
- gItem.m_gCoded = Coded;
- gItem.m_gSender = dtSender;
- gItem.m_gAction = dtAction;
- gItem.m_gBoxMessage = BoxMessage;
- gItem.m_gBoxMoney = BoxMoney;
-
- m_gItems.push_back(gItem);
-}
-
-void GossipMenu::AddMenuItem(uint8 Icon, std::string Message, bool Coded)
-{
- AddMenuItem( Icon, Message, 0, 0, "", 0, Coded);
-}
-
-void GossipMenu::AddMenuItem(uint8 Icon, char const* Message, bool Coded)
-{
- AddMenuItem(Icon, std::string(Message ? Message : ""),Coded);
-}
-
-void GossipMenu::AddMenuItem(uint8 Icon, char const* Message, uint32 dtSender, uint32 dtAction, char const* BoxMessage, uint32 BoxMoney, bool Coded)
-{
- AddMenuItem(Icon, std::string(Message ? Message : ""), dtSender, dtAction, std::string(BoxMessage ? BoxMessage : ""), BoxMoney, Coded);
-}
-
-uint32 GossipMenu::MenuItemSender( unsigned int ItemId )
-{
- if ( ItemId >= m_gItems.size() ) return 0;
-
- return m_gItems[ ItemId ].m_gSender;
-}
-
-uint32 GossipMenu::MenuItemAction( unsigned int ItemId )
-{
- if ( ItemId >= m_gItems.size() ) return 0;
-
- return m_gItems[ ItemId ].m_gAction;
-}
-
-bool GossipMenu::MenuItemCoded( unsigned int ItemId )
-{
- if ( ItemId >= m_gItems.size() ) return 0;
-
- return m_gItems[ ItemId ].m_gCoded;
-}
-
-void GossipMenu::ClearMenu()
-{
- m_gItems.clear();
-}
-
-PlayerMenu::PlayerMenu( WorldSession *session ) : pSession(session)
-{
-}
-
-PlayerMenu::~PlayerMenu()
-{
- ClearMenus();
-}
-
-void PlayerMenu::ClearMenus()
-{
- mGossipMenu.ClearMenu();
- mQuestMenu.ClearMenu();
-}
-
-uint32 PlayerMenu::GossipOptionSender( unsigned int Selection )
-{
- return mGossipMenu.MenuItemSender( Selection );
-}
-
-uint32 PlayerMenu::GossipOptionAction( unsigned int Selection )
-{
- return mGossipMenu.MenuItemAction( Selection );
-}
-
-bool PlayerMenu::GossipOptionCoded( unsigned int Selection )
-{
- return mGossipMenu.MenuItemCoded( Selection );
-}
-
-void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
-{
- WorldPacket data( SMSG_GOSSIP_MESSAGE, (100) ); // guess size
- data << npcGUID;
- data << uint32(0); // new 2.4.0
- data << uint32( TitleTextId );
- data << uint32( mGossipMenu.MenuItemCount() ); // max count 0x0F
-
- for ( unsigned int iI = 0; iI < mGossipMenu.MenuItemCount(); iI++ )
- {
- GossipMenuItem const& gItem = mGossipMenu.GetItem(iI);
- data << uint32( iI );
- data << uint8( gItem.m_gIcon );
- // icons:
- // 0 unlearn talents/misc
- // 1 trader
- // 2 taxi
- // 3 trainer
- // 9 BG/arena
- data << uint8( gItem.m_gCoded ); // makes pop up box password
- data << uint32(gItem.m_gBoxMoney); // money required to open menu, 2.0.3
- data << gItem.m_gMessage; // text for gossip item
- data << gItem.m_gBoxMessage; // accept text (related to money) pop up box, 2.0.3
- }
-
- data << uint32( mQuestMenu.MenuItemCount() ); // max count 0x20
-
- for ( uint16 iI = 0; iI < mQuestMenu.MenuItemCount(); iI++ )
- {
- QuestMenuItem const& qItem = mQuestMenu.GetItem(iI);
- uint32 questID = qItem.m_qId;
- Quest const* pQuest = objmgr.GetQuestTemplate(questID);
-
- data << questID;
- data << uint32( qItem.m_qIcon );
- data << uint32( pQuest ? pQuest->GetQuestLevel() : 0 );
- std::string Title = pQuest->GetTitle();
-
- int loc_idx = pSession->GetSessionDbLocaleIndex();
- if (loc_idx >= 0)
- {
- QuestLocale const *ql = objmgr.GetQuestLocale(questID);
- if (ql)
- {
- if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
- Title=ql->Title[loc_idx];
- }
- }
- data << Title;
- }
-
- pSession->SendPacket( &data );
- //sLog.outDebug( "WORLD: Sent SMSG_GOSSIP_MESSAGE NPCGuid=%u",GUID_LOPART(npcGUID) );
-}
-
-void PlayerMenu::CloseGossip()
-{
- WorldPacket data( SMSG_GOSSIP_COMPLETE, 0 );
- pSession->SendPacket( &data );
-
- //sLog.outDebug( "WORLD: Sent SMSG_GOSSIP_COMPLETE" );
-}
-
-void PlayerMenu::SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, char const * locName )
-{
- WorldPacket data( SMSG_GOSSIP_POI, (4+4+4+4+4+10) ); // guess size
- data << Flags;
- data << X << Y;
- data << uint32(Icon);
- data << uint32(Data);
- data << locName;
-
- pSession->SendPacket( &data );
- //sLog.outDebug("WORLD: Sent SMSG_GOSSIP_POI");
-}
-
-void PlayerMenu::SendTalking( uint32 textID )
-{
- GossipText *pGossip;
- std::string GossipStr;
-
- pGossip = objmgr.GetGossipText(textID);
-
- WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size
- data << textID; // can be < 0
-
- if (!pGossip)
- {
- for(uint32 i = 0; i < 8; ++i)
- {
- data << float(0);
- data << "Greetings $N";
- data << "Greetings $N";
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- }
- }
- else
- {
- std::string Text_0[8],Text_1[8];
- for (int i=0;i<8;i++)
- {
- Text_0[i]=pGossip->Options[i].Text_0;
- Text_1[i]=pGossip->Options[i].Text_1;
- }
- int loc_idx = pSession->GetSessionDbLocaleIndex();
- if (loc_idx >= 0)
- {
- NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textID);
- if (nl)
- {
- for (int i=0;i<8;i++)
- {
- if (nl->Text_0[i].size() > loc_idx && !nl->Text_0[i][loc_idx].empty())
- Text_0[i]=nl->Text_0[i][loc_idx];
- if (nl->Text_1[i].size() > loc_idx && !nl->Text_1[i][loc_idx].empty())
- Text_1[i]=nl->Text_1[i][loc_idx];
- }
- }
- }
- for (int i=0; i<8; i++)
- {
- data << pGossip->Options[i].Probability;
-
- if ( Text_0[i].empty() )
- data << Text_1[i];
- else
- data << Text_0[i];
-
- if ( Text_1[i].empty() )
- data << Text_0[i];
- else
- data << Text_1[i];
-
- data << pGossip->Options[i].Language;
-
- data << pGossip->Options[i].Emotes[0]._Delay;
- data << pGossip->Options[i].Emotes[0]._Emote;
-
- data << pGossip->Options[i].Emotes[1]._Delay;
- data << pGossip->Options[i].Emotes[1]._Emote;
-
- data << pGossip->Options[i].Emotes[2]._Delay;
- data << pGossip->Options[i].Emotes[2]._Emote;
- }
- }
- pSession->SendPacket( &data );
-
- sLog.outDebug( "WORLD: Sent SMSG_NPC_TEXT_UPDATE " );
-}
-
-void PlayerMenu::SendTalking( char const * title, char const * text )
-{
- WorldPacket data( SMSG_NPC_TEXT_UPDATE, 50 ); // guess size
- data << uint32(0);
- for(uint32 i = 0; i < 8; ++i)
- {
- data << float(0);
- data << title;
- data << text;
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- }
-
- pSession->SendPacket( &data );
-
- sLog.outDebug( "WORLD: Sent SMSG_NPC_TEXT_UPDATE " );
-}
-
-/*********************************************************/
-/*** QUEST SYSTEM ***/
-/*********************************************************/
-
-QuestMenu::QuestMenu()
-{
- m_qItems.reserve(16); // can be set for max from most often sizes to speedup push_back and less memory use
-}
-
-QuestMenu::~QuestMenu()
-{
- ClearMenu();
-}
-
-void QuestMenu::AddMenuItem( uint32 QuestId, uint8 Icon)
-{
- Quest const* qinfo = objmgr.GetQuestTemplate(QuestId);
- if (!qinfo) return;
-
- ASSERT( m_qItems.size() <= GOSSIP_MAX_MENU_ITEMS );
-
- QuestMenuItem qItem;
-
- qItem.m_qId = QuestId;
- qItem.m_qIcon = Icon;
-
- m_qItems.push_back(qItem);
-}
-
-bool QuestMenu::HasItem( uint32 questid )
-{
- for (QuestMenuItemList::iterator i = m_qItems.begin(); i != m_qItems.end(); i++)
- {
- if(i->m_qId==questid)
- {
- return true;
- }
- }
- return false;
-}
-
-void QuestMenu::ClearMenu()
-{
- m_qItems.clear();
-}
-
-void PlayerMenu::SendQuestGiverQuestList( QEmote eEmote, std::string Title, uint64 npcGUID )
-{
- WorldPacket data( SMSG_QUESTGIVER_QUEST_LIST, 100 ); // guess size
- data << uint64(npcGUID);
- data << Title;
- data << uint32(eEmote._Delay ); // player emote
- data << uint32(eEmote._Emote ); // NPC emote
- data << uint8 ( mQuestMenu.MenuItemCount() );
-
- for ( uint16 iI = 0; iI < mQuestMenu.MenuItemCount(); iI++ )
- {
- QuestMenuItem const& qmi = mQuestMenu.GetItem(iI);
-
- uint32 questID = qmi.m_qId;
- Quest const *pQuest = objmgr.GetQuestTemplate(questID);
-
- std::string title = pQuest ? pQuest->GetTitle() : "";
-
- int loc_idx = pSession->GetSessionDbLocaleIndex();
- if (loc_idx >= 0)
- {
- if(QuestLocale const *ql = objmgr.GetQuestLocale(questID))
- {
- if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
- title=ql->Title[loc_idx];
- }
- }
-
- data << uint32(questID);
- data << uint32(qmi.m_qIcon);
- data << uint32(pQuest ? pQuest->GetQuestLevel() : 0);
- data << title;
- }
- pSession->SendPacket( &data );
- //uint32 fqid=pQuestMenu->GetItem(0).m_qId;
- //sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_QUEST_LIST NPC Guid=%u, questid-0=%u",npcGUID,fqid);
-}
-
-void PlayerMenu::SendQuestGiverStatus( uint8 questStatus, uint64 npcGUID )
-{
- WorldPacket data( SMSG_QUESTGIVER_STATUS, 9 );
- data << uint64(npcGUID);
- data << uint8(questStatus);
-
- pSession->SendPacket( &data );
- sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_STATUS NPC Guid=%u, status=%u",GUID_LOPART(npcGUID),questStatus);
-}
-
-void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID, bool ActivateAccept )
-{
- WorldPacket data(SMSG_QUESTGIVER_QUEST_DETAILS, 100); // guess size
-
- std::string Title = pQuest->GetTitle();
- std::string Details = pQuest->GetDetails();
- std::string Objectives = pQuest->GetObjectives();
- std::string EndText = pQuest->GetEndText();
-
- int loc_idx = pSession->GetSessionDbLocaleIndex();
- if (loc_idx >= 0)
- {
- QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
- if (ql)
- {
- if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
- Title=ql->Title[loc_idx];
- if (ql->Details.size() > loc_idx && !ql->Details[loc_idx].empty())
- Details=ql->Details[loc_idx];
- if (ql->Objectives.size() > loc_idx && !ql->Objectives[loc_idx].empty())
- Objectives=ql->Objectives[loc_idx];
- if (ql->EndText.size() > loc_idx && !ql->EndText[loc_idx].empty())
- EndText=ql->EndText[loc_idx];
- }
- }
-
- data << uint64(npcGUID);
- data << uint32(pQuest->GetQuestId());
- data << Title << Details << Objectives;
- data << uint32(ActivateAccept);
- data << uint32(pQuest->GetSuggestedPlayers());
-
- if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
- {
- data << uint32(0); // Rewarded chosen items hidden
- data << uint32(0); // Rewarded items hidden
- data << uint32(0); // Rewarded money hidden
- }
- else
- {
- ItemPrototype const* IProto;
-
- data << uint32(pQuest->GetRewChoiceItemsCount());
- for (uint32 i=0; i < QUEST_REWARD_CHOICES_COUNT; i++)
- {
- if ( !pQuest->RewChoiceItemId[i] ) continue;
- data << uint32(pQuest->RewChoiceItemId[i]);
- data << uint32(pQuest->RewChoiceItemCount[i]);
- IProto = objmgr.GetItemPrototype(pQuest->RewChoiceItemId[i]);
- if ( IProto )
- data << uint32(IProto->DisplayInfoID);
- else
- data << uint32( 0x00 );
- }
-
- data << uint32(pQuest->GetRewItemsCount());
- for (uint32 i=0; i < QUEST_REWARDS_COUNT; i++)
- {
- if ( !pQuest->RewItemId[i] ) continue;
- data << uint32(pQuest->RewItemId[i]);
- data << uint32(pQuest->RewItemCount[i]);
- IProto = objmgr.GetItemPrototype(pQuest->RewItemId[i]);
- if ( IProto )
- data << uint32(IProto->DisplayInfoID);
- else
- data << uint32(0);
- }
- data << uint32(pQuest->GetRewOrReqMoney());
- }
-
- data << uint32(0); // Honor points reward, not implemented
- data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0)
- data << uint32(pQuest->GetRewSpellCast()); // casted spell
- data << uint32(pQuest->GetCharTitleId()); // CharTitleId, new 2.4.0, player gets this title (id from CharTitles)
-
- data << uint32(QUEST_EMOTE_COUNT);
- for (uint32 i=0; i < QUEST_EMOTE_COUNT; i++)
- {
- data << uint32(pQuest->DetailsEmote[i]);
- data << uint32(0); // DetailsEmoteDelay
- }
- pSession->SendPacket( &data );
-
- sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_DETAILS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId());
-}
-
-void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
-{
- std::string Title,Details,Objectives,EndText;
- std::string ObjectiveText[QUEST_OBJECTIVES_COUNT];
- Title = pQuest->GetTitle();
- Details = pQuest->GetDetails();
- Objectives = pQuest->GetObjectives();
- EndText = pQuest->GetEndText();
- for (int i=0;i<QUEST_OBJECTIVES_COUNT;i++)
- ObjectiveText[i]=pQuest->ObjectiveText[i];
-
- int loc_idx = pSession->GetSessionDbLocaleIndex();
- if (loc_idx >= 0)
- {
- QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
- if (ql)
- {
- if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
- Title=ql->Title[loc_idx];
- if (ql->Details.size() > loc_idx && !ql->Details[loc_idx].empty())
- Details=ql->Details[loc_idx];
- if (ql->Objectives.size() > loc_idx && !ql->Objectives[loc_idx].empty())
- Objectives=ql->Objectives[loc_idx];
- if (ql->EndText.size() > loc_idx && !ql->EndText[loc_idx].empty())
- EndText=ql->EndText[loc_idx];
-
- for (int i=0;i<QUEST_OBJECTIVES_COUNT;i++)
- if (ql->ObjectiveText[i].size() > loc_idx && !ql->ObjectiveText[i][loc_idx].empty())
- ObjectiveText[i]=ql->ObjectiveText[i][loc_idx];
- }
- }
-
- WorldPacket data( SMSG_QUEST_QUERY_RESPONSE, 100 ); // guess size
-
- data << uint32(pQuest->GetQuestId());
- data << uint32(pQuest->GetQuestMethod()); // Accepted values: 0, 1 or 2. 0==IsAutoComplete() (skip objectives/details)
- data << uint32(pQuest->GetQuestLevel()); // may be 0
- data << uint32(pQuest->GetZoneOrSort()); // zone or sort to display in quest log
-
- data << uint32(pQuest->GetType());
- data << uint32(pQuest->GetSuggestedPlayers());
-
- data << uint32(pQuest->GetRepObjectiveFaction()); // shown in quest log as part of quest objective
- data << uint32(pQuest->GetRepObjectiveValue()); // shown in quest log as part of quest objective
-
- data << uint32(0); // RequiredOpositeRepFaction
- data << uint32(0); // RequiredOpositeRepValue, required faction value with another (oposite) faction (objective)
-
- data << uint32(pQuest->GetNextQuestInChain()); // client will request this quest from NPC, if not 0
-
- if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
- data << uint32(0); // Hide money rewarded
- else
- data << uint32(pQuest->GetRewOrReqMoney());
-
- data << uint32(pQuest->GetRewMoneyMaxLevel()); // used in XP calculation at client
- data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0)
- data << uint32(pQuest->GetRewSpellCast()); // casted spell
-
- data << uint32(0); // Honor points reward, not implemented
- data << uint32(pQuest->GetSrcItemId());
- data << uint32(pQuest->GetFlags() & 0xFFFF);
- data << uint32(pQuest->GetCharTitleId()); // CharTitleId, new 2.4.0, player gets this title (id from CharTitles)
-
- int iI;
-
- if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
- {
- for (iI = 0; iI < QUEST_REWARDS_COUNT; iI++)
- data << uint32(0) << uint32(0);
- for (iI = 0; iI < QUEST_REWARD_CHOICES_COUNT; iI++)
- data << uint32(0) << uint32(0);
- }
- else
- {
- for (iI = 0; iI < QUEST_REWARDS_COUNT; iI++)
- {
- data << uint32(pQuest->RewItemId[iI]);
- data << uint32(pQuest->RewItemCount[iI]);
- }
- for (iI = 0; iI < QUEST_REWARD_CHOICES_COUNT; iI++)
- {
- data << uint32(pQuest->RewChoiceItemId[iI]);
- data << uint32(pQuest->RewChoiceItemCount[iI]);
- }
- }
-
- data << pQuest->GetPointMapId();
- data << pQuest->GetPointX();
- data << pQuest->GetPointY();
- data << pQuest->GetPointOpt();
-
- data << Title;
- data << Objectives;
- data << Details;
- data << EndText;
-
- for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; iI++)
- {
- if (pQuest->ReqCreatureOrGOId[iI] < 0)
- {
- // client expected gameobject template id in form (id|0x80000000)
- data << uint32((pQuest->ReqCreatureOrGOId[iI]*(-1))|0x80000000);
- }
- else
- {
- data << uint32(pQuest->ReqCreatureOrGOId[iI]);
- }
- data << uint32(pQuest->ReqCreatureOrGOCount[iI]);
- data << uint32(pQuest->ReqItemId[iI]);
- data << uint32(pQuest->ReqItemCount[iI]);
- }
-
- for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; iI++)
- data << ObjectiveText[iI];
-
- pSession->SendPacket( &data );
- sLog.outDebug( "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u",pQuest->GetQuestId() );
-}
-
-void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, bool EnbleNext )
-{
- std::string Title = pQuest->GetTitle();
- std::string OfferRewardText = pQuest->GetOfferRewardText();
-
- int loc_idx = pSession->GetSessionDbLocaleIndex();
- if (loc_idx >= 0)
- {
- QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
- if (ql)
- {
- if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
- Title=ql->Title[loc_idx];
- if (ql->OfferRewardText.size() > loc_idx && !ql->OfferRewardText[loc_idx].empty())
- OfferRewardText=ql->OfferRewardText[loc_idx];
- }
- }
-
- WorldPacket data( SMSG_QUESTGIVER_OFFER_REWARD, 50 ); // guess size
-
- data << npcGUID;
- data << pQuest->GetQuestId();
- data << Title;
- data << OfferRewardText;
-
- data << uint32( EnbleNext );
- data << uint32(0); // unk
-
- uint32 EmoteCount = 0;
- for (uint32 i = 0; i < QUEST_EMOTE_COUNT; i++)
- {
- if(pQuest->OfferRewardEmote[i] <= 0)
- break;
- ++EmoteCount;
- }
-
- data << EmoteCount; // Emote Count
- for (uint32 i = 0; i < EmoteCount; i++)
- {
- data << uint32(0); // Delay Emote
- data << pQuest->OfferRewardEmote[i];
- }
-
- ItemPrototype const *pItem;
-
- data << uint32(pQuest->GetRewChoiceItemsCount());
- for (uint32 i=0; i < pQuest->GetRewChoiceItemsCount(); i++)
- {
- pItem = objmgr.GetItemPrototype( pQuest->RewChoiceItemId[i] );
-
- data << uint32(pQuest->RewChoiceItemId[i]);
- data << uint32(pQuest->RewChoiceItemCount[i]);
-
- if ( pItem )
- data << uint32(pItem->DisplayInfoID);
- else
- data << uint32(0);
- }
-
- data << uint32(pQuest->GetRewItemsCount());
- for (uint16 i=0; i < pQuest->GetRewItemsCount(); i++)
- {
- pItem = objmgr.GetItemPrototype(pQuest->RewItemId[i]);
- data << uint32(pQuest->RewItemId[i]);
- data << uint32(pQuest->RewItemCount[i]);
-
- if ( pItem )
- data << uint32(pItem->DisplayInfoID);
- else
- data << uint32(0);
- }
-
- data << uint32(pQuest->GetRewOrReqMoney());
- data << uint32(0x00); // new 2.3.0. Honor points
- data << uint32(0x08); // unused by client?
- data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0)
- data << uint32(pQuest->GetRewSpellCast()); // casted spell
- data << uint32(0); // Honor points reward, not implemented
- pSession->SendPacket( &data );
- sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
-}
-
-void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID, bool Completable, bool CloseOnCancel )
-{
- // We can always call to RequestItems, but this packet only goes out if there are actually
- // items. Otherwise, we'll skip straight to the OfferReward
-
- // We may wish a better check, perhaps checking the real quest requirements
- if (pQuest->GetRequestItemsText().empty())
- {
- SendQuestGiverOfferReward(pQuest, npcGUID, true);
- return;
- }
-
- std::string Title,RequestItemsText;
- Title = pQuest->GetTitle();
- RequestItemsText = pQuest->GetRequestItemsText();
-
- int loc_idx = pSession->GetSessionDbLocaleIndex();
- if (loc_idx >= 0)
- {
- QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
- if (ql)
- {
- if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
- Title=ql->Title[loc_idx];
- if (ql->RequestItemsText.size() > loc_idx && !ql->RequestItemsText[loc_idx].empty())
- RequestItemsText=ql->RequestItemsText[loc_idx];
- }
- }
-
- WorldPacket data( SMSG_QUESTGIVER_REQUEST_ITEMS, 50 ); // guess size
- data << npcGUID;
- data << pQuest->GetQuestId();
- data << Title;
- data << RequestItemsText;
-
- data << uint32(0x00); // unknown
-
- if(Completable)
- data << pQuest->GetCompleteEmote();
- else
- data << pQuest->GetIncompleteEmote();
-
- // Close Window after cancel
- if (CloseOnCancel)
- data << uint32(0x01);
- else
- data << uint32(0x00);
-
- data << uint32(0x00); // unknown
-
- // Required Money
- data << uint32(pQuest->GetRewOrReqMoney() < 0 ? -pQuest->GetRewOrReqMoney() : 0);
-
- data << uint32( pQuest->GetReqItemsCount() );
- ItemPrototype const *pItem;
- for (int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
- {
- if ( !pQuest->ReqItemId[i] ) continue;
- pItem = objmgr.GetItemPrototype(pQuest->ReqItemId[i]);
- data << uint32(pQuest->ReqItemId[i]);
- data << uint32(pQuest->ReqItemCount[i]);
-
- if ( pItem )
- data << uint32(pItem->DisplayInfoID);
- else
- data << uint32(0);
- }
-
- if ( !Completable )
- data << uint32(0x00);
- else
- data << uint32(0x03);
-
- data << uint32(0x04) << uint32(0x08) << uint32(0x10);
-
- pSession->SendPacket( &data );
- sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
-}
+/*
+ * 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 "QuestDef.h"
+#include "GossipDef.h"
+#include "ObjectMgr.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+
+GossipMenu::GossipMenu()
+{
+ m_gItems.reserve(16); // can be set for max from most often sizes to speedup push_back and less memory use
+}
+
+GossipMenu::~GossipMenu()
+{
+ ClearMenu();
+}
+
+void GossipMenu::AddMenuItem(uint8 Icon, std::string Message, uint32 dtSender, uint32 dtAction, std::string BoxMessage, uint32 BoxMoney, bool Coded)
+{
+ ASSERT( m_gItems.size() <= GOSSIP_MAX_MENU_ITEMS );
+
+ GossipMenuItem gItem;
+
+ gItem.m_gIcon = Icon;
+ gItem.m_gMessage = Message;
+ gItem.m_gCoded = Coded;
+ gItem.m_gSender = dtSender;
+ gItem.m_gAction = dtAction;
+ gItem.m_gBoxMessage = BoxMessage;
+ gItem.m_gBoxMoney = BoxMoney;
+
+ m_gItems.push_back(gItem);
+}
+
+void GossipMenu::AddMenuItem(uint8 Icon, std::string Message, bool Coded)
+{
+ AddMenuItem( Icon, Message, 0, 0, "", 0, Coded);
+}
+
+void GossipMenu::AddMenuItem(uint8 Icon, char const* Message, bool Coded)
+{
+ AddMenuItem(Icon, std::string(Message ? Message : ""),Coded);
+}
+
+void GossipMenu::AddMenuItem(uint8 Icon, char const* Message, uint32 dtSender, uint32 dtAction, char const* BoxMessage, uint32 BoxMoney, bool Coded)
+{
+ AddMenuItem(Icon, std::string(Message ? Message : ""), dtSender, dtAction, std::string(BoxMessage ? BoxMessage : ""), BoxMoney, Coded);
+}
+
+uint32 GossipMenu::MenuItemSender( unsigned int ItemId )
+{
+ if ( ItemId >= m_gItems.size() ) return 0;
+
+ return m_gItems[ ItemId ].m_gSender;
+}
+
+uint32 GossipMenu::MenuItemAction( unsigned int ItemId )
+{
+ if ( ItemId >= m_gItems.size() ) return 0;
+
+ return m_gItems[ ItemId ].m_gAction;
+}
+
+bool GossipMenu::MenuItemCoded( unsigned int ItemId )
+{
+ if ( ItemId >= m_gItems.size() ) return 0;
+
+ return m_gItems[ ItemId ].m_gCoded;
+}
+
+void GossipMenu::ClearMenu()
+{
+ m_gItems.clear();
+}
+
+PlayerMenu::PlayerMenu( WorldSession *session ) : pSession(session)
+{
+}
+
+PlayerMenu::~PlayerMenu()
+{
+ ClearMenus();
+}
+
+void PlayerMenu::ClearMenus()
+{
+ mGossipMenu.ClearMenu();
+ mQuestMenu.ClearMenu();
+}
+
+uint32 PlayerMenu::GossipOptionSender( unsigned int Selection )
+{
+ return mGossipMenu.MenuItemSender( Selection );
+}
+
+uint32 PlayerMenu::GossipOptionAction( unsigned int Selection )
+{
+ return mGossipMenu.MenuItemAction( Selection );
+}
+
+bool PlayerMenu::GossipOptionCoded( unsigned int Selection )
+{
+ return mGossipMenu.MenuItemCoded( Selection );
+}
+
+void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
+{
+ WorldPacket data( SMSG_GOSSIP_MESSAGE, (100) ); // guess size
+ data << npcGUID;
+ data << uint32(0); // new 2.4.0
+ data << uint32( TitleTextId );
+ data << uint32( mGossipMenu.MenuItemCount() ); // max count 0x0F
+
+ for ( unsigned int iI = 0; iI < mGossipMenu.MenuItemCount(); iI++ )
+ {
+ GossipMenuItem const& gItem = mGossipMenu.GetItem(iI);
+ data << uint32( iI );
+ data << uint8( gItem.m_gIcon );
+ // icons:
+ // 0 unlearn talents/misc
+ // 1 trader
+ // 2 taxi
+ // 3 trainer
+ // 9 BG/arena
+ data << uint8( gItem.m_gCoded ); // makes pop up box password
+ data << uint32(gItem.m_gBoxMoney); // money required to open menu, 2.0.3
+ data << gItem.m_gMessage; // text for gossip item
+ data << gItem.m_gBoxMessage; // accept text (related to money) pop up box, 2.0.3
+ }
+
+ data << uint32( mQuestMenu.MenuItemCount() ); // max count 0x20
+
+ for ( uint16 iI = 0; iI < mQuestMenu.MenuItemCount(); iI++ )
+ {
+ QuestMenuItem const& qItem = mQuestMenu.GetItem(iI);
+ uint32 questID = qItem.m_qId;
+ Quest const* pQuest = objmgr.GetQuestTemplate(questID);
+
+ data << questID;
+ data << uint32( qItem.m_qIcon );
+ data << uint32( pQuest ? pQuest->GetQuestLevel() : 0 );
+ std::string Title = pQuest->GetTitle();
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(questID);
+ if (ql)
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ Title=ql->Title[loc_idx];
+ }
+ }
+ data << Title;
+ }
+
+ pSession->SendPacket( &data );
+ //sLog.outDebug( "WORLD: Sent SMSG_GOSSIP_MESSAGE NPCGuid=%u",GUID_LOPART(npcGUID) );
+}
+
+void PlayerMenu::CloseGossip()
+{
+ WorldPacket data( SMSG_GOSSIP_COMPLETE, 0 );
+ pSession->SendPacket( &data );
+
+ //sLog.outDebug( "WORLD: Sent SMSG_GOSSIP_COMPLETE" );
+}
+
+void PlayerMenu::SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, char const * locName )
+{
+ WorldPacket data( SMSG_GOSSIP_POI, (4+4+4+4+4+10) ); // guess size
+ data << Flags;
+ data << X << Y;
+ data << uint32(Icon);
+ data << uint32(Data);
+ data << locName;
+
+ pSession->SendPacket( &data );
+ //sLog.outDebug("WORLD: Sent SMSG_GOSSIP_POI");
+}
+
+void PlayerMenu::SendTalking( uint32 textID )
+{
+ GossipText *pGossip;
+ std::string GossipStr;
+
+ pGossip = objmgr.GetGossipText(textID);
+
+ WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size
+ data << textID; // can be < 0
+
+ if (!pGossip)
+ {
+ for(uint32 i = 0; i < 8; ++i)
+ {
+ data << float(0);
+ data << "Greetings $N";
+ data << "Greetings $N";
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ }
+ }
+ else
+ {
+ std::string Text_0[8],Text_1[8];
+ for (int i=0;i<8;i++)
+ {
+ Text_0[i]=pGossip->Options[i].Text_0;
+ Text_1[i]=pGossip->Options[i].Text_1;
+ }
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textID);
+ if (nl)
+ {
+ for (int i=0;i<8;i++)
+ {
+ if (nl->Text_0[i].size() > loc_idx && !nl->Text_0[i][loc_idx].empty())
+ Text_0[i]=nl->Text_0[i][loc_idx];
+ if (nl->Text_1[i].size() > loc_idx && !nl->Text_1[i][loc_idx].empty())
+ Text_1[i]=nl->Text_1[i][loc_idx];
+ }
+ }
+ }
+ for (int i=0; i<8; i++)
+ {
+ data << pGossip->Options[i].Probability;
+
+ if ( Text_0[i].empty() )
+ data << Text_1[i];
+ else
+ data << Text_0[i];
+
+ if ( Text_1[i].empty() )
+ data << Text_0[i];
+ else
+ data << Text_1[i];
+
+ data << pGossip->Options[i].Language;
+
+ data << pGossip->Options[i].Emotes[0]._Delay;
+ data << pGossip->Options[i].Emotes[0]._Emote;
+
+ data << pGossip->Options[i].Emotes[1]._Delay;
+ data << pGossip->Options[i].Emotes[1]._Emote;
+
+ data << pGossip->Options[i].Emotes[2]._Delay;
+ data << pGossip->Options[i].Emotes[2]._Emote;
+ }
+ }
+ pSession->SendPacket( &data );
+
+ sLog.outDebug( "WORLD: Sent SMSG_NPC_TEXT_UPDATE " );
+}
+
+void PlayerMenu::SendTalking( char const * title, char const * text )
+{
+ WorldPacket data( SMSG_NPC_TEXT_UPDATE, 50 ); // guess size
+ data << uint32(0);
+ for(uint32 i = 0; i < 8; ++i)
+ {
+ data << float(0);
+ data << title;
+ data << text;
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ }
+
+ pSession->SendPacket( &data );
+
+ sLog.outDebug( "WORLD: Sent SMSG_NPC_TEXT_UPDATE " );
+}
+
+/*********************************************************/
+/*** QUEST SYSTEM ***/
+/*********************************************************/
+
+QuestMenu::QuestMenu()
+{
+ m_qItems.reserve(16); // can be set for max from most often sizes to speedup push_back and less memory use
+}
+
+QuestMenu::~QuestMenu()
+{
+ ClearMenu();
+}
+
+void QuestMenu::AddMenuItem( uint32 QuestId, uint8 Icon)
+{
+ Quest const* qinfo = objmgr.GetQuestTemplate(QuestId);
+ if (!qinfo) return;
+
+ ASSERT( m_qItems.size() <= GOSSIP_MAX_MENU_ITEMS );
+
+ QuestMenuItem qItem;
+
+ qItem.m_qId = QuestId;
+ qItem.m_qIcon = Icon;
+
+ m_qItems.push_back(qItem);
+}
+
+bool QuestMenu::HasItem( uint32 questid )
+{
+ for (QuestMenuItemList::iterator i = m_qItems.begin(); i != m_qItems.end(); i++)
+ {
+ if(i->m_qId==questid)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void QuestMenu::ClearMenu()
+{
+ m_qItems.clear();
+}
+
+void PlayerMenu::SendQuestGiverQuestList( QEmote eEmote, std::string Title, uint64 npcGUID )
+{
+ WorldPacket data( SMSG_QUESTGIVER_QUEST_LIST, 100 ); // guess size
+ data << uint64(npcGUID);
+ data << Title;
+ data << uint32(eEmote._Delay ); // player emote
+ data << uint32(eEmote._Emote ); // NPC emote
+ data << uint8 ( mQuestMenu.MenuItemCount() );
+
+ for ( uint16 iI = 0; iI < mQuestMenu.MenuItemCount(); iI++ )
+ {
+ QuestMenuItem const& qmi = mQuestMenu.GetItem(iI);
+
+ uint32 questID = qmi.m_qId;
+ Quest const *pQuest = objmgr.GetQuestTemplate(questID);
+
+ std::string title = pQuest ? pQuest->GetTitle() : "";
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ if(QuestLocale const *ql = objmgr.GetQuestLocale(questID))
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ title=ql->Title[loc_idx];
+ }
+ }
+
+ data << uint32(questID);
+ data << uint32(qmi.m_qIcon);
+ data << uint32(pQuest ? pQuest->GetQuestLevel() : 0);
+ data << title;
+ }
+ pSession->SendPacket( &data );
+ //uint32 fqid=pQuestMenu->GetItem(0).m_qId;
+ //sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_QUEST_LIST NPC Guid=%u, questid-0=%u",npcGUID,fqid);
+}
+
+void PlayerMenu::SendQuestGiverStatus( uint8 questStatus, uint64 npcGUID )
+{
+ WorldPacket data( SMSG_QUESTGIVER_STATUS, 9 );
+ data << uint64(npcGUID);
+ data << uint8(questStatus);
+
+ pSession->SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_STATUS NPC Guid=%u, status=%u",GUID_LOPART(npcGUID),questStatus);
+}
+
+void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID, bool ActivateAccept )
+{
+ WorldPacket data(SMSG_QUESTGIVER_QUEST_DETAILS, 100); // guess size
+
+ std::string Title = pQuest->GetTitle();
+ std::string Details = pQuest->GetDetails();
+ std::string Objectives = pQuest->GetObjectives();
+ std::string EndText = pQuest->GetEndText();
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
+ if (ql)
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ Title=ql->Title[loc_idx];
+ if (ql->Details.size() > loc_idx && !ql->Details[loc_idx].empty())
+ Details=ql->Details[loc_idx];
+ if (ql->Objectives.size() > loc_idx && !ql->Objectives[loc_idx].empty())
+ Objectives=ql->Objectives[loc_idx];
+ if (ql->EndText.size() > loc_idx && !ql->EndText[loc_idx].empty())
+ EndText=ql->EndText[loc_idx];
+ }
+ }
+
+ data << uint64(npcGUID);
+ data << uint32(pQuest->GetQuestId());
+ data << Title << Details << Objectives;
+ data << uint32(ActivateAccept);
+ data << uint32(pQuest->GetSuggestedPlayers());
+
+ if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
+ {
+ data << uint32(0); // Rewarded chosen items hidden
+ data << uint32(0); // Rewarded items hidden
+ data << uint32(0); // Rewarded money hidden
+ }
+ else
+ {
+ ItemPrototype const* IProto;
+
+ data << uint32(pQuest->GetRewChoiceItemsCount());
+ for (uint32 i=0; i < QUEST_REWARD_CHOICES_COUNT; i++)
+ {
+ if ( !pQuest->RewChoiceItemId[i] ) continue;
+ data << uint32(pQuest->RewChoiceItemId[i]);
+ data << uint32(pQuest->RewChoiceItemCount[i]);
+ IProto = objmgr.GetItemPrototype(pQuest->RewChoiceItemId[i]);
+ if ( IProto )
+ data << uint32(IProto->DisplayInfoID);
+ else
+ data << uint32( 0x00 );
+ }
+
+ data << uint32(pQuest->GetRewItemsCount());
+ for (uint32 i=0; i < QUEST_REWARDS_COUNT; i++)
+ {
+ if ( !pQuest->RewItemId[i] ) continue;
+ data << uint32(pQuest->RewItemId[i]);
+ data << uint32(pQuest->RewItemCount[i]);
+ IProto = objmgr.GetItemPrototype(pQuest->RewItemId[i]);
+ if ( IProto )
+ data << uint32(IProto->DisplayInfoID);
+ else
+ data << uint32(0);
+ }
+ data << uint32(pQuest->GetRewOrReqMoney());
+ }
+
+ data << uint32(0); // Honor points reward, not implemented
+ data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0)
+ data << uint32(pQuest->GetRewSpellCast()); // casted spell
+ data << uint32(pQuest->GetCharTitleId()); // CharTitleId, new 2.4.0, player gets this title (id from CharTitles)
+
+ data << uint32(QUEST_EMOTE_COUNT);
+ for (uint32 i=0; i < QUEST_EMOTE_COUNT; i++)
+ {
+ data << uint32(pQuest->DetailsEmote[i]);
+ data << uint32(0); // DetailsEmoteDelay
+ }
+ pSession->SendPacket( &data );
+
+ sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_DETAILS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId());
+}
+
+void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
+{
+ std::string Title,Details,Objectives,EndText;
+ std::string ObjectiveText[QUEST_OBJECTIVES_COUNT];
+ Title = pQuest->GetTitle();
+ Details = pQuest->GetDetails();
+ Objectives = pQuest->GetObjectives();
+ EndText = pQuest->GetEndText();
+ for (int i=0;i<QUEST_OBJECTIVES_COUNT;i++)
+ ObjectiveText[i]=pQuest->ObjectiveText[i];
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
+ if (ql)
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ Title=ql->Title[loc_idx];
+ if (ql->Details.size() > loc_idx && !ql->Details[loc_idx].empty())
+ Details=ql->Details[loc_idx];
+ if (ql->Objectives.size() > loc_idx && !ql->Objectives[loc_idx].empty())
+ Objectives=ql->Objectives[loc_idx];
+ if (ql->EndText.size() > loc_idx && !ql->EndText[loc_idx].empty())
+ EndText=ql->EndText[loc_idx];
+
+ for (int i=0;i<QUEST_OBJECTIVES_COUNT;i++)
+ if (ql->ObjectiveText[i].size() > loc_idx && !ql->ObjectiveText[i][loc_idx].empty())
+ ObjectiveText[i]=ql->ObjectiveText[i][loc_idx];
+ }
+ }
+
+ WorldPacket data( SMSG_QUEST_QUERY_RESPONSE, 100 ); // guess size
+
+ data << uint32(pQuest->GetQuestId());
+ data << uint32(pQuest->GetQuestMethod()); // Accepted values: 0, 1 or 2. 0==IsAutoComplete() (skip objectives/details)
+ data << uint32(pQuest->GetQuestLevel()); // may be 0
+ data << uint32(pQuest->GetZoneOrSort()); // zone or sort to display in quest log
+
+ data << uint32(pQuest->GetType());
+ data << uint32(pQuest->GetSuggestedPlayers());
+
+ data << uint32(pQuest->GetRepObjectiveFaction()); // shown in quest log as part of quest objective
+ data << uint32(pQuest->GetRepObjectiveValue()); // shown in quest log as part of quest objective
+
+ data << uint32(0); // RequiredOpositeRepFaction
+ data << uint32(0); // RequiredOpositeRepValue, required faction value with another (oposite) faction (objective)
+
+ data << uint32(pQuest->GetNextQuestInChain()); // client will request this quest from NPC, if not 0
+
+ if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
+ data << uint32(0); // Hide money rewarded
+ else
+ data << uint32(pQuest->GetRewOrReqMoney());
+
+ data << uint32(pQuest->GetRewMoneyMaxLevel()); // used in XP calculation at client
+ data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0)
+ data << uint32(pQuest->GetRewSpellCast()); // casted spell
+
+ data << uint32(0); // Honor points reward, not implemented
+ data << uint32(pQuest->GetSrcItemId());
+ data << uint32(pQuest->GetFlags() & 0xFFFF);
+ data << uint32(pQuest->GetCharTitleId()); // CharTitleId, new 2.4.0, player gets this title (id from CharTitles)
+
+ int iI;
+
+ if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
+ {
+ for (iI = 0; iI < QUEST_REWARDS_COUNT; iI++)
+ data << uint32(0) << uint32(0);
+ for (iI = 0; iI < QUEST_REWARD_CHOICES_COUNT; iI++)
+ data << uint32(0) << uint32(0);
+ }
+ else
+ {
+ for (iI = 0; iI < QUEST_REWARDS_COUNT; iI++)
+ {
+ data << uint32(pQuest->RewItemId[iI]);
+ data << uint32(pQuest->RewItemCount[iI]);
+ }
+ for (iI = 0; iI < QUEST_REWARD_CHOICES_COUNT; iI++)
+ {
+ data << uint32(pQuest->RewChoiceItemId[iI]);
+ data << uint32(pQuest->RewChoiceItemCount[iI]);
+ }
+ }
+
+ data << pQuest->GetPointMapId();
+ data << pQuest->GetPointX();
+ data << pQuest->GetPointY();
+ data << pQuest->GetPointOpt();
+
+ data << Title;
+ data << Objectives;
+ data << Details;
+ data << EndText;
+
+ for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; iI++)
+ {
+ if (pQuest->ReqCreatureOrGOId[iI] < 0)
+ {
+ // client expected gameobject template id in form (id|0x80000000)
+ data << uint32((pQuest->ReqCreatureOrGOId[iI]*(-1))|0x80000000);
+ }
+ else
+ {
+ data << uint32(pQuest->ReqCreatureOrGOId[iI]);
+ }
+ data << uint32(pQuest->ReqCreatureOrGOCount[iI]);
+ data << uint32(pQuest->ReqItemId[iI]);
+ data << uint32(pQuest->ReqItemCount[iI]);
+ }
+
+ for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; iI++)
+ data << ObjectiveText[iI];
+
+ pSession->SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u",pQuest->GetQuestId() );
+}
+
+void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, bool EnbleNext )
+{
+ std::string Title = pQuest->GetTitle();
+ std::string OfferRewardText = pQuest->GetOfferRewardText();
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
+ if (ql)
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ Title=ql->Title[loc_idx];
+ if (ql->OfferRewardText.size() > loc_idx && !ql->OfferRewardText[loc_idx].empty())
+ OfferRewardText=ql->OfferRewardText[loc_idx];
+ }
+ }
+
+ WorldPacket data( SMSG_QUESTGIVER_OFFER_REWARD, 50 ); // guess size
+
+ data << npcGUID;
+ data << pQuest->GetQuestId();
+ data << Title;
+ data << OfferRewardText;
+
+ data << uint32( EnbleNext );
+ data << uint32(0); // unk
+
+ uint32 EmoteCount = 0;
+ for (uint32 i = 0; i < QUEST_EMOTE_COUNT; i++)
+ {
+ if(pQuest->OfferRewardEmote[i] <= 0)
+ break;
+ ++EmoteCount;
+ }
+
+ data << EmoteCount; // Emote Count
+ for (uint32 i = 0; i < EmoteCount; i++)
+ {
+ data << uint32(0); // Delay Emote
+ data << pQuest->OfferRewardEmote[i];
+ }
+
+ ItemPrototype const *pItem;
+
+ data << uint32(pQuest->GetRewChoiceItemsCount());
+ for (uint32 i=0; i < pQuest->GetRewChoiceItemsCount(); i++)
+ {
+ pItem = objmgr.GetItemPrototype( pQuest->RewChoiceItemId[i] );
+
+ data << uint32(pQuest->RewChoiceItemId[i]);
+ data << uint32(pQuest->RewChoiceItemCount[i]);
+
+ if ( pItem )
+ data << uint32(pItem->DisplayInfoID);
+ else
+ data << uint32(0);
+ }
+
+ data << uint32(pQuest->GetRewItemsCount());
+ for (uint16 i=0; i < pQuest->GetRewItemsCount(); i++)
+ {
+ pItem = objmgr.GetItemPrototype(pQuest->RewItemId[i]);
+ data << uint32(pQuest->RewItemId[i]);
+ data << uint32(pQuest->RewItemCount[i]);
+
+ if ( pItem )
+ data << uint32(pItem->DisplayInfoID);
+ else
+ data << uint32(0);
+ }
+
+ data << uint32(pQuest->GetRewOrReqMoney());
+ data << uint32(0x00); // new 2.3.0. Honor points
+ data << uint32(0x08); // unused by client?
+ data << uint32(pQuest->GetRewSpell()); // reward spell, this spell will display (icon) (casted if RewSpellCast==0)
+ data << uint32(pQuest->GetRewSpellCast()); // casted spell
+ data << uint32(0); // Honor points reward, not implemented
+ pSession->SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
+}
+
+void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID, bool Completable, bool CloseOnCancel )
+{
+ // We can always call to RequestItems, but this packet only goes out if there are actually
+ // items. Otherwise, we'll skip straight to the OfferReward
+
+ // We may wish a better check, perhaps checking the real quest requirements
+ if (pQuest->GetRequestItemsText().empty())
+ {
+ SendQuestGiverOfferReward(pQuest, npcGUID, true);
+ return;
+ }
+
+ std::string Title,RequestItemsText;
+ Title = pQuest->GetTitle();
+ RequestItemsText = pQuest->GetRequestItemsText();
+
+ int loc_idx = pSession->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(pQuest->GetQuestId());
+ if (ql)
+ {
+ if (ql->Title.size() > loc_idx && !ql->Title[loc_idx].empty())
+ Title=ql->Title[loc_idx];
+ if (ql->RequestItemsText.size() > loc_idx && !ql->RequestItemsText[loc_idx].empty())
+ RequestItemsText=ql->RequestItemsText[loc_idx];
+ }
+ }
+
+ WorldPacket data( SMSG_QUESTGIVER_REQUEST_ITEMS, 50 ); // guess size
+ data << npcGUID;
+ data << pQuest->GetQuestId();
+ data << Title;
+ data << RequestItemsText;
+
+ data << uint32(0x00); // unknown
+
+ if(Completable)
+ data << pQuest->GetCompleteEmote();
+ else
+ data << pQuest->GetIncompleteEmote();
+
+ // Close Window after cancel
+ if (CloseOnCancel)
+ data << uint32(0x01);
+ else
+ data << uint32(0x00);
+
+ data << uint32(0x00); // unknown
+
+ // Required Money
+ data << uint32(pQuest->GetRewOrReqMoney() < 0 ? -pQuest->GetRewOrReqMoney() : 0);
+
+ data << uint32( pQuest->GetReqItemsCount() );
+ ItemPrototype const *pItem;
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if ( !pQuest->ReqItemId[i] ) continue;
+ pItem = objmgr.GetItemPrototype(pQuest->ReqItemId[i]);
+ data << uint32(pQuest->ReqItemId[i]);
+ data << uint32(pQuest->ReqItemCount[i]);
+
+ if ( pItem )
+ data << uint32(pItem->DisplayInfoID);
+ else
+ data << uint32(0);
+ }
+
+ if ( !Completable )
+ data << uint32(0x00);
+ else
+ data << uint32(0x03);
+
+ data << uint32(0x04) << uint32(0x08) << uint32(0x10);
+
+ pSession->SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
+}
diff --git a/src/game/GossipDef.h b/src/game/GossipDef.h
index 18f7c007aa1..10a08987d40 100644
--- a/src/game/GossipDef.h
+++ b/src/game/GossipDef.h
@@ -1,206 +1,206 @@
-/*
- * 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
- */
-
-#ifndef MANGOSSERVER_GOSSIP_H
-#define MANGOSSERVER_GOSSIP_H
-
-#include "Common.h"
-#include "QuestDef.h"
-#include "NPCHandler.h"
-
-class WorldSession;
-
-#define GOSSIP_MAX_MENU_ITEMS 64 // client supported items unknown, but provided number must be enough
-#define DEFAULT_GOSSIP_MESSAGE 0xffffff
-
-//POI defines
-enum Poi_Icon
-{
- ICON_POI_0 = 0, // Grey ?
- ICON_POI_1 = 1, // Red ?
- ICON_POI_2 = 2, // Blue ?
- ICON_POI_BWTOMB = 3, // Blue and White Tomb Stone
- ICON_POI_HOUSE = 4, // House
- ICON_POI_TOWER = 5, // Tower
- ICON_POI_REDFLAG = 6, // Red Flag with Yellow !
- ICON_POI_TOMB = 7, // Tomb Stone
- ICON_POI_BWTOWER = 8, // Blue and White Tower
- ICON_POI_REDTOWER = 9, // Red Tower
- ICON_POI_BLUETOWER = 10, // Blue Tower
- ICON_POI_RWTOWER = 11, // Red and White Tower
- ICON_POI_REDTOMB = 12, // Red Tomb Stone
- ICON_POI_RWTOMB = 13, // Red and White Tomb Stone
- ICON_POI_BLUETOMB = 14, // Blue Tomb Stone
- ICON_POI_NOTHING = 15, // NOTHING
- ICON_POI_16 = 16, // Red ?
- ICON_POI_17 = 17, // Grey ?
- ICON_POI_18 = 18, // Blue ?
- ICON_POI_19 = 19, // Red and White ?
- ICON_POI_20 = 20, // Red ?
- ICON_POI_GREYLOGS = 21, // Grey Wood Logs
- ICON_POI_BWLOGS = 22, // Blue and White Wood Logs
- ICON_POI_BLUELOGS = 23, // Blue Wood Logs
- ICON_POI_RWLOGS = 24, // Red and White Wood Logs
- ICON_POI_REDLOGS = 25, // Red Wood Logs
- ICON_POI_26 = 26, // Grey ?
- ICON_POI_27 = 27, // Blue and White ?
- ICON_POI_28 = 28, // Blue ?
- ICON_POI_29 = 29, // Red and White ?
- ICON_POI_30 = 30, // Red ?
- ICON_POI_GREYHOUSE = 31, // Grey House
- ICON_POI_BWHOUSE = 32, // Blue and White House
- ICON_POI_BLUEHOUSE = 33, // Blue House
- ICON_POI_RWHOUSE = 34, // Red and White House
- ICON_POI_REDHOUSE = 35, // Red House
- ICON_POI_GREYHORSE = 36, // Grey Horse
- ICON_POI_BWHORSE = 37, // Blue and White Horse
- ICON_POI_BLUEHORSE = 38, // Blue Horse
- ICON_POI_RWHORSE = 39, // Red and White Horse
- ICON_POI_REDHORSE = 40 // Red Horse
-};
-
-struct GossipMenuItem
-{
- uint8 m_gIcon;
- bool m_gCoded;
- std::string m_gMessage;
- uint32 m_gSender;
- uint32 m_gAction;
- std::string m_gBoxMessage;
- uint32 m_gBoxMoney;
-};
-
-typedef std::vector<GossipMenuItem> GossipMenuItemList;
-
-struct QuestMenuItem
-{
- uint32 m_qId;
- uint8 m_qIcon;
-};
-
-typedef std::vector<QuestMenuItem> QuestMenuItemList;
-
-class MANGOS_DLL_SPEC GossipMenu
-{
- public:
- GossipMenu();
- ~GossipMenu();
-
- void AddMenuItem(uint8 Icon, std::string Message, bool Coded = false);
- void AddMenuItem(uint8 Icon, std::string Message, uint32 dtSender, uint32 dtAction, std::string BoxMessage, uint32 BoxMoney, bool Coded = false);
-
- // for using from scripts, don't must be inlined
- void AddMenuItem(uint8 Icon, char const* Message, bool Coded = false);
- void AddMenuItem(uint8 Icon, char const* Message, uint32 dtSender, uint32 dtAction, char const* BoxMessage, uint32 BoxMoney, bool Coded = false);
-
- unsigned int MenuItemCount() const
- {
- return m_gItems.size();
- }
-
- bool Empty() const
- {
- return m_gItems.empty();
- }
-
- GossipMenuItem const& GetItem( unsigned int Id )
- {
- return m_gItems[ Id ];
- }
-
- uint32 MenuItemSender( unsigned int ItemId );
- uint32 MenuItemAction( unsigned int ItemId );
- bool MenuItemCoded( unsigned int ItemId );
-
- void ClearMenu();
-
- protected:
- GossipMenuItemList m_gItems;
-};
-
-class QuestMenu
-{
- public:
- QuestMenu();
- ~QuestMenu();
-
- void AddMenuItem( uint32 QuestId, uint8 Icon);
- void ClearMenu();
-
- uint8 MenuItemCount() const
- {
- return m_qItems.size();
- }
-
- bool Empty() const
- {
- return m_qItems.empty();
- }
-
- bool HasItem( uint32 questid );
-
- QuestMenuItem const& GetItem( uint16 Id )
- {
- return m_qItems[ Id ];
- }
-
- protected:
- QuestMenuItemList m_qItems;
-};
-
-class MANGOS_DLL_SPEC PlayerMenu
-{
- private:
- GossipMenu mGossipMenu;
- QuestMenu mQuestMenu;
- WorldSession* pSession;
-
- public:
- PlayerMenu( WorldSession *Session );
- ~PlayerMenu();
-
- GossipMenu& GetGossipMenu() { return mGossipMenu; }
- QuestMenu& GetQuestMenu() { return mQuestMenu; }
-
- bool Empty() const { return mGossipMenu.Empty() && mQuestMenu.Empty(); }
-
- void ClearMenus();
- uint32 GossipOptionSender( unsigned int Selection );
- uint32 GossipOptionAction( unsigned int Selection );
- bool GossipOptionCoded( unsigned int Selection );
-
- void SendGossipMenu( uint32 TitleTextId, uint64 npcGUID );
- void CloseGossip();
- void SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, const char * locName );
- void SendTalking( uint32 textID );
- void SendTalking( char const * title, char const * text );
-
- /*********************************************************/
- /*** QUEST SYSTEM ***/
- /*********************************************************/
- void SendQuestGiverStatus( uint8 questStatus, uint64 npcGUID );
-
- void SendQuestGiverQuestList( QEmote eEmote, std::string Title, uint64 npcGUID );
-
- void SendQuestQueryResponse ( Quest const *pQuest );
- void SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID, bool ActivateAccept);
-
- void SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, bool EnbleNext );
- void SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID, bool Completable, bool CloseOnCancel );
-};
-#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
+ */
+
+#ifndef MANGOSSERVER_GOSSIP_H
+#define MANGOSSERVER_GOSSIP_H
+
+#include "Common.h"
+#include "QuestDef.h"
+#include "NPCHandler.h"
+
+class WorldSession;
+
+#define GOSSIP_MAX_MENU_ITEMS 64 // client supported items unknown, but provided number must be enough
+#define DEFAULT_GOSSIP_MESSAGE 0xffffff
+
+//POI defines
+enum Poi_Icon
+{
+ ICON_POI_0 = 0, // Grey ?
+ ICON_POI_1 = 1, // Red ?
+ ICON_POI_2 = 2, // Blue ?
+ ICON_POI_BWTOMB = 3, // Blue and White Tomb Stone
+ ICON_POI_HOUSE = 4, // House
+ ICON_POI_TOWER = 5, // Tower
+ ICON_POI_REDFLAG = 6, // Red Flag with Yellow !
+ ICON_POI_TOMB = 7, // Tomb Stone
+ ICON_POI_BWTOWER = 8, // Blue and White Tower
+ ICON_POI_REDTOWER = 9, // Red Tower
+ ICON_POI_BLUETOWER = 10, // Blue Tower
+ ICON_POI_RWTOWER = 11, // Red and White Tower
+ ICON_POI_REDTOMB = 12, // Red Tomb Stone
+ ICON_POI_RWTOMB = 13, // Red and White Tomb Stone
+ ICON_POI_BLUETOMB = 14, // Blue Tomb Stone
+ ICON_POI_NOTHING = 15, // NOTHING
+ ICON_POI_16 = 16, // Red ?
+ ICON_POI_17 = 17, // Grey ?
+ ICON_POI_18 = 18, // Blue ?
+ ICON_POI_19 = 19, // Red and White ?
+ ICON_POI_20 = 20, // Red ?
+ ICON_POI_GREYLOGS = 21, // Grey Wood Logs
+ ICON_POI_BWLOGS = 22, // Blue and White Wood Logs
+ ICON_POI_BLUELOGS = 23, // Blue Wood Logs
+ ICON_POI_RWLOGS = 24, // Red and White Wood Logs
+ ICON_POI_REDLOGS = 25, // Red Wood Logs
+ ICON_POI_26 = 26, // Grey ?
+ ICON_POI_27 = 27, // Blue and White ?
+ ICON_POI_28 = 28, // Blue ?
+ ICON_POI_29 = 29, // Red and White ?
+ ICON_POI_30 = 30, // Red ?
+ ICON_POI_GREYHOUSE = 31, // Grey House
+ ICON_POI_BWHOUSE = 32, // Blue and White House
+ ICON_POI_BLUEHOUSE = 33, // Blue House
+ ICON_POI_RWHOUSE = 34, // Red and White House
+ ICON_POI_REDHOUSE = 35, // Red House
+ ICON_POI_GREYHORSE = 36, // Grey Horse
+ ICON_POI_BWHORSE = 37, // Blue and White Horse
+ ICON_POI_BLUEHORSE = 38, // Blue Horse
+ ICON_POI_RWHORSE = 39, // Red and White Horse
+ ICON_POI_REDHORSE = 40 // Red Horse
+};
+
+struct GossipMenuItem
+{
+ uint8 m_gIcon;
+ bool m_gCoded;
+ std::string m_gMessage;
+ uint32 m_gSender;
+ uint32 m_gAction;
+ std::string m_gBoxMessage;
+ uint32 m_gBoxMoney;
+};
+
+typedef std::vector<GossipMenuItem> GossipMenuItemList;
+
+struct QuestMenuItem
+{
+ uint32 m_qId;
+ uint8 m_qIcon;
+};
+
+typedef std::vector<QuestMenuItem> QuestMenuItemList;
+
+class MANGOS_DLL_SPEC GossipMenu
+{
+ public:
+ GossipMenu();
+ ~GossipMenu();
+
+ void AddMenuItem(uint8 Icon, std::string Message, bool Coded = false);
+ void AddMenuItem(uint8 Icon, std::string Message, uint32 dtSender, uint32 dtAction, std::string BoxMessage, uint32 BoxMoney, bool Coded = false);
+
+ // for using from scripts, don't must be inlined
+ void AddMenuItem(uint8 Icon, char const* Message, bool Coded = false);
+ void AddMenuItem(uint8 Icon, char const* Message, uint32 dtSender, uint32 dtAction, char const* BoxMessage, uint32 BoxMoney, bool Coded = false);
+
+ unsigned int MenuItemCount() const
+ {
+ return m_gItems.size();
+ }
+
+ bool Empty() const
+ {
+ return m_gItems.empty();
+ }
+
+ GossipMenuItem const& GetItem( unsigned int Id )
+ {
+ return m_gItems[ Id ];
+ }
+
+ uint32 MenuItemSender( unsigned int ItemId );
+ uint32 MenuItemAction( unsigned int ItemId );
+ bool MenuItemCoded( unsigned int ItemId );
+
+ void ClearMenu();
+
+ protected:
+ GossipMenuItemList m_gItems;
+};
+
+class QuestMenu
+{
+ public:
+ QuestMenu();
+ ~QuestMenu();
+
+ void AddMenuItem( uint32 QuestId, uint8 Icon);
+ void ClearMenu();
+
+ uint8 MenuItemCount() const
+ {
+ return m_qItems.size();
+ }
+
+ bool Empty() const
+ {
+ return m_qItems.empty();
+ }
+
+ bool HasItem( uint32 questid );
+
+ QuestMenuItem const& GetItem( uint16 Id )
+ {
+ return m_qItems[ Id ];
+ }
+
+ protected:
+ QuestMenuItemList m_qItems;
+};
+
+class MANGOS_DLL_SPEC PlayerMenu
+{
+ private:
+ GossipMenu mGossipMenu;
+ QuestMenu mQuestMenu;
+ WorldSession* pSession;
+
+ public:
+ PlayerMenu( WorldSession *Session );
+ ~PlayerMenu();
+
+ GossipMenu& GetGossipMenu() { return mGossipMenu; }
+ QuestMenu& GetQuestMenu() { return mQuestMenu; }
+
+ bool Empty() const { return mGossipMenu.Empty() && mQuestMenu.Empty(); }
+
+ void ClearMenus();
+ uint32 GossipOptionSender( unsigned int Selection );
+ uint32 GossipOptionAction( unsigned int Selection );
+ bool GossipOptionCoded( unsigned int Selection );
+
+ void SendGossipMenu( uint32 TitleTextId, uint64 npcGUID );
+ void CloseGossip();
+ void SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, const char * locName );
+ void SendTalking( uint32 textID );
+ void SendTalking( char const * title, char const * text );
+
+ /*********************************************************/
+ /*** QUEST SYSTEM ***/
+ /*********************************************************/
+ void SendQuestGiverStatus( uint8 questStatus, uint64 npcGUID );
+
+ void SendQuestGiverQuestList( QEmote eEmote, std::string Title, uint64 npcGUID );
+
+ void SendQuestQueryResponse ( Quest const *pQuest );
+ void SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID, bool ActivateAccept);
+
+ void SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, bool EnbleNext );
+ void SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID, bool Completable, bool CloseOnCancel );
+};
+#endif
diff --git a/src/game/GridNotifiers.h b/src/game/GridNotifiers.h
index 8eb69b4f6b6..dd4d8a63f81 100644
--- a/src/game/GridNotifiers.h
+++ b/src/game/GridNotifiers.h
@@ -1,941 +1,941 @@
-/*
- * 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
- */
-
-#ifndef MANGOS_GRIDNOTIFIERS_H
-#define MANGOS_GRIDNOTIFIERS_H
-
-#include "ObjectGridLoader.h"
-#include "ByteBuffer.h"
-#include "UpdateData.h"
-#include <iostream>
-
-#include "Corpse.h"
-#include "Object.h"
-#include "DynamicObject.h"
-#include "GameObject.h"
-#include "Player.h"
-#include "Unit.h"
-
-class Player;
-//class Map;
-
-namespace MaNGOS
-{
-
- struct MANGOS_DLL_DECL PlayerNotifier
- {
- explicit PlayerNotifier(Player &pl) : i_player(pl) {}
- void Visit(PlayerMapType &);
- template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
- Player &i_player;
- };
-
- struct MANGOS_DLL_DECL VisibleNotifier
- {
- Player &i_player;
- UpdateData i_data;
- UpdateDataMapType i_data_updates;
- Player::ClientGUIDs i_clientGUIDs;
- std::set<WorldObject*> i_visibleNow;
-
- explicit VisibleNotifier(Player &player) : i_player(player),i_clientGUIDs(player.m_clientGUIDs) {}
- template<class T> void Visit(GridRefManager<T> &m);
- void Visit(PlayerMapType &);
- void Notify(void);
- };
-
- struct MANGOS_DLL_DECL VisibleChangesNotifier
- {
- WorldObject &i_object;
-
- explicit VisibleChangesNotifier(WorldObject &object) : i_object(object) {}
- template<class T> void Visit(GridRefManager<T> &) {}
- void Visit(PlayerMapType &);
- };
-
- struct MANGOS_DLL_DECL GridUpdater
- {
- GridType &i_grid;
- uint32 i_timeDiff;
- GridUpdater(GridType &grid, uint32 diff) : i_grid(grid), i_timeDiff(diff) {}
-
- template<class T> void updateObjects(GridRefManager<T> &m)
- {
- for(typename GridRefManager<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
- iter->getSource()->Update(i_timeDiff);
- }
-
- void Visit(PlayerMapType &m) { updateObjects<Player>(m); }
- void Visit(CreatureMapType &m){ updateObjects<Creature>(m); }
- void Visit(GameObjectMapType &m) { updateObjects<GameObject>(m); }
- void Visit(DynamicObjectMapType &m) { updateObjects<DynamicObject>(m); }
- void Visit(CorpseMapType &m) { updateObjects<Corpse>(m); }
- };
-
- struct MANGOS_DLL_DECL MessageDeliverer
- {
- Player &i_player;
- WorldPacket *i_message;
- bool i_toSelf;
- MessageDeliverer(Player &pl, WorldPacket *msg, bool to_self) : i_player(pl), i_message(msg), i_toSelf(to_self) {}
- void Visit(PlayerMapType &m);
- template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
- };
-
- struct MANGOS_DLL_DECL ObjectMessageDeliverer
- {
- WorldPacket *i_message;
- explicit ObjectMessageDeliverer(WorldPacket *msg) : i_message(msg) {}
- void Visit(PlayerMapType &m);
- template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
- };
-
- struct MANGOS_DLL_DECL MessageDistDeliverer
- {
- Player &i_player;
- WorldPacket *i_message;
- bool i_toSelf;
- bool i_ownTeamOnly;
- float i_dist;
- MessageDistDeliverer(Player &pl, WorldPacket *msg, float dist, bool to_self, bool ownTeamOnly) : i_player(pl), i_message(msg), i_dist(dist), i_toSelf(to_self), i_ownTeamOnly(ownTeamOnly) {}
- void Visit(PlayerMapType &m);
- template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
- };
-
- struct MANGOS_DLL_DECL ObjectMessageDistDeliverer
- {
- WorldObject &i_object;
- WorldPacket *i_message;
- float i_dist;
- ObjectMessageDistDeliverer(WorldObject &obj, WorldPacket *msg, float dist) : i_object(obj), i_message(msg), i_dist(dist) {}
- void Visit(PlayerMapType &m);
- template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
- };
-
- struct MANGOS_DLL_DECL ObjectUpdater
- {
- uint32 i_timeDiff;
- explicit ObjectUpdater(const uint32 &diff) : i_timeDiff(diff) {}
- template<class T> void Visit(GridRefManager<T> &m);
- void Visit(PlayerMapType &) {}
- void Visit(CorpseMapType &) {}
- void Visit(CreatureMapType &);
- };
-
- template<class T>
- struct MANGOS_DLL_DECL ObjectAccessorNotifier
- {
- T *& i_object;
-
- uint64 i_id;
- ObjectAccessorNotifier(T * &obj, uint64 id) : i_object(obj), i_id(id)
- {
- i_object = NULL;
- }
-
- void Visit(GridRefManager<T> &m )
- {
- if( i_object == NULL )
- {
- GridRefManager<T> *iter = m.find(i_id);
- if( iter != m.end() )
- {
- assert( iter->second != NULL );
- i_object = iter->second;
- }
- }
- }
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- struct MANGOS_DLL_DECL PlayerRelocationNotifier
- {
- Player &i_player;
- PlayerRelocationNotifier(Player &pl) : i_player(pl) {}
- template<class T> void Visit(GridRefManager<T> &) {}
- void Visit(PlayerMapType &);
- void Visit(CreatureMapType &);
- };
-
- struct MANGOS_DLL_DECL CreatureRelocationNotifier
- {
- Creature &i_creature;
- CreatureRelocationNotifier(Creature &c) : i_creature(c) {}
- template<class T> void Visit(GridRefManager<T> &) {}
- #ifdef WIN32
- template<> void Visit(PlayerMapType &);
- #endif
- };
-
- struct MANGOS_DLL_DECL DynamicObjectUpdater
- {
- DynamicObject &i_dynobject;
- Unit* i_check;
- DynamicObjectUpdater(DynamicObject &dynobject, Unit* caster) : i_dynobject(dynobject)
- {
- i_check = caster;
- Unit* owner = i_check->GetOwner();
- if(owner)
- i_check = owner;
- }
-
- template<class T> inline void Visit(GridRefManager<T> &) {}
- #ifdef WIN32
- template<> inline void Visit<Player>(PlayerMapType &);
- template<> inline void Visit<Creature>(CreatureMapType &);
- #endif
-
- void VisitHelper(Unit* target);
- };
-
- // SEARCHERS & LIST SEARCHERS & WORKERS
-
- // WorldObject searchers & workers
-
- template<class Check>
- struct MANGOS_DLL_DECL WorldObjectSearcher
- {
- WorldObject* &i_object;
- Check &i_check;
-
- WorldObjectSearcher(WorldObject* & result, Check& check) : i_object(result),i_check(check) {}
-
- void Visit(GameObjectMapType &m);
- void Visit(PlayerMapType &m);
- void Visit(CreatureMapType &m);
- void Visit(CorpseMapType &m);
- void Visit(DynamicObjectMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- template<class Check>
- struct MANGOS_DLL_DECL WorldObjectListSearcher
- {
- std::list<WorldObject*> &i_objects;
- Check& i_check;
-
- WorldObjectListSearcher(std::list<WorldObject*> &objects, Check & check) : i_objects(objects),i_check(check) {}
-
- void Visit(PlayerMapType &m);
- void Visit(CreatureMapType &m);
- void Visit(CorpseMapType &m);
- void Visit(GameObjectMapType &m);
- void Visit(DynamicObjectMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- template<class Do>
- struct MANGOS_DLL_DECL WorldObjectWorker
- {
- Do const& i_do;
-
- explicit WorldObjectWorker(Do const& _do) : i_do(_do) {}
-
- void Visit(GameObjectMapType &m)
- {
- for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
- i_do(itr->getSource());
- }
-
- void Visit(PlayerMapType &m)
- {
- for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
- i_do(itr->getSource());
- }
- void Visit(CreatureMapType &m)
- {
- for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
- i_do(itr->getSource());
- }
-
- void Visit(CorpseMapType &m)
- {
- for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
- i_do(itr->getSource());
- }
-
- void Visit(DynamicObjectMapType &m)
- {
- for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
- i_do(itr->getSource());
- }
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- // Gameobject searchers
-
- template<class Check>
- struct MANGOS_DLL_DECL GameObjectSearcher
- {
- GameObject* &i_object;
- Check &i_check;
-
- GameObjectSearcher(GameObject* & result, Check& check) : i_object(result),i_check(check) {}
-
- void Visit(GameObjectMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- // Last accepted by Check GO if any (Check can change requirements at each call)
- template<class Check>
- struct MANGOS_DLL_DECL GameObjectLastSearcher
- {
- GameObject* &i_object;
- Check& i_check;
-
- GameObjectLastSearcher(GameObject* & result, Check& check) : i_object(result),i_check(check) {}
-
- void Visit(GameObjectMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- template<class Check>
- struct MANGOS_DLL_DECL GameObjectListSearcher
- {
- std::list<GameObject*> &i_objects;
- Check& i_check;
-
- GameObjectListSearcher(std::list<GameObject*> &objects, Check & check) : i_objects(objects),i_check(check) {}
-
- void Visit(GameObjectMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- // Unit searchers
-
- // First accepted by Check Unit if any
- template<class Check>
- struct MANGOS_DLL_DECL UnitSearcher
- {
- Unit* &i_object;
- Check & i_check;
-
- UnitSearcher(Unit* & result, Check & check) : i_object(result),i_check(check) {}
-
- void Visit(CreatureMapType &m);
- void Visit(PlayerMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- // Last accepted by Check Unit if any (Check can change requirements at each call)
- template<class Check>
- struct MANGOS_DLL_DECL UnitLastSearcher
- {
- Unit* &i_object;
- Check & i_check;
-
- UnitLastSearcher(Unit* & result, Check & check) : i_object(result),i_check(check) {}
-
- void Visit(CreatureMapType &m);
- void Visit(PlayerMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- // All accepted by Check units if any
- template<class Check>
- struct MANGOS_DLL_DECL UnitListSearcher
- {
- std::list<Unit*> &i_objects;
- Check& i_check;
-
- UnitListSearcher(std::list<Unit*> &objects, Check & check) : i_objects(objects),i_check(check) {}
-
- void Visit(PlayerMapType &m);
- void Visit(CreatureMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- // Creature searchers
-
- template<class Check>
- struct MANGOS_DLL_DECL CreatureSearcher
- {
- Creature* &i_object;
- Check & i_check;
-
- CreatureSearcher(Creature* & result, Check & check) : i_object(result),i_check(check) {}
-
- void Visit(CreatureMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- // Last accepted by Check Creature if any (Check can change requirements at each call)
- template<class Check>
- struct MANGOS_DLL_DECL CreatureLastSearcher
- {
- Creature* &i_object;
- Check & i_check;
-
- CreatureLastSearcher(Creature* & result, Check & check) : i_object(result),i_check(check) {}
-
- void Visit(CreatureMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- template<class Check>
- struct MANGOS_DLL_DECL CreatureListSearcher
- {
- std::list<Creature*> &i_objects;
- Check& i_check;
-
- CreatureListSearcher(std::list<Creature*> &objects, Check & check) : i_objects(objects),i_check(check) {}
-
- void Visit(CreatureMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- // Player searchers
-
- template<class Check>
- struct MANGOS_DLL_DECL PlayerSearcher
- {
- Player* &i_object;
- Check & i_check;
-
- PlayerSearcher(Player* & result, Check & check) : i_object(result),i_check(check) {}
-
- void Visit(PlayerMapType &m);
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- template<class Do>
- struct MANGOS_DLL_DECL PlayerWorker
- {
- Do& i_do;
-
- explicit PlayerWorker(Do& _do) : i_do(_do) {}
-
- void Visit(PlayerMapType &m)
- {
- for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
- i_do(itr->getSource());
- }
-
- template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
- };
-
- // CHECKS && DO classes
-
- // WorldObject check classes
- class CannibalizeObjectCheck
- {
- public:
- CannibalizeObjectCheck(Unit* funit, float range) : i_funit(funit), i_range(range) {}
- bool operator()(Player* u)
- {
- if( i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight() )
- return false;
-
- if(i_funit->IsWithinDistInMap(u, i_range) )
- return true;
-
- return false;
- }
- bool operator()(Corpse* u);
- bool operator()(Creature* u)
- {
- if( i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight() ||
- (u->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD)==0)
- return false;
-
- if(i_funit->IsWithinDistInMap(u, i_range) )
- return true;
-
- return false;
- }
- template<class NOT_INTERESTED> bool operator()(NOT_INTERESTED* u) { return false; }
- private:
- Unit* const i_funit;
- float i_range;
- };
-
- // WorldObject do classes
-
- class RespawnDo
- {
- public:
- RespawnDo() {}
- void operator()(Creature* u) const { u->Respawn(); }
- void operator()(GameObject* u) const { u->Respawn(); }
- void operator()(WorldObject*) const {}
- void operator()(Corpse*) const {}
- };
-
- // GameObject checks
-
- class GameObjectFocusCheck
- {
- public:
- GameObjectFocusCheck(Unit const* unit,uint32 focusId) : i_unit(unit), i_focusId(focusId) {}
- bool operator()(GameObject* go) const
- {
- if(go->GetGOInfo()->type != GAMEOBJECT_TYPE_SPELL_FOCUS)
- return false;
-
- if(go->GetGOInfo()->spellFocus.focusId != i_focusId)
- return false;
-
- float dist = go->GetGOInfo()->spellFocus.dist;
-
- return go->IsWithinDistInMap(i_unit, dist);
- }
- private:
- Unit const* i_unit;
- uint32 i_focusId;
- };
-
- // Find the nearest Fishing hole and return true only if source object is in range of hole
- class NearestGameObjectFishingHole
- {
- public:
- NearestGameObjectFishingHole(WorldObject const& obj, float range) : i_obj(obj), i_range(range) {}
- bool operator()(GameObject* go)
- {
- if(go->GetGOInfo()->type == GAMEOBJECT_TYPE_FISHINGHOLE && go->isSpawned() && i_obj.IsWithinDistInMap(go, i_range) && i_obj.IsWithinDistInMap(go, go->GetGOInfo()->fishinghole.radius))
- {
- i_range = i_obj.GetDistance(go);
- return true;
- }
- return false;
- }
- float GetLastRange() const { return i_range; }
- private:
- WorldObject const& i_obj;
- float i_range;
-
- // prevent clone
- NearestGameObjectFishingHole(NearestGameObjectFishingHole const&);
- };
-
- // Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest GO)
- class NearestGameObjectEntryInObjectRangeCheck
- {
- public:
- NearestGameObjectEntryInObjectRangeCheck(WorldObject const& obj,uint32 entry, float range) : i_obj(obj), i_entry(entry), i_range(range) {}
- bool operator()(GameObject* go)
- {
- if(go->GetEntry() == i_entry && i_obj.IsWithinDistInMap(go, i_range))
- {
- i_range = i_obj.GetDistance(go); // use found GO range as new range limit for next check
- return true;
- }
- return false;
- }
- float GetLastRange() const { return i_range; }
- private:
- WorldObject const& i_obj;
- uint32 i_entry;
- float i_range;
-
- // prevent clone this object
- NearestGameObjectEntryInObjectRangeCheck(NearestGameObjectEntryInObjectRangeCheck const&);
- };
-
- class GameObjectWithDbGUIDCheck
- {
- public:
- GameObjectWithDbGUIDCheck(WorldObject const& obj,uint32 db_guid) : i_obj(obj), i_db_guid(db_guid) {}
- bool operator()(GameObject const* go) const
- {
- return go->GetDBTableGUIDLow() == i_db_guid;
- }
- private:
- WorldObject const& i_obj;
- uint32 i_db_guid;
- };
-
- // Unit checks
-
- class AnyUnfriendlyUnitInObjectRangeCheck
- {
- public:
- AnyUnfriendlyUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) {}
- bool operator()(Unit* u)
- {
- if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range) && !i_funit->IsFriendlyTo(u))
- return true;
- else
- return false;
- }
- private:
- WorldObject const* i_obj;
- Unit const* i_funit;
- float i_range;
- };
-
- class AnyFriendlyUnitInObjectRangeCheck
- {
- public:
- AnyFriendlyUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) {}
- bool operator()(Unit* u)
- {
- if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range) && i_funit->IsFriendlyTo(u))
- return true;
- else
- return false;
- }
- private:
- WorldObject const* i_obj;
- Unit const* i_funit;
- float i_range;
- };
-
- class AnyUnitInObjectRangeCheck
- {
- public:
- AnyUnitInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {}
- bool operator()(Unit* u)
- {
- if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range))
- return true;
-
- return false;
- }
- private:
- WorldObject const* i_obj;
- float i_range;
- };
-
- // Success at unit in range, range update for next check (this can be use with UnitLastSearcher to find nearest unit)
- class NearestAttackableUnitInObjectRangeCheck
- {
- public:
- NearestAttackableUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) {}
- bool operator()(Unit* u)
- {
- if( u->isTargetableForAttack() && i_obj->IsWithinDistInMap(u, i_range) &&
- !i_funit->IsFriendlyTo(u) && u->isVisibleForOrDetect(i_funit,false) )
- {
- i_range = i_obj->GetDistance(u); // use found unit range as new range limit for next check
- return true;
- }
-
- return false;
- }
- private:
- WorldObject const* i_obj;
- Unit const* i_funit;
- float i_range;
-
- // prevent clone this object
- NearestAttackableUnitInObjectRangeCheck(NearestAttackableUnitInObjectRangeCheck const&);
- };
-
- class AnyAoETargetUnitInObjectRangeCheck
- {
- public:
- AnyAoETargetUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range)
- : i_obj(obj), i_funit(funit), i_range(range)
- {
- Unit const* check = i_funit;
- Unit const* owner = i_funit->GetOwner();
- if(owner)
- check = owner;
- i_targetForPlayer = ( check->GetTypeId()==TYPEID_PLAYER );
- }
- bool operator()(Unit* u)
- {
- // Check contains checks for: live, non-selectable, non-attackable flags, flight check and GM check, ignore totems
- if (!u->isTargetableForAttack())
- return false;
- if(u->GetTypeId()==TYPEID_UNIT && ((Creature*)u)->isTotem())
- return false;
-
- if(( i_targetForPlayer ? !i_funit->IsFriendlyTo(u) : i_funit->IsHostileTo(u) )&& i_obj->IsWithinDistInMap(u, i_range))
- return true;
-
- return false;
- }
- private:
- bool i_targetForPlayer;
- WorldObject const* i_obj;
- Unit const* i_funit;
- float i_range;
- };
-
- struct AnyDeadUnitCheck
- {
- bool operator()(Unit* u) { return !u->isAlive(); }
- };
-
- struct AnyStealthedCheck
- {
- bool operator()(Unit* u) { return u->GetVisibility()==VISIBILITY_GROUP_STEALTH; }
- };
-
- // Creature checks
-
- class InAttackDistanceFromAnyHostileCreatureCheck
- {
- public:
- explicit InAttackDistanceFromAnyHostileCreatureCheck(Unit* funit) : i_funit(funit) {}
- bool operator()(Creature* u)
- {
- if(u->isAlive() && u->IsHostileTo(i_funit) && i_funit->IsWithinDistInMap(u, u->GetAttackDistance(i_funit)))
- return true;
-
- return false;
- }
- private:
- Unit* const i_funit;
- };
-
- class NearestAssistCreatureInCreatureRangeCheck
- {
- public:
- NearestAssistCreatureInCreatureRangeCheck(Creature* obj,Unit* enemy, float range)
- : i_obj(obj), i_enemy(enemy), i_range(range) {}
-
- bool operator()(Creature* u)
- {
- if(u->getFaction() == i_obj->getFaction() && !u->isInCombat() && !u->GetCharmerOrOwnerGUID() && u->IsHostileTo(i_enemy) && u->isAlive()&& i_obj->IsWithinDistInMap(u, i_range) && i_obj->IsWithinLOSInMap(u))
- {
- i_range = i_obj->GetDistance(u); // use found unit range as new range limit for next check
- return true;
- }
- return false;
- }
- float GetLastRange() const { return i_range; }
- private:
- Creature* const i_obj;
- Unit* const i_enemy;
- float i_range;
-
- // prevent clone this object
- NearestAssistCreatureInCreatureRangeCheck(NearestAssistCreatureInCreatureRangeCheck const&);
- };
-
- class AnyAssistCreatureInRangeCheck
- {
- public:
- AnyAssistCreatureInRangeCheck(Unit* funit, Unit* enemy, float range)
- : i_funit(funit), i_enemy(enemy), i_range(range)
- {
- }
- bool operator()(Creature* u)
- {
- if(u == i_funit)
- return false;
-
- // we don't need help from zombies :)
- if( !u->isAlive() )
- return false;
-
- // skip fighting creature
- if( u->isInCombat() )
- return false;
-
- // only from same creature faction
- if(u->getFaction() != i_funit->getFaction() )
- return false;
-
- // only free creature
- if( u->GetCharmerOrOwnerGUID() )
- return false;
-
- // too far
- if( !i_funit->IsWithinDistInMap(u, i_range) )
- return false;
-
- // skip non hostile to caster enemy creatures
- if( !u->IsHostileTo(i_enemy) )
- return false;
-
- // only if see assisted creature
- if(!u->IsWithinLOSInMap(i_funit) )
- return false;
-
- return true;
- }
- private:
- Unit* const i_funit;
- Unit* const i_enemy;
- float i_range;
- };
-
- // Success at unit in range, range update for next check (this can be use with CreatureLastSearcher to find nearest creature)
- class NearestCreatureEntryWithLiveStateInObjectRangeCheck
- {
- public:
- NearestCreatureEntryWithLiveStateInObjectRangeCheck(WorldObject const& obj,uint32 entry, bool alive, float range)
- : i_obj(obj), i_entry(entry), i_alive(alive), i_range(range) {}
-
- bool operator()(Creature* u)
- {
- if(u->GetEntry() == i_entry && u->isAlive()==i_alive && i_obj.IsWithinDistInMap(u, i_range))
- {
- i_range = i_obj.GetDistance(u); // use found unit range as new range limit for next check
- return true;
- }
- return false;
- }
- float GetLastRange() const { return i_range; }
- private:
- WorldObject const& i_obj;
- uint32 i_entry;
- bool i_alive;
- float i_range;
-
- // prevent clone this object
- NearestCreatureEntryWithLiveStateInObjectRangeCheck(NearestCreatureEntryWithLiveStateInObjectRangeCheck const&);
- };
-
- class AnyPlayerInObjectRangeCheck
- {
- public:
- AnyPlayerInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {}
- bool operator()(Player* u)
- {
- if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range))
- return true;
-
- return false;
- }
- private:
- WorldObject const* i_obj;
- float i_range;
- };
-
- // Searchers used by ScriptedAI
- class MostHPMissingInRange
- {
- public:
- MostHPMissingInRange(Unit const* obj, float range, uint32 hp) : i_obj(obj), i_range(range), i_hp(hp) {}
- bool operator()(Unit* u)
- {
- if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && u->GetMaxHealth() - u->GetHealth() > i_hp)
- {
- i_hp = u->GetMaxHealth() - u->GetHealth();
- return true;
- }
- return false;
- }
- private:
- Unit const* i_obj;
- float i_range;
- uint32 i_hp;
- };
-
- class FriendlyCCedInRange
- {
- public:
- FriendlyCCedInRange(Unit const* obj, float range) : i_obj(obj), i_range(range) {}
- 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_STUNNED) || u->hasUnitState(UNIT_STAT_CONFUSED)))
- {
- return true;
- }
- return false;
- }
- private:
- Unit const* i_obj;
- float i_range;
- };
-
- class FriendlyMissingBuffInRange
- {
- public:
- FriendlyMissingBuffInRange(Unit const* obj, float range, uint32 spellid) : i_obj(obj), i_range(range), i_spell(spellid) {}
- bool operator()(Unit* u)
- {
- if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) &&
- !(u->HasAura(i_spell, 0) || u->HasAura(i_spell, 1) || u->HasAura(i_spell, 2)))
- {
- return true;
- }
- return false;
- }
- private:
- Unit const* i_obj;
- float i_range;
- uint32 i_spell;
- };
-
- class AllFriendlyCreaturesInGrid
- {
- public:
- AllFriendlyCreaturesInGrid(Unit const* obj) : pUnit(obj) {}
- bool operator() (Unit* u)
- {
- if(u->isAlive() && u->GetVisibility() == VISIBILITY_ON && u->IsFriendlyTo(pUnit))
- return true;
-
- return false;
- }
- private:
- Unit const* pUnit;
- };
-
- class AllGameObjectsWithEntryInGrid
- {
- public:
- AllGameObjectsWithEntryInGrid(uint32 ent) : entry(ent) {}
- bool operator() (GameObject* g)
- {
- if(g->GetEntry() == entry)
- return true;
-
- return false;
- }
- private:
- uint32 entry;
- };
-
- class AllCreaturesOfEntryInRange
- {
- public:
- AllCreaturesOfEntryInRange(Unit const* obj, uint32 ent, float ran) : pUnit(obj), entry(ent), range(ran) {}
- bool operator() (Unit* u)
- {
- if(u->GetEntry() == entry && pUnit->IsWithinDistInMap(u, range))
- return true;
-
- return false;
- }
- private:
- Unit const* pUnit;
- uint32 entry;
- float range;
- };
-
- #ifndef WIN32
- template<> void PlayerRelocationNotifier::Visit<Creature>(CreatureMapType &);
- template<> void PlayerRelocationNotifier::Visit<Player>(PlayerMapType &);
- template<> void CreatureRelocationNotifier::Visit<Player>(PlayerMapType &);
- template<> void CreatureRelocationNotifier::Visit<Creature>(CreatureMapType &);
- template<> inline void DynamicObjectUpdater::Visit<Creature>(CreatureMapType &);
- template<> inline void DynamicObjectUpdater::Visit<Player>(PlayerMapType &);
- #endif
-}
-#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
+ */
+
+#ifndef MANGOS_GRIDNOTIFIERS_H
+#define MANGOS_GRIDNOTIFIERS_H
+
+#include "ObjectGridLoader.h"
+#include "ByteBuffer.h"
+#include "UpdateData.h"
+#include <iostream>
+
+#include "Corpse.h"
+#include "Object.h"
+#include "DynamicObject.h"
+#include "GameObject.h"
+#include "Player.h"
+#include "Unit.h"
+
+class Player;
+//class Map;
+
+namespace MaNGOS
+{
+
+ struct MANGOS_DLL_DECL PlayerNotifier
+ {
+ explicit PlayerNotifier(Player &pl) : i_player(pl) {}
+ void Visit(PlayerMapType &);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ Player &i_player;
+ };
+
+ struct MANGOS_DLL_DECL VisibleNotifier
+ {
+ Player &i_player;
+ UpdateData i_data;
+ UpdateDataMapType i_data_updates;
+ Player::ClientGUIDs i_clientGUIDs;
+ std::set<WorldObject*> i_visibleNow;
+
+ explicit VisibleNotifier(Player &player) : i_player(player),i_clientGUIDs(player.m_clientGUIDs) {}
+ template<class T> void Visit(GridRefManager<T> &m);
+ void Visit(PlayerMapType &);
+ void Notify(void);
+ };
+
+ struct MANGOS_DLL_DECL VisibleChangesNotifier
+ {
+ WorldObject &i_object;
+
+ explicit VisibleChangesNotifier(WorldObject &object) : i_object(object) {}
+ template<class T> void Visit(GridRefManager<T> &) {}
+ void Visit(PlayerMapType &);
+ };
+
+ struct MANGOS_DLL_DECL GridUpdater
+ {
+ GridType &i_grid;
+ uint32 i_timeDiff;
+ GridUpdater(GridType &grid, uint32 diff) : i_grid(grid), i_timeDiff(diff) {}
+
+ template<class T> void updateObjects(GridRefManager<T> &m)
+ {
+ for(typename GridRefManager<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
+ iter->getSource()->Update(i_timeDiff);
+ }
+
+ void Visit(PlayerMapType &m) { updateObjects<Player>(m); }
+ void Visit(CreatureMapType &m){ updateObjects<Creature>(m); }
+ void Visit(GameObjectMapType &m) { updateObjects<GameObject>(m); }
+ void Visit(DynamicObjectMapType &m) { updateObjects<DynamicObject>(m); }
+ void Visit(CorpseMapType &m) { updateObjects<Corpse>(m); }
+ };
+
+ struct MANGOS_DLL_DECL MessageDeliverer
+ {
+ Player &i_player;
+ WorldPacket *i_message;
+ bool i_toSelf;
+ MessageDeliverer(Player &pl, WorldPacket *msg, bool to_self) : i_player(pl), i_message(msg), i_toSelf(to_self) {}
+ void Visit(PlayerMapType &m);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ struct MANGOS_DLL_DECL ObjectMessageDeliverer
+ {
+ WorldPacket *i_message;
+ explicit ObjectMessageDeliverer(WorldPacket *msg) : i_message(msg) {}
+ void Visit(PlayerMapType &m);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ struct MANGOS_DLL_DECL MessageDistDeliverer
+ {
+ Player &i_player;
+ WorldPacket *i_message;
+ bool i_toSelf;
+ bool i_ownTeamOnly;
+ float i_dist;
+ MessageDistDeliverer(Player &pl, WorldPacket *msg, float dist, bool to_self, bool ownTeamOnly) : i_player(pl), i_message(msg), i_dist(dist), i_toSelf(to_self), i_ownTeamOnly(ownTeamOnly) {}
+ void Visit(PlayerMapType &m);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ struct MANGOS_DLL_DECL ObjectMessageDistDeliverer
+ {
+ WorldObject &i_object;
+ WorldPacket *i_message;
+ float i_dist;
+ ObjectMessageDistDeliverer(WorldObject &obj, WorldPacket *msg, float dist) : i_object(obj), i_message(msg), i_dist(dist) {}
+ void Visit(PlayerMapType &m);
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ struct MANGOS_DLL_DECL ObjectUpdater
+ {
+ uint32 i_timeDiff;
+ explicit ObjectUpdater(const uint32 &diff) : i_timeDiff(diff) {}
+ template<class T> void Visit(GridRefManager<T> &m);
+ void Visit(PlayerMapType &) {}
+ void Visit(CorpseMapType &) {}
+ void Visit(CreatureMapType &);
+ };
+
+ template<class T>
+ struct MANGOS_DLL_DECL ObjectAccessorNotifier
+ {
+ T *& i_object;
+
+ uint64 i_id;
+ ObjectAccessorNotifier(T * &obj, uint64 id) : i_object(obj), i_id(id)
+ {
+ i_object = NULL;
+ }
+
+ void Visit(GridRefManager<T> &m )
+ {
+ if( i_object == NULL )
+ {
+ GridRefManager<T> *iter = m.find(i_id);
+ if( iter != m.end() )
+ {
+ assert( iter->second != NULL );
+ i_object = iter->second;
+ }
+ }
+ }
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ struct MANGOS_DLL_DECL PlayerRelocationNotifier
+ {
+ Player &i_player;
+ PlayerRelocationNotifier(Player &pl) : i_player(pl) {}
+ template<class T> void Visit(GridRefManager<T> &) {}
+ void Visit(PlayerMapType &);
+ void Visit(CreatureMapType &);
+ };
+
+ struct MANGOS_DLL_DECL CreatureRelocationNotifier
+ {
+ Creature &i_creature;
+ CreatureRelocationNotifier(Creature &c) : i_creature(c) {}
+ template<class T> void Visit(GridRefManager<T> &) {}
+ #ifdef WIN32
+ template<> void Visit(PlayerMapType &);
+ #endif
+ };
+
+ struct MANGOS_DLL_DECL DynamicObjectUpdater
+ {
+ DynamicObject &i_dynobject;
+ Unit* i_check;
+ DynamicObjectUpdater(DynamicObject &dynobject, Unit* caster) : i_dynobject(dynobject)
+ {
+ i_check = caster;
+ Unit* owner = i_check->GetOwner();
+ if(owner)
+ i_check = owner;
+ }
+
+ template<class T> inline void Visit(GridRefManager<T> &) {}
+ #ifdef WIN32
+ template<> inline void Visit<Player>(PlayerMapType &);
+ template<> inline void Visit<Creature>(CreatureMapType &);
+ #endif
+
+ void VisitHelper(Unit* target);
+ };
+
+ // SEARCHERS & LIST SEARCHERS & WORKERS
+
+ // WorldObject searchers & workers
+
+ template<class Check>
+ struct MANGOS_DLL_DECL WorldObjectSearcher
+ {
+ WorldObject* &i_object;
+ Check &i_check;
+
+ WorldObjectSearcher(WorldObject* & result, Check& check) : i_object(result),i_check(check) {}
+
+ void Visit(GameObjectMapType &m);
+ void Visit(PlayerMapType &m);
+ void Visit(CreatureMapType &m);
+ void Visit(CorpseMapType &m);
+ void Visit(DynamicObjectMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ template<class Check>
+ struct MANGOS_DLL_DECL WorldObjectListSearcher
+ {
+ std::list<WorldObject*> &i_objects;
+ Check& i_check;
+
+ WorldObjectListSearcher(std::list<WorldObject*> &objects, Check & check) : i_objects(objects),i_check(check) {}
+
+ void Visit(PlayerMapType &m);
+ void Visit(CreatureMapType &m);
+ void Visit(CorpseMapType &m);
+ void Visit(GameObjectMapType &m);
+ void Visit(DynamicObjectMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ template<class Do>
+ struct MANGOS_DLL_DECL WorldObjectWorker
+ {
+ Do const& i_do;
+
+ explicit WorldObjectWorker(Do const& _do) : i_do(_do) {}
+
+ void Visit(GameObjectMapType &m)
+ {
+ for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+
+ void Visit(PlayerMapType &m)
+ {
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+ void Visit(CreatureMapType &m)
+ {
+ for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+
+ void Visit(CorpseMapType &m)
+ {
+ for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+
+ void Visit(DynamicObjectMapType &m)
+ {
+ for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Gameobject searchers
+
+ template<class Check>
+ struct MANGOS_DLL_DECL GameObjectSearcher
+ {
+ GameObject* &i_object;
+ Check &i_check;
+
+ GameObjectSearcher(GameObject* & result, Check& check) : i_object(result),i_check(check) {}
+
+ void Visit(GameObjectMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Last accepted by Check GO if any (Check can change requirements at each call)
+ template<class Check>
+ struct MANGOS_DLL_DECL GameObjectLastSearcher
+ {
+ GameObject* &i_object;
+ Check& i_check;
+
+ GameObjectLastSearcher(GameObject* & result, Check& check) : i_object(result),i_check(check) {}
+
+ void Visit(GameObjectMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ template<class Check>
+ struct MANGOS_DLL_DECL GameObjectListSearcher
+ {
+ std::list<GameObject*> &i_objects;
+ Check& i_check;
+
+ GameObjectListSearcher(std::list<GameObject*> &objects, Check & check) : i_objects(objects),i_check(check) {}
+
+ void Visit(GameObjectMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Unit searchers
+
+ // First accepted by Check Unit if any
+ template<class Check>
+ struct MANGOS_DLL_DECL UnitSearcher
+ {
+ Unit* &i_object;
+ Check & i_check;
+
+ UnitSearcher(Unit* & result, Check & check) : i_object(result),i_check(check) {}
+
+ void Visit(CreatureMapType &m);
+ void Visit(PlayerMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Last accepted by Check Unit if any (Check can change requirements at each call)
+ template<class Check>
+ struct MANGOS_DLL_DECL UnitLastSearcher
+ {
+ Unit* &i_object;
+ Check & i_check;
+
+ UnitLastSearcher(Unit* & result, Check & check) : i_object(result),i_check(check) {}
+
+ void Visit(CreatureMapType &m);
+ void Visit(PlayerMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // All accepted by Check units if any
+ template<class Check>
+ struct MANGOS_DLL_DECL UnitListSearcher
+ {
+ std::list<Unit*> &i_objects;
+ Check& i_check;
+
+ UnitListSearcher(std::list<Unit*> &objects, Check & check) : i_objects(objects),i_check(check) {}
+
+ void Visit(PlayerMapType &m);
+ void Visit(CreatureMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Creature searchers
+
+ template<class Check>
+ struct MANGOS_DLL_DECL CreatureSearcher
+ {
+ Creature* &i_object;
+ Check & i_check;
+
+ CreatureSearcher(Creature* & result, Check & check) : i_object(result),i_check(check) {}
+
+ void Visit(CreatureMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Last accepted by Check Creature if any (Check can change requirements at each call)
+ template<class Check>
+ struct MANGOS_DLL_DECL CreatureLastSearcher
+ {
+ Creature* &i_object;
+ Check & i_check;
+
+ CreatureLastSearcher(Creature* & result, Check & check) : i_object(result),i_check(check) {}
+
+ void Visit(CreatureMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ template<class Check>
+ struct MANGOS_DLL_DECL CreatureListSearcher
+ {
+ std::list<Creature*> &i_objects;
+ Check& i_check;
+
+ CreatureListSearcher(std::list<Creature*> &objects, Check & check) : i_objects(objects),i_check(check) {}
+
+ void Visit(CreatureMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // Player searchers
+
+ template<class Check>
+ struct MANGOS_DLL_DECL PlayerSearcher
+ {
+ Player* &i_object;
+ Check & i_check;
+
+ PlayerSearcher(Player* & result, Check & check) : i_object(result),i_check(check) {}
+
+ void Visit(PlayerMapType &m);
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ template<class Do>
+ struct MANGOS_DLL_DECL PlayerWorker
+ {
+ Do& i_do;
+
+ explicit PlayerWorker(Do& _do) : i_do(_do) {}
+
+ void Visit(PlayerMapType &m)
+ {
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ i_do(itr->getSource());
+ }
+
+ template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
+ };
+
+ // CHECKS && DO classes
+
+ // WorldObject check classes
+ class CannibalizeObjectCheck
+ {
+ public:
+ CannibalizeObjectCheck(Unit* funit, float range) : i_funit(funit), i_range(range) {}
+ bool operator()(Player* u)
+ {
+ if( i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight() )
+ return false;
+
+ if(i_funit->IsWithinDistInMap(u, i_range) )
+ return true;
+
+ return false;
+ }
+ bool operator()(Corpse* u);
+ bool operator()(Creature* u)
+ {
+ if( i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight() ||
+ (u->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD)==0)
+ return false;
+
+ if(i_funit->IsWithinDistInMap(u, i_range) )
+ return true;
+
+ return false;
+ }
+ template<class NOT_INTERESTED> bool operator()(NOT_INTERESTED* u) { return false; }
+ private:
+ Unit* const i_funit;
+ float i_range;
+ };
+
+ // WorldObject do classes
+
+ class RespawnDo
+ {
+ public:
+ RespawnDo() {}
+ void operator()(Creature* u) const { u->Respawn(); }
+ void operator()(GameObject* u) const { u->Respawn(); }
+ void operator()(WorldObject*) const {}
+ void operator()(Corpse*) const {}
+ };
+
+ // GameObject checks
+
+ class GameObjectFocusCheck
+ {
+ public:
+ GameObjectFocusCheck(Unit const* unit,uint32 focusId) : i_unit(unit), i_focusId(focusId) {}
+ bool operator()(GameObject* go) const
+ {
+ if(go->GetGOInfo()->type != GAMEOBJECT_TYPE_SPELL_FOCUS)
+ return false;
+
+ if(go->GetGOInfo()->spellFocus.focusId != i_focusId)
+ return false;
+
+ float dist = go->GetGOInfo()->spellFocus.dist;
+
+ return go->IsWithinDistInMap(i_unit, dist);
+ }
+ private:
+ Unit const* i_unit;
+ uint32 i_focusId;
+ };
+
+ // Find the nearest Fishing hole and return true only if source object is in range of hole
+ class NearestGameObjectFishingHole
+ {
+ public:
+ NearestGameObjectFishingHole(WorldObject const& obj, float range) : i_obj(obj), i_range(range) {}
+ bool operator()(GameObject* go)
+ {
+ if(go->GetGOInfo()->type == GAMEOBJECT_TYPE_FISHINGHOLE && go->isSpawned() && i_obj.IsWithinDistInMap(go, i_range) && i_obj.IsWithinDistInMap(go, go->GetGOInfo()->fishinghole.radius))
+ {
+ i_range = i_obj.GetDistance(go);
+ return true;
+ }
+ return false;
+ }
+ float GetLastRange() const { return i_range; }
+ private:
+ WorldObject const& i_obj;
+ float i_range;
+
+ // prevent clone
+ NearestGameObjectFishingHole(NearestGameObjectFishingHole const&);
+ };
+
+ // Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest GO)
+ class NearestGameObjectEntryInObjectRangeCheck
+ {
+ public:
+ NearestGameObjectEntryInObjectRangeCheck(WorldObject const& obj,uint32 entry, float range) : i_obj(obj), i_entry(entry), i_range(range) {}
+ bool operator()(GameObject* go)
+ {
+ if(go->GetEntry() == i_entry && i_obj.IsWithinDistInMap(go, i_range))
+ {
+ i_range = i_obj.GetDistance(go); // use found GO range as new range limit for next check
+ return true;
+ }
+ return false;
+ }
+ float GetLastRange() const { return i_range; }
+ private:
+ WorldObject const& i_obj;
+ uint32 i_entry;
+ float i_range;
+
+ // prevent clone this object
+ NearestGameObjectEntryInObjectRangeCheck(NearestGameObjectEntryInObjectRangeCheck const&);
+ };
+
+ class GameObjectWithDbGUIDCheck
+ {
+ public:
+ GameObjectWithDbGUIDCheck(WorldObject const& obj,uint32 db_guid) : i_obj(obj), i_db_guid(db_guid) {}
+ bool operator()(GameObject const* go) const
+ {
+ return go->GetDBTableGUIDLow() == i_db_guid;
+ }
+ private:
+ WorldObject const& i_obj;
+ uint32 i_db_guid;
+ };
+
+ // Unit checks
+
+ class AnyUnfriendlyUnitInObjectRangeCheck
+ {
+ public:
+ AnyUnfriendlyUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range) && !i_funit->IsFriendlyTo(u))
+ return true;
+ else
+ return false;
+ }
+ private:
+ WorldObject const* i_obj;
+ Unit const* i_funit;
+ float i_range;
+ };
+
+ class AnyFriendlyUnitInObjectRangeCheck
+ {
+ public:
+ AnyFriendlyUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range) && i_funit->IsFriendlyTo(u))
+ return true;
+ else
+ return false;
+ }
+ private:
+ WorldObject const* i_obj;
+ Unit const* i_funit;
+ float i_range;
+ };
+
+ class AnyUnitInObjectRangeCheck
+ {
+ public:
+ AnyUnitInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range))
+ return true;
+
+ return false;
+ }
+ private:
+ WorldObject const* i_obj;
+ float i_range;
+ };
+
+ // Success at unit in range, range update for next check (this can be use with UnitLastSearcher to find nearest unit)
+ class NearestAttackableUnitInObjectRangeCheck
+ {
+ public:
+ NearestAttackableUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range) : i_obj(obj), i_funit(funit), i_range(range) {}
+ bool operator()(Unit* u)
+ {
+ if( u->isTargetableForAttack() && i_obj->IsWithinDistInMap(u, i_range) &&
+ !i_funit->IsFriendlyTo(u) && u->isVisibleForOrDetect(i_funit,false) )
+ {
+ i_range = i_obj->GetDistance(u); // use found unit range as new range limit for next check
+ return true;
+ }
+
+ return false;
+ }
+ private:
+ WorldObject const* i_obj;
+ Unit const* i_funit;
+ float i_range;
+
+ // prevent clone this object
+ NearestAttackableUnitInObjectRangeCheck(NearestAttackableUnitInObjectRangeCheck const&);
+ };
+
+ class AnyAoETargetUnitInObjectRangeCheck
+ {
+ public:
+ AnyAoETargetUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range)
+ : i_obj(obj), i_funit(funit), i_range(range)
+ {
+ Unit const* check = i_funit;
+ Unit const* owner = i_funit->GetOwner();
+ if(owner)
+ check = owner;
+ i_targetForPlayer = ( check->GetTypeId()==TYPEID_PLAYER );
+ }
+ bool operator()(Unit* u)
+ {
+ // Check contains checks for: live, non-selectable, non-attackable flags, flight check and GM check, ignore totems
+ if (!u->isTargetableForAttack())
+ return false;
+ if(u->GetTypeId()==TYPEID_UNIT && ((Creature*)u)->isTotem())
+ return false;
+
+ if(( i_targetForPlayer ? !i_funit->IsFriendlyTo(u) : i_funit->IsHostileTo(u) )&& i_obj->IsWithinDistInMap(u, i_range))
+ return true;
+
+ return false;
+ }
+ private:
+ bool i_targetForPlayer;
+ WorldObject const* i_obj;
+ Unit const* i_funit;
+ float i_range;
+ };
+
+ struct AnyDeadUnitCheck
+ {
+ bool operator()(Unit* u) { return !u->isAlive(); }
+ };
+
+ struct AnyStealthedCheck
+ {
+ bool operator()(Unit* u) { return u->GetVisibility()==VISIBILITY_GROUP_STEALTH; }
+ };
+
+ // Creature checks
+
+ class InAttackDistanceFromAnyHostileCreatureCheck
+ {
+ public:
+ explicit InAttackDistanceFromAnyHostileCreatureCheck(Unit* funit) : i_funit(funit) {}
+ bool operator()(Creature* u)
+ {
+ if(u->isAlive() && u->IsHostileTo(i_funit) && i_funit->IsWithinDistInMap(u, u->GetAttackDistance(i_funit)))
+ return true;
+
+ return false;
+ }
+ private:
+ Unit* const i_funit;
+ };
+
+ class NearestAssistCreatureInCreatureRangeCheck
+ {
+ public:
+ NearestAssistCreatureInCreatureRangeCheck(Creature* obj,Unit* enemy, float range)
+ : i_obj(obj), i_enemy(enemy), i_range(range) {}
+
+ bool operator()(Creature* u)
+ {
+ if(u->getFaction() == i_obj->getFaction() && !u->isInCombat() && !u->GetCharmerOrOwnerGUID() && u->IsHostileTo(i_enemy) && u->isAlive()&& i_obj->IsWithinDistInMap(u, i_range) && i_obj->IsWithinLOSInMap(u))
+ {
+ i_range = i_obj->GetDistance(u); // use found unit range as new range limit for next check
+ return true;
+ }
+ return false;
+ }
+ float GetLastRange() const { return i_range; }
+ private:
+ Creature* const i_obj;
+ Unit* const i_enemy;
+ float i_range;
+
+ // prevent clone this object
+ NearestAssistCreatureInCreatureRangeCheck(NearestAssistCreatureInCreatureRangeCheck const&);
+ };
+
+ class AnyAssistCreatureInRangeCheck
+ {
+ public:
+ AnyAssistCreatureInRangeCheck(Unit* funit, Unit* enemy, float range)
+ : i_funit(funit), i_enemy(enemy), i_range(range)
+ {
+ }
+ bool operator()(Creature* u)
+ {
+ if(u == i_funit)
+ return false;
+
+ // we don't need help from zombies :)
+ if( !u->isAlive() )
+ return false;
+
+ // skip fighting creature
+ if( u->isInCombat() )
+ return false;
+
+ // only from same creature faction
+ if(u->getFaction() != i_funit->getFaction() )
+ return false;
+
+ // only free creature
+ if( u->GetCharmerOrOwnerGUID() )
+ return false;
+
+ // too far
+ if( !i_funit->IsWithinDistInMap(u, i_range) )
+ return false;
+
+ // skip non hostile to caster enemy creatures
+ if( !u->IsHostileTo(i_enemy) )
+ return false;
+
+ // only if see assisted creature
+ if(!u->IsWithinLOSInMap(i_funit) )
+ return false;
+
+ return true;
+ }
+ private:
+ Unit* const i_funit;
+ Unit* const i_enemy;
+ float i_range;
+ };
+
+ // Success at unit in range, range update for next check (this can be use with CreatureLastSearcher to find nearest creature)
+ class NearestCreatureEntryWithLiveStateInObjectRangeCheck
+ {
+ public:
+ NearestCreatureEntryWithLiveStateInObjectRangeCheck(WorldObject const& obj,uint32 entry, bool alive, float range)
+ : i_obj(obj), i_entry(entry), i_alive(alive), i_range(range) {}
+
+ bool operator()(Creature* u)
+ {
+ if(u->GetEntry() == i_entry && u->isAlive()==i_alive && i_obj.IsWithinDistInMap(u, i_range))
+ {
+ i_range = i_obj.GetDistance(u); // use found unit range as new range limit for next check
+ return true;
+ }
+ return false;
+ }
+ float GetLastRange() const { return i_range; }
+ private:
+ WorldObject const& i_obj;
+ uint32 i_entry;
+ bool i_alive;
+ float i_range;
+
+ // prevent clone this object
+ NearestCreatureEntryWithLiveStateInObjectRangeCheck(NearestCreatureEntryWithLiveStateInObjectRangeCheck const&);
+ };
+
+ class AnyPlayerInObjectRangeCheck
+ {
+ public:
+ AnyPlayerInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {}
+ bool operator()(Player* u)
+ {
+ if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range))
+ return true;
+
+ return false;
+ }
+ private:
+ WorldObject const* i_obj;
+ float i_range;
+ };
+
+ // Searchers used by ScriptedAI
+ class MostHPMissingInRange
+ {
+ public:
+ MostHPMissingInRange(Unit const* obj, float range, uint32 hp) : i_obj(obj), i_range(range), i_hp(hp) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) && u->GetMaxHealth() - u->GetHealth() > i_hp)
+ {
+ i_hp = u->GetMaxHealth() - u->GetHealth();
+ return true;
+ }
+ return false;
+ }
+ private:
+ Unit const* i_obj;
+ float i_range;
+ uint32 i_hp;
+ };
+
+ class FriendlyCCedInRange
+ {
+ public:
+ FriendlyCCedInRange(Unit const* obj, float range) : i_obj(obj), i_range(range) {}
+ 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_STUNNED) || u->hasUnitState(UNIT_STAT_CONFUSED)))
+ {
+ return true;
+ }
+ return false;
+ }
+ private:
+ Unit const* i_obj;
+ float i_range;
+ };
+
+ class FriendlyMissingBuffInRange
+ {
+ public:
+ FriendlyMissingBuffInRange(Unit const* obj, float range, uint32 spellid) : i_obj(obj), i_range(range), i_spell(spellid) {}
+ bool operator()(Unit* u)
+ {
+ if(u->isAlive() && u->isInCombat() && !i_obj->IsHostileTo(u) && i_obj->IsWithinDistInMap(u, i_range) &&
+ !(u->HasAura(i_spell, 0) || u->HasAura(i_spell, 1) || u->HasAura(i_spell, 2)))
+ {
+ return true;
+ }
+ return false;
+ }
+ private:
+ Unit const* i_obj;
+ float i_range;
+ uint32 i_spell;
+ };
+
+ class AllFriendlyCreaturesInGrid
+ {
+ public:
+ AllFriendlyCreaturesInGrid(Unit const* obj) : pUnit(obj) {}
+ bool operator() (Unit* u)
+ {
+ if(u->isAlive() && u->GetVisibility() == VISIBILITY_ON && u->IsFriendlyTo(pUnit))
+ return true;
+
+ return false;
+ }
+ private:
+ Unit const* pUnit;
+ };
+
+ class AllGameObjectsWithEntryInGrid
+ {
+ public:
+ AllGameObjectsWithEntryInGrid(uint32 ent) : entry(ent) {}
+ bool operator() (GameObject* g)
+ {
+ if(g->GetEntry() == entry)
+ return true;
+
+ return false;
+ }
+ private:
+ uint32 entry;
+ };
+
+ class AllCreaturesOfEntryInRange
+ {
+ public:
+ AllCreaturesOfEntryInRange(Unit const* obj, uint32 ent, float ran) : pUnit(obj), entry(ent), range(ran) {}
+ bool operator() (Unit* u)
+ {
+ if(u->GetEntry() == entry && pUnit->IsWithinDistInMap(u, range))
+ return true;
+
+ return false;
+ }
+ private:
+ Unit const* pUnit;
+ uint32 entry;
+ float range;
+ };
+
+ #ifndef WIN32
+ template<> void PlayerRelocationNotifier::Visit<Creature>(CreatureMapType &);
+ template<> void PlayerRelocationNotifier::Visit<Player>(PlayerMapType &);
+ template<> void CreatureRelocationNotifier::Visit<Player>(PlayerMapType &);
+ template<> void CreatureRelocationNotifier::Visit<Creature>(CreatureMapType &);
+ template<> inline void DynamicObjectUpdater::Visit<Creature>(CreatureMapType &);
+ template<> inline void DynamicObjectUpdater::Visit<Player>(PlayerMapType &);
+ #endif
+}
+#endif
diff --git a/src/game/Group.cpp b/src/game/Group.cpp
index 273edc135b9..d98a45ff4be 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, 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;
- }
-}
+/*
+ * 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/GuardAI.cpp b/src/game/GuardAI.cpp
index 32598e8a3d3..be5616205a2 100644
--- a/src/game/GuardAI.cpp
+++ b/src/game/GuardAI.cpp
@@ -1,154 +1,154 @@
-/*
- * 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 "GuardAI.h"
-#include "Errors.h"
-#include "Creature.h"
-#include "Player.h"
-#include "ObjectAccessor.h"
-#include "World.h"
-
-int GuardAI::Permissible(const Creature *creature)
-{
- if( creature->isGuard())
- return PERMIT_BASE_SPECIAL;
-
- return PERMIT_BASE_NO;
-}
-
-GuardAI::GuardAI(Creature &c) : i_creature(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
-{
-}
-
-void GuardAI::MoveInLineOfSight(Unit *u)
-{
- // Ignore Z for flying creatures
- if ( !i_creature.canFly() && i_creature.GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE )
- return;
-
- if( !i_creature.getVictim() && u->isTargetableForAttack() &&
- ( u->IsHostileToPlayers() || i_creature.IsHostileTo(u) /*|| u->getVictim() && i_creature.IsFriendlyTo(u->getVictim())*/ ) &&
- u->isInAccessablePlaceFor(&i_creature))
- {
- float attackRadius = i_creature.GetAttackDistance(u);
- if(i_creature.IsWithinDistInMap(u,attackRadius))
- {
- //Need add code to let guard support player
- AttackStart(u);
- u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
- }
- }
-}
-
-void GuardAI::EnterEvadeMode()
-{
- if( !i_creature.isAlive() )
- {
- DEBUG_LOG("Creature stopped attacking because he's dead [guid=%u]", i_creature.GetGUIDLow());
- i_creature.StopMoving();
- i_creature.GetMotionMaster()->MoveIdle();
-
- i_state = STATE_NORMAL;
-
- i_victimGuid = 0;
- i_creature.CombatStop();
- i_creature.DeleteThreatList();
- return;
- }
-
- Unit* victim = ObjectAccessor::GetUnit(i_creature, i_victimGuid );
-
- if( !victim )
- {
- DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow());
- }
- else if( !victim ->isAlive() )
- {
- DEBUG_LOG("Creature stopped attacking because victim is dead [guid=%u]", i_creature.GetGUIDLow());
- }
- else if( victim ->HasStealthAura() )
- {
- DEBUG_LOG("Creature stopped attacking because victim is using stealth [guid=%u]", i_creature.GetGUIDLow());
- }
- else if( victim ->isInFlight() )
- {
- DEBUG_LOG("Creature stopped attacking because victim is flying away [guid=%u]", i_creature.GetGUIDLow());
- }
- else
- {
- DEBUG_LOG("Creature stopped attacking because victim outran him [guid=%u]", i_creature.GetGUIDLow());
- }
-
- i_creature.RemoveAllAuras();
- i_creature.DeleteThreatList();
- i_victimGuid = 0;
- i_creature.CombatStop();
- i_state = STATE_NORMAL;
-
- // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
- if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE )
- i_creature.GetMotionMaster()->MoveTargetedHome();
-}
-
-void GuardAI::UpdateAI(const uint32 /*diff*/)
-{
- // update i_victimGuid if i_creature.getVictim() !=0 and changed
- if(!i_creature.SelectHostilTarget() || !i_creature.getVictim())
- return;
-
- i_victimGuid = i_creature.getVictim()->GetGUID();
-
- if( i_creature.isAttackReady() )
- {
- if( i_creature.IsWithinDistInMap(i_creature.getVictim(), ATTACK_DISTANCE))
- {
- i_creature.AttackerStateUpdate(i_creature.getVictim());
- i_creature.resetAttackTimer();
- }
- }
-}
-
-bool GuardAI::IsVisible(Unit *pl) const
-{
- return i_creature.GetDistance(pl) < sWorld.getConfig(CONFIG_SIGHT_GUARDER)
- && pl->isVisibleForOrDetect(&i_creature,true);
-}
-
-void GuardAI::AttackStart(Unit *u)
-{
- if( !u )
- return;
-
- // DEBUG_LOG("Creature %s tagged a victim to kill [guid=%u]", i_creature.GetName(), u->GetGUIDLow());
- if(i_creature.Attack(u,true))
- {
- i_creature.SetInCombatWith(u);
- u->SetInCombatWith(&i_creature);
-
- i_creature.AddThreat(u, 0.0f);
- i_victimGuid = u->GetGUID();
- i_creature.GetMotionMaster()->MoveChase(u);
- }
-}
-
-void GuardAI::JustDied(Unit *killer)
-{
- if(Player* pkiller = killer->GetCharmerOrOwnerPlayerOrPlayerItself())
- i_creature.SendZoneUnderAttackMessage(pkiller);
-}
-
+/*
+ * 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 "GuardAI.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "Player.h"
+#include "ObjectAccessor.h"
+#include "World.h"
+
+int GuardAI::Permissible(const Creature *creature)
+{
+ if( creature->isGuard())
+ return PERMIT_BASE_SPECIAL;
+
+ return PERMIT_BASE_NO;
+}
+
+GuardAI::GuardAI(Creature &c) : i_creature(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
+{
+}
+
+void GuardAI::MoveInLineOfSight(Unit *u)
+{
+ // Ignore Z for flying creatures
+ if ( !i_creature.canFly() && i_creature.GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE )
+ return;
+
+ if( !i_creature.getVictim() && u->isTargetableForAttack() &&
+ ( u->IsHostileToPlayers() || i_creature.IsHostileTo(u) /*|| u->getVictim() && i_creature.IsFriendlyTo(u->getVictim())*/ ) &&
+ u->isInAccessablePlaceFor(&i_creature))
+ {
+ float attackRadius = i_creature.GetAttackDistance(u);
+ if(i_creature.IsWithinDistInMap(u,attackRadius))
+ {
+ //Need add code to let guard support player
+ AttackStart(u);
+ u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ }
+ }
+}
+
+void GuardAI::EnterEvadeMode()
+{
+ if( !i_creature.isAlive() )
+ {
+ DEBUG_LOG("Creature stopped attacking because he's dead [guid=%u]", i_creature.GetGUIDLow());
+ i_creature.StopMoving();
+ i_creature.GetMotionMaster()->MoveIdle();
+
+ i_state = STATE_NORMAL;
+
+ i_victimGuid = 0;
+ i_creature.CombatStop();
+ i_creature.DeleteThreatList();
+ return;
+ }
+
+ Unit* victim = ObjectAccessor::GetUnit(i_creature, i_victimGuid );
+
+ if( !victim )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( !victim ->isAlive() )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is dead [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( victim ->HasStealthAura() )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is using stealth [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else if( victim ->isInFlight() )
+ {
+ DEBUG_LOG("Creature stopped attacking because victim is flying away [guid=%u]", i_creature.GetGUIDLow());
+ }
+ else
+ {
+ DEBUG_LOG("Creature stopped attacking because victim outran him [guid=%u]", i_creature.GetGUIDLow());
+ }
+
+ i_creature.RemoveAllAuras();
+ i_creature.DeleteThreatList();
+ i_victimGuid = 0;
+ i_creature.CombatStop();
+ i_state = STATE_NORMAL;
+
+ // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
+ if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE )
+ i_creature.GetMotionMaster()->MoveTargetedHome();
+}
+
+void GuardAI::UpdateAI(const uint32 /*diff*/)
+{
+ // update i_victimGuid if i_creature.getVictim() !=0 and changed
+ if(!i_creature.SelectHostilTarget() || !i_creature.getVictim())
+ return;
+
+ i_victimGuid = i_creature.getVictim()->GetGUID();
+
+ if( i_creature.isAttackReady() )
+ {
+ if( i_creature.IsWithinDistInMap(i_creature.getVictim(), ATTACK_DISTANCE))
+ {
+ i_creature.AttackerStateUpdate(i_creature.getVictim());
+ i_creature.resetAttackTimer();
+ }
+ }
+}
+
+bool GuardAI::IsVisible(Unit *pl) const
+{
+ return i_creature.GetDistance(pl) < sWorld.getConfig(CONFIG_SIGHT_GUARDER)
+ && pl->isVisibleForOrDetect(&i_creature,true);
+}
+
+void GuardAI::AttackStart(Unit *u)
+{
+ if( !u )
+ return;
+
+ // DEBUG_LOG("Creature %s tagged a victim to kill [guid=%u]", i_creature.GetName(), u->GetGUIDLow());
+ if(i_creature.Attack(u,true))
+ {
+ i_creature.SetInCombatWith(u);
+ u->SetInCombatWith(&i_creature);
+
+ i_creature.AddThreat(u, 0.0f);
+ i_victimGuid = u->GetGUID();
+ i_creature.GetMotionMaster()->MoveChase(u);
+ }
+}
+
+void GuardAI::JustDied(Unit *killer)
+{
+ if(Player* pkiller = killer->GetCharmerOrOwnerPlayerOrPlayerItself())
+ i_creature.SendZoneUnderAttackMessage(pkiller);
+}
+
diff --git a/src/game/Guild.cpp b/src/game/Guild.cpp
index 07b9346312d..90d8a6b93cf 100644
--- a/src/game/Guild.cpp
+++ b/src/game/Guild.cpp
@@ -1,1963 +1,1963 @@
-/*
- * 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 "Database/DatabaseEnv.h"
-#include "WorldPacket.h"
-#include "WorldSession.h"
-#include "MapManager.h"
-#include "Player.h"
-#include "Opcodes.h"
-#include "ObjectMgr.h"
-#include "Guild.h"
-#include "Chat.h"
-#include "SocialMgr.h"
-#include "Util.h"
-
-Guild::Guild()
-{
- Id = 0;
- name = "";
- leaderGuid = 0;
- GINFO = MOTD = "";
- EmblemStyle = 0;
- EmblemColor = 0;
- BorderStyle = 0;
- BorderColor = 0;
- BackgroundColor = 0;
-
- CreatedYear = 0;
- CreatedMonth = 0;
- CreatedDay = 0;
-}
-
-Guild::~Guild()
-{
-
-}
-
-bool Guild::create(uint64 lGuid, std::string gname)
-{
- std::string rname;
- std::string lName;
-
- if(!objmgr.GetPlayerNameByGUID(lGuid, lName))
- return false;
- if(objmgr.GetGuildByName(gname))
- return false;
-
- sLog.outDebug("GUILD: creating guild %s to leader: %u", gname.c_str(), GUID_LOPART(lGuid));
-
- leaderGuid = lGuid;
- name = gname;
- GINFO = "";
- MOTD = "No message set.";
- guildbank_money = 0;
- purchased_tabs = 0;
-
- QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guildid) FROM guild" );
- if( result )
- {
- Id = (*result)[0].GetUInt32()+1;
- delete result;
- }
- else Id = 1;
-
- // gname already assigned to Guild::name, use it to encode string for DB
- CharacterDatabase.escape_string(gname);
-
- std::string dbGINFO = GINFO;
- std::string dbMOTD = MOTD;
- CharacterDatabase.escape_string(dbGINFO);
- CharacterDatabase.escape_string(dbMOTD);
-
- CharacterDatabase.BeginTransaction();
- // CharacterDatabase.PExecute("DELETE FROM guild WHERE guildid='%u'", Id); - MAX(guildid)+1 not exist
- CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid='%u'", Id);
- CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guildid='%u'", Id);
- CharacterDatabase.PExecute("INSERT INTO guild (guildid,name,leaderguid,info,motd,createdate,EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor,BankMoney) "
- "VALUES('%u','%s','%u', '%s', '%s', NOW(),'%u','%u','%u','%u','%u','" I64FMTD "')",
- Id, gname.c_str(), GUID_LOPART(leaderGuid), dbGINFO.c_str(), dbMOTD.c_str(), EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, guildbank_money);
- CharacterDatabase.CommitTransaction();
-
- rname = "Guild Master";
- CreateRank(rname,GR_RIGHT_ALL);
- rname = "Officer";
- CreateRank(rname,GR_RIGHT_ALL);
- rname = "Veteran";
- CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
- rname = "Member";
- CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
- rname = "Initiate";
- CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
-
- return AddMember(lGuid, (uint32)GR_GUILDMASTER);
-}
-
-bool Guild::AddMember(uint64 plGuid, uint32 plRank)
-{
- if(Player::GetGuildIdFromDB(plGuid) != 0) // player already in guild
- return false;
-
- // remove all player signs from another petitions
- // this will be prevent attempt joining player to many guilds and corrupt guild data integrity
- Player::RemovePetitionsAndSigns(plGuid, 9);
-
- // fill player data
- MemberSlot newmember;
-
- if(!FillPlayerData(plGuid, &newmember)) // problems with player data collection
- return false;
-
- newmember.RankId = plRank;
- newmember.OFFnote = (std::string)"";
- newmember.Pnote = (std::string)"";
- newmember.logout_time = time(NULL);
- newmember.BankResetTimeMoney = 0; // this will force update at first query
- for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
- newmember.BankResetTimeTab[i] = 0;
- members[GUID_LOPART(plGuid)] = newmember;
-
- std::string dbPnote = newmember.Pnote;
- std::string dbOFFnote = newmember.OFFnote;
- CharacterDatabase.escape_string(dbPnote);
- CharacterDatabase.escape_string(dbOFFnote);
-
- CharacterDatabase.PExecute("INSERT INTO guild_member (guildid,guid,rank,pnote,offnote) VALUES ('%u', '%u', '%u','%s','%s')",
- Id, GUID_LOPART(plGuid), newmember.RankId, dbPnote.c_str(), dbOFFnote.c_str());
-
- Player* pl = objmgr.GetPlayer(plGuid);
- if(pl)
- {
- pl->SetInGuild(Id);
- pl->SetRank(newmember.RankId);
- pl->SetGuildIdInvited(0);
- }
- else
- {
- Player::SetUInt32ValueInDB(PLAYER_GUILDID, Id, plGuid);
- Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, newmember.RankId, plGuid);
- }
- return true;
-}
-
-void Guild::SetMOTD(std::string motd)
-{
- MOTD = motd;
-
- // motd now can be used for encoding to DB
- CharacterDatabase.escape_string(motd);
- CharacterDatabase.PExecute("UPDATE guild SET motd='%s' WHERE guildid='%u'", motd.c_str(), Id);
-}
-
-void Guild::SetGINFO(std::string ginfo)
-{
- GINFO = ginfo;
-
- // ginfo now can be used for encoding to DB
- CharacterDatabase.escape_string(ginfo);
- CharacterDatabase.PExecute("UPDATE guild SET info='%s' WHERE guildid='%u'", ginfo.c_str(), Id);
-}
-
-bool Guild::LoadGuildFromDB(uint32 GuildId)
-{
- if(!LoadRanksFromDB(GuildId))
- return false;
-
- if(!LoadMembersFromDB(GuildId))
- return false;
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT MAX(TabId) FROM guild_bank_tab WHERE guildid='%u'", GuildId);
- if(result)
- {
- Field *fields = result->Fetch();
- purchased_tabs = fields[0].GetUInt8()+1; // Because TabId begins at 0
- delete result;
- }
- else
- purchased_tabs = 0;
-
- LoadBankRightsFromDB(GuildId); // Must be after LoadRanksFromDB because it populates rank struct
-
- // 0 1 2 3 4 5 6
- result = CharacterDatabase.PQuery("SELECT guildid, name, leaderguid, EmblemStyle, EmblemColor, BorderStyle, BorderColor,"
- // 7 8 9 10 11
- "BackgroundColor, info, motd, createdate, BankMoney FROM guild WHERE guildid = '%u'", GuildId);
-
- if(!result)
- return false;
-
- Field *fields = result->Fetch();
-
- Id = fields[0].GetUInt32();
- name = fields[1].GetCppString();
- leaderGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER);
-
- EmblemStyle = fields[3].GetUInt32();
- EmblemColor = fields[4].GetUInt32();
- BorderStyle = fields[5].GetUInt32();
- BorderColor = fields[6].GetUInt32();
- BackgroundColor = fields[7].GetUInt32();
- GINFO = fields[8].GetCppString();
- MOTD = fields[9].GetCppString();
- uint64 time = fields[10].GetUInt64(); //datetime is uint64 type ... YYYYmmdd:hh:mm:ss
- guildbank_money = fields[11].GetUInt64();
-
- delete result;
-
- uint64 dTime = time /1000000;
- CreatedDay = dTime%100;
- CreatedMonth = (dTime/100)%100;
- CreatedYear = (dTime/10000)%10000;
-
- // If the leader does not exist attempt to promote another member
- if(!objmgr.GetPlayerAccountIdByGUID(leaderGuid ))
- {
- DelMember(leaderGuid);
-
- // check no members case (disbanded)
- if(members.empty())
- return false;
- }
-
- sLog.outDebug("Guild %u Creation time Loaded day: %u, month: %u, year: %u", GuildId, CreatedDay, CreatedMonth, CreatedYear);
- m_bankloaded = false;
- m_eventlogloaded = false;
- m_onlinemembers = 0;
- RenumBankLogs();
- RenumGuildEventlog();
- return true;
-}
-
-bool Guild::LoadRanksFromDB(uint32 GuildId)
-{
- Field *fields;
- QueryResult *result = CharacterDatabase.PQuery("SELECT rname,rights,BankMoneyPerDay,rid FROM guild_rank WHERE guildid = '%u' ORDER BY rid ASC", GuildId);
-
- if(!result)
- return false;
-
- bool broken_ranks = false;
-
- do
- {
- fields = result->Fetch();
-
- std::string rankName = fields[0].GetCppString();
- uint32 rankRights = fields[1].GetUInt32();
- uint32 rankMoney = fields[2].GetUInt32();
- uint32 rankRID = fields[3].GetUInt32();
-
- if(rankRID != m_ranks.size()+1) // guild_rank.rid always store rank+1
- broken_ranks = true;
-
- if(m_ranks.size()==GR_GUILDMASTER) // prevent loss leader rights
- rankRights |= GR_RIGHT_ALL;
-
- AddRank(rankName,rankRights,rankMoney);
- }while( result->NextRow() );
- delete result;
-
- if(m_ranks.size()==0) // empty rank table?
- {
- AddRank("Guild Master",GR_RIGHT_ALL,0);
- broken_ranks = true;
- }
-
- // guild_rank have wrong numbered ranks, repair
- if(broken_ranks)
- {
- sLog.outError("Guild %u have broken `guild_rank` data, repairing...",GuildId);
- CharacterDatabase.BeginTransaction();
- CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid='%u'", GuildId);
- for(size_t i =0; i < m_ranks.size(); ++i)
- {
- // guild_rank.rid always store rank+1
- std::string name = m_ranks[i].name;
- uint32 rights = m_ranks[i].rights;
- CharacterDatabase.escape_string(name);
- CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", GuildId, i+1, name.c_str(), rights);
- }
- CharacterDatabase.CommitTransaction();
- }
-
- return true;
-}
-
-bool Guild::LoadMembersFromDB(uint32 GuildId)
-{
- // 0 1 2 3 4 5
- QueryResult *result = CharacterDatabase.PQuery("SELECT guild_member.guid,rank, pnote, offnote, BankResetTimeMoney,BankRemMoney,"
- // 6 7 8 9 10 11
- "BankResetTimeTab0, BankRemSlotsTab0, BankResetTimeTab1, BankRemSlotsTab1, BankResetTimeTab2, BankRemSlotsTab2,"
- // 12 13 14 15 16 17
- "BankResetTimeTab3, BankRemSlotsTab3, BankResetTimeTab4, BankRemSlotsTab4, BankResetTimeTab5, BankRemSlotsTab5,"
- // 18
- "logout_time FROM guild_member LEFT JOIN characters ON characters.guid = guild_member.guid WHERE guildid = '%u'", GuildId);
-
- if(!result)
- return false;
-
- do
- {
- Field *fields = result->Fetch();
- MemberSlot newmember;
- newmember.RankId = fields[1].GetUInt32();
- uint64 guid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
-
- // Player does not exist
- if(!FillPlayerData(guid, &newmember))
- continue;
-
- newmember.Pnote = fields[2].GetCppString();
- newmember.OFFnote = fields[3].GetCppString();
- newmember.BankResetTimeMoney = fields[4].GetUInt32();
- newmember.BankRemMoney = fields[5].GetUInt32();
- for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
- {
- newmember.BankResetTimeTab[i] = fields[6+(2*i)].GetUInt32();
- newmember.BankRemSlotsTab[i] = fields[7+(2*i)].GetUInt32();
- }
- newmember.logout_time = fields[18].GetUInt64();
- members[GUID_LOPART(guid)] = newmember;
-
- }while( result->NextRow() );
- delete result;
-
- if(members.empty())
- return false;
-
- return true;
-}
-
-bool Guild::FillPlayerData(uint64 guid, MemberSlot* memslot)
-{
- std::string plName;
- uint32 plLevel;
- uint32 plClass;
- uint32 plZone;
-
- Player* pl = objmgr.GetPlayer(guid);
- if(pl)
- {
- plName = pl->GetName();
- plLevel = pl->getLevel();
- plClass = pl->getClass();
- plZone = pl->GetZoneId();
- }
- else
- {
- if(!objmgr.GetPlayerNameByGUID(guid, plName)) // player doesn't exist
- return false;
-
- plLevel = Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL, guid);
- if(plLevel<1||plLevel>255) // can be at broken `data` field
- {
- sLog.outError("Player (GUID: %u) has a broken data in field `characters`.`data`.",GUID_LOPART(guid));
- return false;
- }
- plZone = Player::GetZoneIdFromDB(guid);
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT class FROM characters WHERE guid='%u'", GUID_LOPART(guid));
- if(!result)
- return false;
- plClass = (*result)[0].GetUInt32();
- if(plClass<CLASS_WARRIOR||plClass>=MAX_CLASSES) // can be at broken `class` field
- {
- sLog.outError("Player (GUID: %u) has a broken data in field `characters`.`class`.",GUID_LOPART(guid));
- return false;
- }
-
- delete result;
- }
-
- memslot->name = plName;
- memslot->level = plLevel;
- memslot->Class = plClass;
- memslot->zoneId = plZone;
-
- return(true);
-}
-
-void Guild::LoadPlayerStatsByGuid(uint64 guid)
-{
- MemberList::iterator itr = members.find(GUID_LOPART(guid));
- if (itr == members.end() )
- return;
-
- Player *pl = ObjectAccessor::FindPlayer(guid);
- if(!pl)
- return;
- itr->second.name = pl->GetName();
- itr->second.level = pl->getLevel();
- itr->second.Class = pl->getClass();
-}
-
-void Guild::SetLeader(uint64 guid)
-{
- leaderGuid = guid;
- this->ChangeRank(guid, GR_GUILDMASTER);
-
- CharacterDatabase.PExecute("UPDATE guild SET leaderguid='%u' WHERE guildid='%u'", GUID_LOPART(guid), Id);
-}
-
-void Guild::DelMember(uint64 guid, bool isDisbanding)
-{
- if(this->leaderGuid == guid && !isDisbanding)
- {
- std::ostringstream ss;
- ss<<"SELECT guid FROM guild_member WHERE guildid='"<<Id<<"' AND guid!='"<<this->leaderGuid<<"' ORDER BY rank ASC LIMIT 1";
- QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
- if( result )
- {
- uint64 newLeaderGUID;
- Player *newLeader;
- std::string newLeaderName, oldLeaderName;
-
- newLeaderGUID = (*result)[0].GetUInt64();
- delete result;
-
- this->SetLeader(newLeaderGUID);
-
- newLeader = objmgr.GetPlayer(newLeaderGUID);
- if(newLeader)
- {
- newLeader->SetRank(GR_GUILDMASTER);
- newLeaderName = newLeader->GetName();
- }
- else
- {
- Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, GR_GUILDMASTER, newLeaderGUID);
- objmgr.GetPlayerNameByGUID(newLeaderGUID, newLeaderName);
- }
-
- // when leader non-exist (at guild load with deleted leader only) not send broadcasts
- if(objmgr.GetPlayerNameByGUID(guid, oldLeaderName))
- {
- WorldPacket data(SMSG_GUILD_EVENT, (1+1+oldLeaderName.size()+1+newLeaderName.size()+1));
- data << (uint8)GE_LEADER_CHANGED;
- data << (uint8)2;
- data << oldLeaderName;
- data << newLeaderName;
- this->BroadcastPacket(&data);
-
- data.Initialize(SMSG_GUILD_EVENT, (1+1+oldLeaderName.size()+1));
- data << (uint8)GE_LEFT;
- data << (uint8)1;
- data << oldLeaderName;
- this->BroadcastPacket(&data);
- }
-
- sLog.outDebug( "WORLD: Sent (SMSG_GUILD_EVENT)" );
- }
- else
- {
- this->Disband();
- return;
- }
- }
-
- members.erase(GUID_LOPART(guid));
-
- Player *player = objmgr.GetPlayer(guid);
- if(player)
- {
- player->SetInGuild(0);
- player->SetRank(0);
- }
- else
- {
- Player::SetUInt32ValueInDB(PLAYER_GUILDID, 0, guid);
- Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, GR_GUILDMASTER, guid);
- }
-
- CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guid = '%u'", GUID_LOPART(guid));
-}
-
-void Guild::ChangeRank(uint64 guid, uint32 newRank)
-{
- MemberList::iterator itr = members.find(GUID_LOPART(guid));
- if( itr != members.end() )
- itr->second.RankId = newRank;
-
- Player *player = objmgr.GetPlayer(guid);
- if(player)
- player->SetRank(newRank);
- else
- Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, newRank, guid);
-
- CharacterDatabase.PExecute( "UPDATE guild_member SET rank='%u' WHERE guid='%u'", newRank, GUID_LOPART(guid) );
-}
-
-void Guild::SetPNOTE(uint64 guid,std::string pnote)
-{
- MemberList::iterator itr = members.find(GUID_LOPART(guid));
- if( itr == members.end() )
- return;
-
- itr->second.Pnote = pnote;
-
- // pnote now can be used for encoding to DB
- CharacterDatabase.escape_string(pnote);
- CharacterDatabase.PExecute("UPDATE guild_member SET pnote = '%s' WHERE guid = '%u'", pnote.c_str(), itr->first);
-}
-
-void Guild::SetOFFNOTE(uint64 guid,std::string offnote)
-{
- MemberList::iterator itr = members.find(GUID_LOPART(guid));
- if( itr == members.end() )
- return;
- itr->second.OFFnote = offnote;
- // offnote now can be used for encoding to DB
- CharacterDatabase.escape_string(offnote);
- CharacterDatabase.PExecute("UPDATE guild_member SET offnote = '%s' WHERE guid = '%u'", offnote.c_str(), itr->first);
-}
-
-void Guild::BroadcastToGuild(WorldSession *session, std::string msg, uint32 language)
-{
- if (session && session->GetPlayer() && HasRankRight(session->GetPlayer()->GetRank(),GR_RIGHT_GCHATSPEAK))
- {
- WorldPacket data;
- ChatHandler(session).FillMessageData(&data, CHAT_MSG_GUILD, language, 0, msg.c_str());
-
- for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
- {
- Player *pl = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
-
- if (pl && pl->GetSession() && HasRankRight(pl->GetRank(),GR_RIGHT_GCHATLISTEN) && !pl->GetSocial()->HasIgnore(session->GetPlayer()->GetGUIDLow()) )
- pl->GetSession()->SendPacket(&data);
- }
- }
-}
-
-void Guild::BroadcastToOfficers(WorldSession *session, std::string msg, uint32 language)
-{
- if (session && session->GetPlayer() && HasRankRight(session->GetPlayer()->GetRank(),GR_RIGHT_OFFCHATSPEAK))
- {
- for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
- {
- WorldPacket data;
- ChatHandler::FillMessageData(&data, session, CHAT_MSG_OFFICER, language, NULL, 0, msg.c_str(),NULL);
-
- Player *pl = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
-
- if (pl && pl->GetSession() && HasRankRight(pl->GetRank(),GR_RIGHT_OFFCHATLISTEN) && !pl->GetSocial()->HasIgnore(session->GetPlayer()->GetGUIDLow()))
- pl->GetSession()->SendPacket(&data);
- }
- }
-}
-
-void Guild::BroadcastPacket(WorldPacket *packet)
-{
- for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
- {
- Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
- if(player)
- player->GetSession()->SendPacket(packet);
- }
-}
-
-void Guild::BroadcastPacketToRank(WorldPacket *packet, uint32 rankId)
-{
- for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
- {
- if (itr->second.RankId == rankId)
- {
- Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
- if(player)
- player->GetSession()->SendPacket(packet);
- }
- }
-}
-
-void Guild::CreateRank(std::string name_,uint32 rights)
-{
- if(m_ranks.size() >= GUILD_MAX_RANKS)
- return;
-
- AddRank(name_,rights,0);
-
- for (int i = 0; i < purchased_tabs; ++i)
- {
- CreateBankRightForTab(m_ranks.size()-1, uint8(i));
- }
-
- // guild_rank.rid always store rank+1 value
-
- // name now can be used for encoding to DB
- CharacterDatabase.escape_string(name_);
- CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", Id, m_ranks.size(), name_.c_str(), rights );
-}
-
-void Guild::AddRank(std::string name_,uint32 rights, uint32 money)
-{
- m_ranks.push_back(RankInfo(name_,rights,money));
-}
-
-void Guild::DelRank()
-{
- if(m_ranks.empty())
- return;
-
- // guild_rank.rid always store rank+1 value
- uint32 rank = m_ranks.size()-1;
- CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE rid>='%u' AND guildid='%u'", (rank+1), Id);
-
- m_ranks.pop_back();
-}
-
-std::string Guild::GetRankName(uint32 rankId)
-{
- if(rankId >= m_ranks.size())
- return "<unknown>";
-
- return m_ranks[rankId].name;
-}
-
-uint32 Guild::GetRankRights(uint32 rankId)
-{
- if(rankId >= m_ranks.size())
- return 0;
-
- return m_ranks[rankId].rights;
-}
-
-void Guild::SetRankName(uint32 rankId, std::string name_)
-{
- if(rankId >= m_ranks.size())
- return;
-
- m_ranks[rankId].name = name_;
-
- // name now can be used for encoding to DB
- CharacterDatabase.escape_string(name_);
- CharacterDatabase.PExecute("UPDATE guild_rank SET rname='%s' WHERE rid='%u' AND guildid='%u'", name_.c_str(), (rankId+1), Id);
-}
-
-void Guild::SetRankRights(uint32 rankId, uint32 rights)
-{
- if(rankId >= m_ranks.size())
- return;
-
- m_ranks[rankId].rights = rights;
-
- CharacterDatabase.PExecute("UPDATE guild_rank SET rights='%u' WHERE rid='%u' AND guildid='%u'", rights, (rankId+1), Id);
-}
-
-void Guild::Disband()
-{
- WorldPacket data(SMSG_GUILD_EVENT, 1);
- data << (uint8)GE_DISBANDED;
- this->BroadcastPacket(&data);
-
- while (!members.empty())
- {
- MemberList::iterator itr = members.begin();
- DelMember(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER), true);
- }
-
- CharacterDatabase.BeginTransaction();
- CharacterDatabase.PExecute("DELETE FROM guild WHERE guildid = '%u'",Id);
- CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid = '%u'",Id);
- CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid = '%u'",Id);
- CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid = '%u'",Id);
- CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u'",Id);
- CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid = '%u'",Id);
- CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid = '%u'",Id);
- CharacterDatabase.CommitTransaction();
- objmgr.RemoveGuild(this);
-}
-
-void Guild::Roster(WorldSession *session)
-{
- // we can only guess size
- WorldPacket data(SMSG_GUILD_ROSTER, (4+MOTD.length()+1+GINFO.length()+1+4+m_ranks.size()*(4+4+GUILD_BANK_MAX_TABS*(4+4))+members.size()*50));
- data << (uint32)members.size();
- data << MOTD;
- data << GINFO;
-
- data << (uint32)m_ranks.size();
- for (RankList::iterator ritr = m_ranks.begin(); ritr != m_ranks.end();++ritr)
- {
- data << (uint32)ritr->rights;
- data << (uint32)ritr->BankMoneyPerDay; // count of: withdraw gold(gold/day) Note: in game set gold, in packet set bronze.
- for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
- {
- data << (uint32)ritr->TabRight[i]; // for TAB_i rights: view tabs = 0x01, deposit items =0x02
- data << (uint32)ritr->TabSlotPerDay[i]; // for TAB_i count of: withdraw items(stack/day)
- }
- }
- for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
- {
- if (Player *pl = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)))
- {
- data << (uint64)pl->GetGUID();
- data << (uint8)1;
- data << (std::string)pl->GetName();
- data << (uint32)itr->second.RankId;
- data << (uint8)pl->getLevel();
- data << (uint8)pl->getClass();
- data << (uint8)0; // new 2.4.0
- data << (uint32)pl->GetZoneId();
- data << itr->second.Pnote;
- data << itr->second.OFFnote;
- }
- else
- {
- data << uint64(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
- data << (uint8)0;
- data << itr->second.name;
- data << (uint32)itr->second.RankId;
- data << (uint8)itr->second.level;
- data << (uint8)itr->second.Class;
- data << (uint8)0; // new 2.4.0
- data << (uint32)itr->second.zoneId;
- data << (float(time(NULL)-itr->second.logout_time) / DAY);
- data << itr->second.Pnote;
- data << itr->second.OFFnote;
- }
- }
- session->SendPacket(&data);;
- sLog.outDebug( "WORLD: Sent (SMSG_GUILD_ROSTER)" );
-}
-
-void Guild::Query(WorldSession *session)
-{
- WorldPacket data(SMSG_GUILD_QUERY_RESPONSE, (8*32+200));// we can only guess size
-
- data << Id;
- data << name;
- RankList::iterator itr;
- for (size_t i = 0 ; i < 10; ++i) // show always 10 ranks
- {
- if(i < m_ranks.size())
- data << m_ranks[i].name;
- else
- data << (uint8)0; // null string
- }
-
- data << uint32(EmblemStyle);
- data << uint32(EmblemColor);
- data << uint32(BorderStyle);
- data << uint32(BorderColor);
- data << uint32(BackgroundColor);
-
- session->SendPacket( &data );
- sLog.outDebug( "WORLD: Sent (SMSG_GUILD_QUERY_RESPONSE)" );
-}
-
-void Guild::SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor, uint32 backgroundColor)
-{
- this->EmblemStyle = emblemStyle;
- this->EmblemColor = emblemColor;
- this->BorderStyle = borderStyle;
- this->BorderColor = borderColor;
- this->BackgroundColor = backgroundColor;
-
- CharacterDatabase.PExecute("UPDATE guild SET EmblemStyle=%u, EmblemColor=%u, BorderStyle=%u, BorderColor=%u, BackgroundColor=%u WHERE guildid = %u", EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, Id);
-}
-
-void Guild::UpdateLogoutTime(uint64 guid)
-{
- MemberList::iterator itr = members.find(GUID_LOPART(guid));
- if (itr == members.end() )
- return;
-
- itr->second.logout_time = time(NULL);
-
- if (m_onlinemembers > 0)
- --m_onlinemembers;
- else
- {
- UnloadGuildBank();
- UnloadGuildEventlog();
- }
-}
-
-// *************************************************
-// Guild Eventlog part
-// *************************************************
-// Display guild eventlog
-void Guild::DisplayGuildEventlog(WorldSession *session)
-{
- // Load guild eventlog, if not already done
- if (!m_eventlogloaded)
- LoadGuildEventLogFromDB();
-
- // Sending result
- WorldPacket data(MSG_GUILD_EVENT_LOG_QUERY, 0);
- // count, max count == 100
- data << uint8(m_GuildEventlog.size());
- for (GuildEventlog::const_iterator itr = m_GuildEventlog.begin(); itr != m_GuildEventlog.end(); ++itr)
- {
- // Event type
- data << uint8((*itr)->EventType);
- // Player 1
- data << uint64((*itr)->PlayerGuid1);
- // Player 2 not for left/join guild events
- if( (*itr)->EventType != GUILD_EVENT_LOG_JOIN_GUILD && (*itr)->EventType != GUILD_EVENT_LOG_LEAVE_GUILD )
- data << uint64((*itr)->PlayerGuid2);
- // New Rank - only for promote/demote guild events
- if( (*itr)->EventType == GUILD_EVENT_LOG_PROMOTE_PLAYER || (*itr)->EventType == GUILD_EVENT_LOG_DEMOTE_PLAYER )
- data << uint8((*itr)->NewRank);
- // Event timestamp
- data << uint32(time(NULL)-(*itr)->TimeStamp);
- }
- session->SendPacket(&data);
- sLog.outDebug("WORLD: Sent (MSG_GUILD_EVENT_LOG_QUERY)");
-}
-
-// Load guild eventlog from DB
-void Guild::LoadGuildEventLogFromDB()
-{
- // Return if already loaded
- if (m_eventlogloaded)
- return;
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp FROM guild_eventlog WHERE guildid=%u ORDER BY LogGuid DESC LIMIT %u", Id, GUILD_EVENTLOG_MAX_ENTRIES);
- if(!result)
- return;
- do
- {
- Field *fields = result->Fetch();
- GuildEventlogEntry *NewEvent = new GuildEventlogEntry;
- // Fill entry
- NewEvent->LogGuid = fields[0].GetUInt32();
- NewEvent->EventType = fields[1].GetUInt8();
- NewEvent->PlayerGuid1 = fields[2].GetUInt32();
- NewEvent->PlayerGuid2 = fields[3].GetUInt32();
- NewEvent->NewRank = fields[4].GetUInt8();
- NewEvent->TimeStamp = fields[5].GetUInt64();
- // Add entry to map
- m_GuildEventlog.push_front(NewEvent);
-
- } while( result->NextRow() );
- delete result;
-
- // Check lists size in case to many event entries in db
- // This cases can happen only if a crash occured somewhere and table has too many log entries
- if (!m_GuildEventlog.empty())
- {
- CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid=%u AND LogGuid < %u", Id, m_GuildEventlog.front()->LogGuid);
- }
- m_eventlogloaded = true;
-}
-
-// Unload guild eventlog
-void Guild::UnloadGuildEventlog()
-{
- if (!m_eventlogloaded)
- return;
- GuildEventlogEntry *EventLogEntry;
- if( !m_GuildEventlog.empty() )
- {
- do
- {
- EventLogEntry = *(m_GuildEventlog.begin());
- m_GuildEventlog.pop_front();
- delete EventLogEntry;
- }while( !m_GuildEventlog.empty() );
- }
- m_eventlogloaded = false;
-}
-
-// This will renum guids used at load to prevent always going up until infinit
-void Guild::RenumGuildEventlog()
-{
- QueryResult *result = CharacterDatabase.PQuery("SELECT Min(LogGuid), Max(LogGuid) FROM guild_eventlog WHERE guildid = %u", Id);
- if(!result)
- return;
-
- Field *fields = result->Fetch();
- CharacterDatabase.PExecute("UPDATE guild_eventlog SET LogGuid=LogGuid-%u+1 WHERE guildid=%u ORDER BY LogGuid %s",fields[0].GetUInt32(), Id, fields[0].GetUInt32()?"ASC":"DESC");
- GuildEventlogMaxGuid = fields[1].GetUInt32()+1;
- delete result;
-}
-
-// Add entry to guild eventlog
-void Guild::LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid2, uint8 NewRank)
-{
- GuildEventlogEntry *NewEvent = new GuildEventlogEntry;
- // Fill entry
- NewEvent->LogGuid = GuildEventlogMaxGuid++;
- NewEvent->EventType = EventType;
- NewEvent->PlayerGuid1 = PlayerGuid1;
- NewEvent->PlayerGuid2 = PlayerGuid2;
- NewEvent->NewRank = NewRank;
- NewEvent->TimeStamp = uint32(time(NULL));
- // Check max entry limit and delete from db if needed
- if (m_GuildEventlog.size() > GUILD_EVENTLOG_MAX_ENTRIES)
- {
- GuildEventlogEntry *OldEvent = *(m_GuildEventlog.begin());
- m_GuildEventlog.pop_front();
- CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, OldEvent->LogGuid);
- delete OldEvent;
- }
- // Add entry to map
- m_GuildEventlog.push_back(NewEvent);
- // Add new eventlog entry into DB
- CharacterDatabase.PExecute("INSERT INTO guild_eventlog (guildid, LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','" I64FMTD "')",
- Id, NewEvent->LogGuid, uint32(NewEvent->EventType), NewEvent->PlayerGuid1, NewEvent->PlayerGuid2, uint32(NewEvent->NewRank), NewEvent->TimeStamp);
-}
-
-// *************************************************
-// Guild Bank part
-// *************************************************
-// Bank content related
-void Guild::DisplayGuildBankContent(WorldSession *session, uint8 TabId)
-{
- WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
-
- GuildBankTab const* tab = GetBankTab(TabId);
- if (!tab)
- return;
-
- if(!IsMemberHaveRights(session->GetPlayer()->GetGUIDLow(),TabId,GUILD_BANK_RIGHT_VIEW_TAB))
- return;
-
- data << uint64(GetGuildBankMoney());
- data << uint8(TabId);
- // remaining slots for today
- data << uint32(GetMemberSlotWithdrawRem(session->GetPlayer()->GetGUIDLow(), TabId));
- data << uint8(0); // Tell client this is a tab content packet
-
- data << uint8(GUILD_BANK_MAX_SLOTS);
-
- for (int i=0; i<GUILD_BANK_MAX_SLOTS; ++i)
- AppendDisplayGuildBankSlot(data, tab, i);
-
- session->SendPacket(&data);
-
- sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
-}
-
-void Guild::DisplayGuildBankMoneyUpdate()
-{
- WorldPacket data(SMSG_GUILD_BANK_LIST, 8+1+4+1+1);
-
- data << uint64(GetGuildBankMoney());
- data << uint8(0);
- // remaining slots for today
-
- size_t rempos = data.wpos();
- data << uint32(0); // will be filled later
- data << uint8(0); // Tell client this is a tab content packet
-
- data << uint8(0); // not send items
-
- BroadcastPacket(&data);
-
- sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
-}
-
-void Guild::DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2)
-{
- GuildBankTab const* tab = GetBankTab(TabId);
- if (!tab)
- return;
-
- WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
-
- data << uint64(GetGuildBankMoney());
- data << uint8(TabId);
- // remaining slots for today
-
- size_t rempos = data.wpos();
- data << uint32(0); // will be filled later
- data << uint8(0); // Tell client this is a tab content packet
-
- if(slot2==-1) // single item in slot1
- {
- data << uint8(1);
-
- AppendDisplayGuildBankSlot(data, tab, slot1);
- }
- else // 2 items (in slot1 and slot2)
- {
- data << uint8(2);
-
- if(slot1 > slot2)
- std::swap(slot1,slot2);
-
- AppendDisplayGuildBankSlot(data, tab, slot1);
- AppendDisplayGuildBankSlot(data, tab, slot2);
- }
-
- for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
- {
- Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
- if(!player)
- continue;
-
- if(!IsMemberHaveRights(itr->first,TabId,GUILD_BANK_RIGHT_VIEW_TAB))
- continue;
-
- data.put<uint32>(rempos,uint32(GetMemberSlotWithdrawRem(player->GetGUIDLow(), TabId)));
-
- player->GetSession()->SendPacket(&data);
- }
-
- sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
-}
-
-void Guild::DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots)
-{
- GuildBankTab const* tab = GetBankTab(TabId);
- if (!tab)
- return;
-
- WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
-
- data << uint64(GetGuildBankMoney());
- data << uint8(TabId);
- // remaining slots for today
-
- size_t rempos = data.wpos();
- data << uint32(0); // will be filled later
- data << uint8(0); // Tell client this is a tab content packet
-
- data << uint8(slots.size()); // updates count
-
- for(GuildItemPosCountVec::const_iterator itr = slots.begin(); itr != slots.end(); ++itr)
- AppendDisplayGuildBankSlot(data, tab, itr->slot);
-
- for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
- {
- Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
- if(!player)
- continue;
-
- if(!IsMemberHaveRights(itr->first,TabId,GUILD_BANK_RIGHT_VIEW_TAB))
- continue;
-
- data.put<uint32>(rempos,uint32(GetMemberSlotWithdrawRem(player->GetGUIDLow(), TabId)));
-
- player->GetSession()->SendPacket(&data);
- }
-
- sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
-}
-
-Item* Guild::GetItem(uint8 TabId, uint8 SlotId)
-{
- if (TabId >= m_TabListMap.size() || SlotId >= GUILD_BANK_MAX_SLOTS)
- return NULL;
- return m_TabListMap[TabId]->Slots[SlotId];
-}
-
-// *************************************************
-// Tab related
-
-void Guild::DisplayGuildBankTabsInfo(WorldSession *session)
-{
- // Time to load bank if not already done
- if (!m_bankloaded)
- LoadGuildBankFromDB();
-
- WorldPacket data(SMSG_GUILD_BANK_LIST, 500);
-
- data << uint64(GetGuildBankMoney());
- data << uint8(0); // TabInfo packet must be for TabId 0
- data << uint32(0xFFFFFFFF); // bit 9 must be set for this packet to work
- data << uint8(1); // Tell Client this is a TabInfo packet
-
- data << uint8(purchased_tabs); // here is the number of tabs
-
- for(int i = 0; i < purchased_tabs; ++i)
- {
- data << m_TabListMap[i]->Name.c_str();
- data << m_TabListMap[i]->Icon.c_str();
- }
- data << uint8(0); // Do not send tab content
- session->SendPacket(&data);
-
- sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
-}
-
-void Guild::CreateNewBankTab()
-{
- if (purchased_tabs >= GUILD_BANK_MAX_TABS)
- return;
-
- ++purchased_tabs;
-
- GuildBankTab* AnotherTab = new GuildBankTab;
- memset(AnotherTab->Slots, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*));
- m_TabListMap.resize(purchased_tabs);
- m_TabListMap[purchased_tabs-1] = AnotherTab;
-
- CharacterDatabase.BeginTransaction();
- CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid='%u' AND TabId='%u'", Id, uint32(purchased_tabs-1));
- CharacterDatabase.PExecute("INSERT INTO guild_bank_tab (guildid,TabId) VALUES ('%u','%u')", Id, uint32(purchased_tabs-1));
- CharacterDatabase.CommitTransaction();
-}
-
-void Guild::SetGuildBankTabInfo(uint8 TabId, std::string Name, std::string Icon)
-{
- if (TabId >= GUILD_BANK_MAX_TABS)
- return;
- if (TabId >= m_TabListMap.size())
- return;
-
- if (!m_TabListMap[TabId])
- return;
-
- if(m_TabListMap[TabId]->Name == Name && m_TabListMap[TabId]->Icon == Icon)
- return;
-
- m_TabListMap[TabId]->Name = Name;
- m_TabListMap[TabId]->Icon = Icon;
-
- CharacterDatabase.escape_string(Name);
- CharacterDatabase.escape_string(Icon);
- CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabName='%s',TabIcon='%s' WHERE guildid='%u' AND TabId='%u'", Name.c_str(), Icon.c_str(), Id, uint32(TabId));
-}
-
-void Guild::CreateBankRightForTab(uint32 rankId, uint8 TabId)
-{
- sLog.outDebug("CreateBankRightForTab. rank: %u, TabId: %u", rankId, uint32(TabId));
- if (rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS)
- return;
-
- m_ranks[rankId].TabRight[TabId]=0;
- m_ranks[rankId].TabSlotPerDay[TabId]=0;
- CharacterDatabase.BeginTransaction();
- CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u' AND TabId = '%u' AND rid = '%u'", Id, uint32(TabId), rankId);
- CharacterDatabase.PExecute("INSERT INTO guild_bank_right (guildid,TabId,rid) VALUES ('%u','%u','%u')", Id, uint32(TabId), rankId);
- CharacterDatabase.CommitTransaction();
-}
-
-uint32 Guild::GetBankRights(uint32 rankId, uint8 TabId) const
-{
- if(rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS)
- return 0;
-
- return m_ranks[rankId].TabRight[TabId];
-}
-
-// *************************************************
-// Guild bank loading/unloading related
-
-// This load should be called when the bank is first accessed by a guild member
-void Guild::LoadGuildBankFromDB()
-{
- if (m_bankloaded)
- return;
-
- m_bankloaded = true;
- LoadGuildBankEventLogFromDB();
-
- // 0 1 2 3
- QueryResult *result = CharacterDatabase.PQuery("SELECT TabId, TabName, TabIcon, TabText FROM guild_bank_tab WHERE guildid='%u' ORDER BY TabId", Id);
- if(!result)
- {
- purchased_tabs = 0;
- return;
- }
-
- m_TabListMap.resize(purchased_tabs);
- do
- {
- Field *fields = result->Fetch();
- uint8 TabId = fields[0].GetUInt8();
-
- GuildBankTab *NewTab = new GuildBankTab;
- memset(NewTab->Slots, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*));
-
- NewTab->Name = fields[1].GetCppString();
- NewTab->Icon = fields[2].GetCppString();
- NewTab->Text = fields[3].GetCppString();
-
- m_TabListMap[TabId] = NewTab;
- }while( result->NextRow() );
-
- delete result;
-
- // 0 1 2 3
- result = CharacterDatabase.PQuery("SELECT TabId, SlotId, item_guid, item_entry FROM guild_bank_item WHERE guildid='%u' ORDER BY TabId", Id);
- if(!result)
- return;
-
- do
- {
- Field *fields = result->Fetch();
- uint8 TabId = fields[0].GetUInt8();
- uint8 SlotId = fields[1].GetUInt8();
- uint32 ItemGuid = fields[2].GetUInt32();
- uint32 ItemEntry = fields[3].GetUInt32();
-
- if (TabId >= purchased_tabs || TabId >= GUILD_BANK_MAX_TABS)
- {
- sLog.outError( "Guild::LoadGuildBankFromDB: Invalid tab for item (GUID: %u id: #%u) in guild bank, skipped.", ItemGuid,ItemEntry);
- continue;
- }
-
- if (SlotId >= GUILD_BANK_MAX_SLOTS)
- {
- sLog.outError( "Guild::LoadGuildBankFromDB: Invalid slot for item (GUID: %u id: #%u) in guild bank, skipped.", ItemGuid,ItemEntry);
- continue;
- }
-
- ItemPrototype const *proto = objmgr.GetItemPrototype(ItemEntry);
-
- if(!proto)
- {
- sLog.outError( "Guild::LoadGuildBankFromDB: Unknown item (GUID: %u id: #%u) in guild bank, skipped.", ItemGuid,ItemEntry);
- continue;
- }
-
- Item *pItem = NewItemOrBag(proto);
- if(!pItem->LoadFromDB(ItemGuid, 0))
- {
- CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid='%u' AND TabId='%u' AND SlotId='%u'", Id, uint32(TabId), uint32(SlotId));
- sLog.outError("Item GUID %u not found in item_instance, deleting from Guild Bank!", ItemGuid);
- delete pItem;
- continue;
- }
-
- pItem->AddToWorld();
- m_TabListMap[TabId]->Slots[SlotId] = pItem;
- }while( result->NextRow() );
-
- delete result;
-}
-
-// This unload should be called when the last member of the guild gets offline
-void Guild::UnloadGuildBank()
-{
- if (!m_bankloaded)
- return;
- for (uint8 i = 0 ; i < purchased_tabs ; ++i )
- {
- for (uint8 j = 0 ; j < GUILD_BANK_MAX_SLOTS ; ++j)
- {
- if (m_TabListMap[i]->Slots[j])
- {
- m_TabListMap[i]->Slots[j]->RemoveFromWorld();
- delete m_TabListMap[i]->Slots[j];
- }
- }
- delete m_TabListMap[i];
- }
- m_TabListMap.clear();
-
- UnloadGuildBankEventLog();
- m_bankloaded = false;
-}
-
-// *************************************************
-// Money deposit/withdraw related
-
-void Guild::SendMoneyInfo(WorldSession *session, uint32 LowGuid)
-{
- WorldPacket data(MSG_GUILD_BANK_MONEY_WITHDRAWN, 4);
- data << uint32(GetMemberMoneyWithdrawRem(LowGuid));
- session->SendPacket(&data);
- sLog.outDebug("WORLD: Sent MSG_GUILD_BANK_MONEY_WITHDRAWN");
-}
-
-bool Guild::MemberMoneyWithdraw(uint32 amount, uint32 LowGuid)
-{
- uint32 MoneyWithDrawRight = GetMemberMoneyWithdrawRem(LowGuid);
-
- if (MoneyWithDrawRight < amount || GetGuildBankMoney() < amount)
- return false;
-
- SetBankMoney(GetGuildBankMoney()-amount);
-
- if (MoneyWithDrawRight < WITHDRAW_MONEY_UNLIMITED)
- {
- MemberList::iterator itr = members.find(LowGuid);
- if (itr == members.end() )
- return false;
- itr->second.BankRemMoney -= amount;
- CharacterDatabase.PExecute("UPDATE guild_member SET BankRemMoney='%u' WHERE guildid='%u' AND guid='%u'",
- itr->second.BankRemMoney, Id, LowGuid);
- }
- return true;
-}
-
-void Guild::SetBankMoney(int64 money)
-{
- if (money < 0) // I don't know how this happens, it does!!
- money = 0;
- guildbank_money = money;
-
- CharacterDatabase.PExecute("UPDATE guild SET BankMoney='" I64FMTD "' WHERE guildid='%u'", money, Id);
-}
-
-// *************************************************
-// Item per day and money per day related
-
-bool Guild::MemberItemWithdraw(uint8 TabId, uint32 LowGuid)
-{
- uint32 SlotsWithDrawRight = GetMemberSlotWithdrawRem(LowGuid, TabId);
-
- if (SlotsWithDrawRight == 0)
- return false;
-
- if (SlotsWithDrawRight < WITHDRAW_SLOT_UNLIMITED)
- {
- MemberList::iterator itr = members.find(LowGuid);
- if (itr == members.end() )
- return false;
- --itr->second.BankRemSlotsTab[TabId];
- CharacterDatabase.PExecute("UPDATE guild_member SET BankRemSlotsTab%u='%u' WHERE guildid='%u' AND guid='%u'",
- uint32(TabId), itr->second.BankRemSlotsTab[TabId], Id, LowGuid);
- }
- return true;
-}
-
-bool Guild::IsMemberHaveRights(uint32 LowGuid, uint8 TabId, uint32 rights) const
-{
- MemberList::const_iterator itr = members.find(LowGuid);
- if (itr == members.end() )
- return false;
-
- if (itr->second.RankId == GR_GUILDMASTER)
- return true;
-
- return (GetBankRights(itr->second.RankId,TabId) & rights)==rights;
-}
-
-uint32 Guild::GetMemberSlotWithdrawRem(uint32 LowGuid, uint8 TabId)
-{
- MemberList::iterator itr = members.find(LowGuid);
- if (itr == members.end() )
- return 0;
-
- if (itr->second.RankId == GR_GUILDMASTER)
- return WITHDRAW_SLOT_UNLIMITED;
-
- if((GetBankRights(itr->second.RankId,TabId) & GUILD_BANK_RIGHT_VIEW_TAB)!=GUILD_BANK_RIGHT_VIEW_TAB)
- return 0;
-
- uint32 curTime = uint32(time(NULL)/MINUTE);
- if (curTime - itr->second.BankResetTimeTab[TabId] >= 24*HOUR/MINUTE)
- {
- itr->second.BankResetTimeTab[TabId] = curTime;
- itr->second.BankRemSlotsTab[TabId] = GetBankSlotPerDay(itr->second.RankId, TabId);
- CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeTab%u='%u',BankRemSlotsTab%u='%u' WHERE guildid='%u' AND guid='%u'",
- uint32(TabId), itr->second.BankResetTimeTab[TabId], uint32(TabId), itr->second.BankRemSlotsTab[TabId], Id, LowGuid);
- }
- return itr->second.BankRemSlotsTab[TabId];
-}
-
-uint32 Guild::GetMemberMoneyWithdrawRem(uint32 LowGuid)
-{
- MemberList::iterator itr = members.find(LowGuid);
- if (itr == members.end() )
- return 0;
-
- if (itr->second.RankId == GR_GUILDMASTER)
- return WITHDRAW_MONEY_UNLIMITED;
-
- uint32 curTime = uint32(time(NULL)/MINUTE); // minutes
- // 24 hours
- if (curTime > itr->second.BankResetTimeMoney + 24*HOUR/MINUTE)
- {
- itr->second.BankResetTimeMoney = curTime;
- itr->second.BankRemMoney = GetBankMoneyPerDay(itr->second.RankId);
- CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeMoney='%u',BankRemMoney='%u' WHERE guildid='%u' AND guid='%u'",
- itr->second.BankResetTimeMoney, itr->second.BankRemMoney, Id, LowGuid);
- }
- return itr->second.BankRemMoney;
-}
-
-void Guild::SetBankMoneyPerDay(uint32 rankId, uint32 money)
-{
- if (rankId >= m_ranks.size())
- return;
-
- if (rankId == GR_GUILDMASTER)
- money = WITHDRAW_MONEY_UNLIMITED;
-
- m_ranks[rankId].BankMoneyPerDay = money;
-
- for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
- if (itr->second.RankId == rankId)
- itr->second.BankResetTimeMoney = 0;
-
- CharacterDatabase.PExecute("UPDATE guild_rank SET BankMoneyPerDay='%u' WHERE rid='%u' AND guildid='%u'", money, (rankId+1), Id);
- CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeMoney='0' WHERE guildid='%u' AND rank='%u'", Id, rankId);
-}
-
-void Guild::SetBankRightsAndSlots(uint32 rankId, uint8 TabId, uint32 right, uint32 nbSlots, bool db)
-{
- if(rankId >= m_ranks.size() ||
- TabId >= GUILD_BANK_MAX_TABS ||
- TabId >= purchased_tabs)
- return;
-
- if (rankId == GR_GUILDMASTER)
- {
- nbSlots = WITHDRAW_SLOT_UNLIMITED;
- right = GUILD_BANK_RIGHT_FULL;
- }
-
- m_ranks[rankId].TabSlotPerDay[TabId]=nbSlots;
- m_ranks[rankId].TabRight[TabId]=right;
-
- if (db)
- {
- for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
- if (itr->second.RankId == rankId)
- for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
- itr->second.BankResetTimeTab[i] = 0;
-
- CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid='%u' AND TabId='%u' AND rid='%u'", Id, uint32(TabId), rankId);
- CharacterDatabase.PExecute("INSERT INTO guild_bank_right (guildid,TabId,rid,gbright,SlotPerDay) VALUES "
- "('%u','%u','%u','%u','%u')", Id, uint32(TabId), rankId, m_ranks[rankId].TabRight[TabId], m_ranks[rankId].TabSlotPerDay[TabId]);
- CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeTab%u='0' WHERE guildid='%u' AND rank='%u'", uint32(TabId), Id, rankId);
- }
-}
-
-uint32 Guild::GetBankMoneyPerDay(uint32 rankId)
-{
- if(rankId >= m_ranks.size())
- return 0;
-
- if (rankId == GR_GUILDMASTER)
- return WITHDRAW_MONEY_UNLIMITED;
- return m_ranks[rankId].BankMoneyPerDay;
-}
-
-uint32 Guild::GetBankSlotPerDay(uint32 rankId, uint8 TabId)
-{
- if(rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS)
- return 0;
-
- if (rankId == GR_GUILDMASTER)
- return WITHDRAW_SLOT_UNLIMITED;
- return m_ranks[rankId].TabSlotPerDay[TabId];
-}
-
-// *************************************************
-// Rights per day related
-
-void Guild::LoadBankRightsFromDB(uint32 GuildId)
-{
- // 0 1 2 3
- QueryResult *result = CharacterDatabase.PQuery("SELECT TabId, rid, gbright, SlotPerDay FROM guild_bank_right WHERE guildid = '%u' ORDER BY TabId", GuildId);
-
- if(!result)
- return;
-
- do
- {
- Field *fields = result->Fetch();
- uint8 TabId = fields[0].GetUInt8();
- uint32 rankId = fields[1].GetUInt32();
- uint16 right = fields[2].GetUInt16();
- uint16 SlotPerDay = fields[3].GetUInt16();
-
- SetBankRightsAndSlots(rankId, TabId, right, SlotPerDay, false);
-
- }while( result->NextRow() );
- delete result;
-
- return;
-}
-
-// *************************************************
-// Bank log related
-
-void Guild::LoadGuildBankEventLogFromDB()
-{
- // We can't add a limit as in Guild::LoadGuildEventLogFromDB since we fetch both money and bank log and know nothing about the composition
- // 0 1 2 3 4 5 6 7
- QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, LogEntry, TabId, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp FROM guild_bank_eventlog WHERE guildid='%u' ORDER BY TimeStamp DESC", Id);
- if(!result)
- return;
-
- do
- {
- Field *fields = result->Fetch();
- GuildBankEvent *NewEvent = new GuildBankEvent;
-
- NewEvent->LogGuid = fields[0].GetUInt32();
- NewEvent->LogEntry = fields[1].GetUInt8();
- uint8 TabId = fields[2].GetUInt8();
- NewEvent->PlayerGuid = fields[3].GetUInt32();
- NewEvent->ItemOrMoney = fields[4].GetUInt32();
- NewEvent->ItemStackCount = fields[5].GetUInt8();
- NewEvent->DestTabId = fields[6].GetUInt8();
- NewEvent->TimeStamp = fields[7].GetUInt64();
-
- if (TabId >= GUILD_BANK_MAX_TABS)
- {
- sLog.outError( "Guild::LoadGuildBankEventLogFromDB: Invalid tabid '%u' for guild bank log entry (guild: '%s', LogGuid: %u), skipped.", TabId, GetName().c_str(), NewEvent->LogGuid);
- delete NewEvent;
- continue;
- }
- if (NewEvent->isMoneyEvent() && m_GuildBankEventLog_Money.size() >= GUILD_BANK_MAX_LOGS
- || m_GuildBankEventLog_Item[TabId].size() >= GUILD_BANK_MAX_LOGS)
- {
- delete NewEvent;
- continue;
- }
- if (NewEvent->isMoneyEvent())
- m_GuildBankEventLog_Money.push_front(NewEvent);
- else
- m_GuildBankEventLog_Item[TabId].push_front(NewEvent);
-
- }while( result->NextRow() );
- delete result;
-
- // Check lists size in case to many event entries in db for a tab or for money
- // This cases can happen only if a crash occured somewhere and table has too many log entries
- if (!m_GuildBankEventLog_Money.empty())
- {
- CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid=%u AND LogGuid < %u",
- Id, m_GuildBankEventLog_Money.front()->LogGuid);
- }
- for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
- {
- if (!m_GuildBankEventLog_Item[i].empty())
- {
- CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid=%u AND LogGuid < %u",
- Id, m_GuildBankEventLog_Item[i].front()->LogGuid);
- }
- }
-}
-
-void Guild::UnloadGuildBankEventLog()
-{
- GuildBankEvent *EventLogEntry;
- if( !m_GuildBankEventLog_Money.empty() )
- {
- do
- {
- EventLogEntry = *(m_GuildBankEventLog_Money.begin());
- m_GuildBankEventLog_Money.pop_front();
- delete EventLogEntry;
- }while( !m_GuildBankEventLog_Money.empty() );
- }
-
- for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
- {
- if( !m_GuildBankEventLog_Item[i].empty() )
- {
- do
- {
- EventLogEntry = *(m_GuildBankEventLog_Item[i].begin());
- m_GuildBankEventLog_Item[i].pop_front();
- delete EventLogEntry;
- }while( !m_GuildBankEventLog_Item[i].empty() );
- }
- }
-}
-
-void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId)
-{
- if (TabId > GUILD_BANK_MAX_TABS)
- return;
-
- if (TabId == GUILD_BANK_MAX_TABS)
- {
- // Here we display money logs
- WorldPacket data(MSG_GUILD_BANK_LOG_QUERY, m_GuildBankEventLog_Money.size()*(4*4+1)+1+1);
- data << uint8(TabId); // Here GUILD_BANK_MAX_TABS
- data << uint8(m_GuildBankEventLog_Money.size()); // number of log entries
- for (GuildBankEventLog::const_iterator itr = m_GuildBankEventLog_Money.begin(); itr != m_GuildBankEventLog_Money.end(); ++itr)
- {
- data << uint8((*itr)->LogEntry);
- data << uint64(MAKE_NEW_GUID((*itr)->PlayerGuid,0,HIGHGUID_PLAYER));
- data << uint32((*itr)->ItemOrMoney);
- data << uint32(time(NULL)-(*itr)->TimeStamp);
- }
- session->SendPacket(&data);
- }
- else
- {
- // here we display current tab logs
- WorldPacket data(MSG_GUILD_BANK_LOG_QUERY, m_GuildBankEventLog_Item[TabId].size()*(4*4+1+1)+1+1);
- data << uint8(TabId); // Here a real Tab Id
- // number of log entries
- data << uint8(m_GuildBankEventLog_Item[TabId].size());
- for (GuildBankEventLog::const_iterator itr = m_GuildBankEventLog_Item[TabId].begin(); itr != m_GuildBankEventLog_Item[TabId].end(); ++itr)
- {
- data << uint8((*itr)->LogEntry);
- data << uint64(MAKE_NEW_GUID((*itr)->PlayerGuid,0,HIGHGUID_PLAYER));
- data << uint32((*itr)->ItemOrMoney);
- data << uint8((*itr)->ItemStackCount);
- if ((*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || (*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2)
- data << uint8((*itr)->DestTabId); // moved tab
- data << uint32(time(NULL)-(*itr)->TimeStamp);
- }
- session->SendPacket(&data);
- }
- sLog.outDebug("WORLD: Sent (MSG_GUILD_BANK_LOG_QUERY)");
-}
-
-void Guild::LogBankEvent(uint8 LogEntry, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount, uint8 DestTabId)
-{
- GuildBankEvent *NewEvent = new GuildBankEvent;
-
- NewEvent->LogGuid = LogMaxGuid++;
- NewEvent->LogEntry = LogEntry;
- NewEvent->PlayerGuid = PlayerGuidLow;
- NewEvent->ItemOrMoney = ItemOrMoney;
- NewEvent->ItemStackCount = ItemStackCount;
- NewEvent->DestTabId = DestTabId;
- NewEvent->TimeStamp = uint32(time(NULL));
-
- if (NewEvent->isMoneyEvent())
- {
- if (m_GuildBankEventLog_Money.size() > GUILD_BANK_MAX_LOGS)
- {
- GuildBankEvent *OldEvent = *(m_GuildBankEventLog_Money.begin());
- m_GuildBankEventLog_Money.pop_front();
- CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, OldEvent->LogGuid);
- delete OldEvent;
- }
- m_GuildBankEventLog_Money.push_back(NewEvent);
- }
- else
- {
- if (m_GuildBankEventLog_Item[TabId].size() > GUILD_BANK_MAX_LOGS)
- {
- GuildBankEvent *OldEvent = *(m_GuildBankEventLog_Item[TabId].begin());
- m_GuildBankEventLog_Item[TabId].pop_front();
- CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, OldEvent->LogGuid);
- delete OldEvent;
- }
- m_GuildBankEventLog_Item[TabId].push_back(NewEvent);
- }
- CharacterDatabase.PExecute("INSERT INTO guild_bank_eventlog (guildid,LogGuid,LogEntry,TabId,PlayerGuid,ItemOrMoney,ItemStackCount,DestTabId,TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','%u','%u','" I64FMTD "')",
- Id, NewEvent->LogGuid, uint32(NewEvent->LogEntry), uint32(TabId), NewEvent->PlayerGuid, NewEvent->ItemOrMoney, uint32(NewEvent->ItemStackCount), uint32(NewEvent->DestTabId), NewEvent->TimeStamp);
-}
-
-// This will renum guids used at load to prevent always going up until infinit
-void Guild::RenumBankLogs()
-{
- QueryResult *result = CharacterDatabase.PQuery("SELECT Min(LogGuid), Max(LogGuid) FROM guild_bank_eventlog WHERE guildid = %u", Id);
- if(!result)
- return;
-
- Field *fields = result->Fetch();
- CharacterDatabase.PExecute("UPDATE guild_bank_eventlog SET LogGuid=LogGuid-%u+1 WHERE guildid=%u ORDER BY LogGuid %s",fields[0].GetUInt32(), Id, fields[0].GetUInt32()?"ASC":"DESC");
- LogMaxGuid = fields[1].GetUInt32()+1;
- delete result;
-}
-
-bool Guild::AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot , uint32 GUIDLow, uint32 Entry )
-{
- CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid = '%u' AND TabId = '%u'AND SlotId = '%u'", GuildId, BankTab, BankTabSlot);
- CharacterDatabase.PExecute("INSERT INTO guild_bank_item (guildid,TabId,SlotId,item_guid,item_entry) "
- "VALUES ('%u', '%u', '%u', '%u', '%u')", GuildId, BankTab, BankTabSlot, GUIDLow, Entry);
- return true;
-}
-
-void Guild::AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *tab, int slot )
-{
- Item *pItem = tab->Slots[slot];
- uint32 entry = pItem ? pItem->GetEntry() : 0;
-
- data << uint8(slot);
- data << uint32(entry);
- if (entry)
- {
- // random item property id +8
- data << (uint32) pItem->GetItemRandomPropertyId();
- if (pItem->GetItemRandomPropertyId())
- // SuffixFactor +4
- data << (uint32) pItem->GetItemSuffixFactor();
- // +12 // ITEM_FIELD_STACK_COUNT
- data << uint8(pItem->GetCount());
- data << uint32(0); // +16 // Unknown value
- data << uint8(0); // unknown 2.4.2
- if (uint32 Enchant0 = pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT))
- {
- data << uint8(1); // number of enchantments (max 3) why max 3?
- data << uint8(PERM_ENCHANTMENT_SLOT); // enchantment slot (range: 0:2)
- data << uint32(Enchant0); // enchantment id
- }
- else
- data << uint8(0); // no enchantments (0)
- }
-}
-
-Item* Guild::StoreItem(uint8 tabId, GuildItemPosCountVec const& dest, Item* pItem )
-{
- if( !pItem )
- return NULL;
-
- Item* lastItem = pItem;
-
- for(GuildItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); )
- {
- uint8 slot = itr->slot;
- uint32 count = itr->count;
-
- ++itr;
-
- if(itr == dest.end())
- {
- lastItem = _StoreItem(tabId,slot,pItem,count,false);
- break;
- }
-
- lastItem = _StoreItem(tabId,slot,pItem,count,true);
- }
-
- return lastItem;
-}
-
-// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
-Item* Guild::_StoreItem( uint8 tab, uint8 slot, Item *pItem, uint32 count, bool clone )
-{
- if( !pItem )
- return NULL;
-
- sLog.outDebug( "GUILD STORAGE: StoreItem tab = %u, slot = %u, item = %u, count = %u", tab, slot, pItem->GetEntry(), count);
-
- Item* pItem2 = m_TabListMap[tab]->Slots[slot];
-
- if( !pItem2 )
- {
- if(clone)
- pItem = pItem->CloneItem(count);
- else
- pItem->SetCount(count);
-
- if(!pItem)
- return NULL;
-
- m_TabListMap[tab]->Slots[slot] = pItem;
-
- pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, 0);
- pItem->SetUInt64Value(ITEM_FIELD_OWNER, 0);
- AddGBankItemToDB(GetId(), tab, slot, pItem->GetGUIDLow(), pItem->GetEntry());
- pItem->FSetState(ITEM_NEW);
- pItem->SaveToDB(); // not in onventory and can be save standalone
-
- return pItem;
- }
- else
- {
- pItem2->SetCount( pItem2->GetCount() + count );
- pItem2->FSetState(ITEM_CHANGED);
- pItem2->SaveToDB(); // not in onventory and can be save standalone
-
- if(!clone)
- {
- pItem->RemoveFromWorld();
- pItem->DeleteFromDB();
- delete pItem;
- }
-
- return pItem2;
- }
-}
-
-void Guild::RemoveItem(uint8 tab, uint8 slot )
-{
- m_TabListMap[tab]->Slots[slot] = NULL;
- CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid='%u' AND TabId='%u' AND SlotId='%u'",
- GetId(), uint32(tab), uint32(slot));
-}
-
-uint8 Guild::_CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32& count, bool swap, Item* pSrcItem ) const
-{
- Item* pItem2 = m_TabListMap[tab]->Slots[slot];
-
- // ignore move item (this slot will be empty at move)
- if(pItem2==pSrcItem)
- pItem2 = NULL;
-
- uint32 need_space;
-
- // empty specific slot - check item fit to slot
- if( !pItem2 || swap )
- {
- // non empty stack with space
- need_space = pSrcItem->GetMaxStackCount();
- }
- // non empty slot, check item type
- else
- {
- // check item type
- if(pItem2->GetEntry() != pSrcItem->GetEntry())
- return EQUIP_ERR_ITEM_CANT_STACK;
-
- // check free space
- if(pItem2->GetCount() >= pSrcItem->GetMaxStackCount())
- return EQUIP_ERR_ITEM_CANT_STACK;
-
- need_space = pSrcItem->GetMaxStackCount() - pItem2->GetCount();
- }
-
- if(need_space > count)
- need_space = count;
-
- GuildItemPosCount newPosition = GuildItemPosCount(slot,need_space);
- if(!newPosition.isContainedIn(dest))
- {
- dest.push_back(newPosition);
- count -= need_space;
- }
-
- return EQUIP_ERR_OK;
-}
-
-uint8 Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32& count, bool merge, Item* pSrcItem, uint8 skip_slot ) const
-{
- for(uint32 j = 0; j < GUILD_BANK_MAX_SLOTS; j++)
- {
- // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
- if(j==skip_slot)
- continue;
-
- Item* pItem2 = m_TabListMap[tab]->Slots[j];
-
- // ignore move item (this slot will be empty at move)
- if(pItem2==pSrcItem)
- pItem2 = NULL;
-
- // if merge skip empty, if !merge skip non-empty
- if((pItem2!=NULL)!=merge)
- continue;
-
- if( pItem2 )
- {
- if(pItem2->GetEntry() == pSrcItem->GetEntry() && pItem2->GetCount() < pSrcItem->GetMaxStackCount() )
- {
- uint32 need_space = pSrcItem->GetMaxStackCount() - pItem2->GetCount();
- if(need_space > count)
- need_space = count;
-
- GuildItemPosCount newPosition = GuildItemPosCount(j,need_space);
- if(!newPosition.isContainedIn(dest))
- {
- dest.push_back(newPosition);
- count -= need_space;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
- }
- else
- {
- uint32 need_space = pSrcItem->GetMaxStackCount();
- if(need_space > count)
- need_space = count;
-
- GuildItemPosCount newPosition = GuildItemPosCount(j,need_space);
- if(!newPosition.isContainedIn(dest))
- {
- dest.push_back(newPosition);
- count -= need_space;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
- }
- return EQUIP_ERR_OK;
-}
-
-uint8 Guild::CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32 count, Item *pItem, bool swap ) const
-{
- sLog.outDebug( "GUILD STORAGE: CanStoreItem tab = %u, slot = %u, item = %u, count = %u", tab, slot, pItem->GetEntry(), count);
-
- if(count > pItem->GetCount())
- return EQUIP_ERR_COULDNT_SPLIT_ITEMS;
-
- if(pItem->IsSoulBound())
- return EQUIP_ERR_CANT_DROP_SOULBOUND;
-
- // in specific slot
- if( slot != NULL_SLOT )
- {
- uint8 res = _CanStoreItem_InSpecificSlot(tab,slot,dest,count,swap,pItem);
- if(res!=EQUIP_ERR_OK)
- return res;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
-
- // not specific slot or have spece for partly store only in specific slot
-
- // search stack in tab for merge to
- if( pItem->GetMaxStackCount() > 1 )
- {
- uint8 res = _CanStoreItem_InTab(tab,dest,count,true,pItem,slot);
- if(res!=EQUIP_ERR_OK)
- return res;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
-
- // search free slot in bag for place to
- uint8 res = _CanStoreItem_InTab(tab,dest,count,false,pItem,slot);
- if(res!=EQUIP_ERR_OK)
- return res;
-
- if(count==0)
- return EQUIP_ERR_OK;
-
- return EQUIP_ERR_BANK_FULL;
-}
-
-void Guild::SetGuildBankTabText(uint8 TabId, std::string text)
-{
- if (TabId >= GUILD_BANK_MAX_TABS)
- return;
- if (TabId >= m_TabListMap.size())
- return;
- if (!m_TabListMap[TabId])
- return;
-
- if(m_TabListMap[TabId]->Text==text)
- return;
-
- utf8truncate(text,500); // DB and client size limitation
-
- m_TabListMap[TabId]->Text = text;
-
- CharacterDatabase.escape_string(text);
- CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabText='%s' WHERE guildid='%u' AND TabId='%u'", text.c_str(), Id, uint32(TabId));
-}
-
-void Guild::SendGuildBankTabText(WorldSession *session, uint8 TabId)
-{
- if (TabId > GUILD_BANK_MAX_TABS)
- return;
-
- GuildBankTab const *tab = GetBankTab(TabId);
- if (!tab)
- return;
-
- WorldPacket data(MSG_QUERY_GUILD_BANK_TEXT, 1+tab->Text.size()+1);
- data << uint8(TabId);
- data << tab->Text;
- session->SendPacket(&data);
-}
-
-bool GuildItemPosCount::isContainedIn(GuildItemPosCountVec const &vec) const
-{
- for(GuildItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr)
- if(itr->slot == this->slot)
- return true;
-
- return 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 "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "MapManager.h"
+#include "Player.h"
+#include "Opcodes.h"
+#include "ObjectMgr.h"
+#include "Guild.h"
+#include "Chat.h"
+#include "SocialMgr.h"
+#include "Util.h"
+
+Guild::Guild()
+{
+ Id = 0;
+ name = "";
+ leaderGuid = 0;
+ GINFO = MOTD = "";
+ EmblemStyle = 0;
+ EmblemColor = 0;
+ BorderStyle = 0;
+ BorderColor = 0;
+ BackgroundColor = 0;
+
+ CreatedYear = 0;
+ CreatedMonth = 0;
+ CreatedDay = 0;
+}
+
+Guild::~Guild()
+{
+
+}
+
+bool Guild::create(uint64 lGuid, std::string gname)
+{
+ std::string rname;
+ std::string lName;
+
+ if(!objmgr.GetPlayerNameByGUID(lGuid, lName))
+ return false;
+ if(objmgr.GetGuildByName(gname))
+ return false;
+
+ sLog.outDebug("GUILD: creating guild %s to leader: %u", gname.c_str(), GUID_LOPART(lGuid));
+
+ leaderGuid = lGuid;
+ name = gname;
+ GINFO = "";
+ MOTD = "No message set.";
+ guildbank_money = 0;
+ purchased_tabs = 0;
+
+ QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guildid) FROM guild" );
+ if( result )
+ {
+ Id = (*result)[0].GetUInt32()+1;
+ delete result;
+ }
+ else Id = 1;
+
+ // gname already assigned to Guild::name, use it to encode string for DB
+ CharacterDatabase.escape_string(gname);
+
+ std::string dbGINFO = GINFO;
+ std::string dbMOTD = MOTD;
+ CharacterDatabase.escape_string(dbGINFO);
+ CharacterDatabase.escape_string(dbMOTD);
+
+ CharacterDatabase.BeginTransaction();
+ // CharacterDatabase.PExecute("DELETE FROM guild WHERE guildid='%u'", Id); - MAX(guildid)+1 not exist
+ CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid='%u'", Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guildid='%u'", Id);
+ CharacterDatabase.PExecute("INSERT INTO guild (guildid,name,leaderguid,info,motd,createdate,EmblemStyle,EmblemColor,BorderStyle,BorderColor,BackgroundColor,BankMoney) "
+ "VALUES('%u','%s','%u', '%s', '%s', NOW(),'%u','%u','%u','%u','%u','" I64FMTD "')",
+ Id, gname.c_str(), GUID_LOPART(leaderGuid), dbGINFO.c_str(), dbMOTD.c_str(), EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, guildbank_money);
+ CharacterDatabase.CommitTransaction();
+
+ rname = "Guild Master";
+ CreateRank(rname,GR_RIGHT_ALL);
+ rname = "Officer";
+ CreateRank(rname,GR_RIGHT_ALL);
+ rname = "Veteran";
+ CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
+ rname = "Member";
+ CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
+ rname = "Initiate";
+ CreateRank(rname,GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
+
+ return AddMember(lGuid, (uint32)GR_GUILDMASTER);
+}
+
+bool Guild::AddMember(uint64 plGuid, uint32 plRank)
+{
+ if(Player::GetGuildIdFromDB(plGuid) != 0) // player already in guild
+ return false;
+
+ // remove all player signs from another petitions
+ // this will be prevent attempt joining player to many guilds and corrupt guild data integrity
+ Player::RemovePetitionsAndSigns(plGuid, 9);
+
+ // fill player data
+ MemberSlot newmember;
+
+ if(!FillPlayerData(plGuid, &newmember)) // problems with player data collection
+ return false;
+
+ newmember.RankId = plRank;
+ newmember.OFFnote = (std::string)"";
+ newmember.Pnote = (std::string)"";
+ newmember.logout_time = time(NULL);
+ newmember.BankResetTimeMoney = 0; // this will force update at first query
+ for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ newmember.BankResetTimeTab[i] = 0;
+ members[GUID_LOPART(plGuid)] = newmember;
+
+ std::string dbPnote = newmember.Pnote;
+ std::string dbOFFnote = newmember.OFFnote;
+ CharacterDatabase.escape_string(dbPnote);
+ CharacterDatabase.escape_string(dbOFFnote);
+
+ CharacterDatabase.PExecute("INSERT INTO guild_member (guildid,guid,rank,pnote,offnote) VALUES ('%u', '%u', '%u','%s','%s')",
+ Id, GUID_LOPART(plGuid), newmember.RankId, dbPnote.c_str(), dbOFFnote.c_str());
+
+ Player* pl = objmgr.GetPlayer(plGuid);
+ if(pl)
+ {
+ pl->SetInGuild(Id);
+ pl->SetRank(newmember.RankId);
+ pl->SetGuildIdInvited(0);
+ }
+ else
+ {
+ Player::SetUInt32ValueInDB(PLAYER_GUILDID, Id, plGuid);
+ Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, newmember.RankId, plGuid);
+ }
+ return true;
+}
+
+void Guild::SetMOTD(std::string motd)
+{
+ MOTD = motd;
+
+ // motd now can be used for encoding to DB
+ CharacterDatabase.escape_string(motd);
+ CharacterDatabase.PExecute("UPDATE guild SET motd='%s' WHERE guildid='%u'", motd.c_str(), Id);
+}
+
+void Guild::SetGINFO(std::string ginfo)
+{
+ GINFO = ginfo;
+
+ // ginfo now can be used for encoding to DB
+ CharacterDatabase.escape_string(ginfo);
+ CharacterDatabase.PExecute("UPDATE guild SET info='%s' WHERE guildid='%u'", ginfo.c_str(), Id);
+}
+
+bool Guild::LoadGuildFromDB(uint32 GuildId)
+{
+ if(!LoadRanksFromDB(GuildId))
+ return false;
+
+ if(!LoadMembersFromDB(GuildId))
+ return false;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT MAX(TabId) FROM guild_bank_tab WHERE guildid='%u'", GuildId);
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ purchased_tabs = fields[0].GetUInt8()+1; // Because TabId begins at 0
+ delete result;
+ }
+ else
+ purchased_tabs = 0;
+
+ LoadBankRightsFromDB(GuildId); // Must be after LoadRanksFromDB because it populates rank struct
+
+ // 0 1 2 3 4 5 6
+ result = CharacterDatabase.PQuery("SELECT guildid, name, leaderguid, EmblemStyle, EmblemColor, BorderStyle, BorderColor,"
+ // 7 8 9 10 11
+ "BackgroundColor, info, motd, createdate, BankMoney FROM guild WHERE guildid = '%u'", GuildId);
+
+ if(!result)
+ return false;
+
+ Field *fields = result->Fetch();
+
+ Id = fields[0].GetUInt32();
+ name = fields[1].GetCppString();
+ leaderGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER);
+
+ EmblemStyle = fields[3].GetUInt32();
+ EmblemColor = fields[4].GetUInt32();
+ BorderStyle = fields[5].GetUInt32();
+ BorderColor = fields[6].GetUInt32();
+ BackgroundColor = fields[7].GetUInt32();
+ GINFO = fields[8].GetCppString();
+ MOTD = fields[9].GetCppString();
+ uint64 time = fields[10].GetUInt64(); //datetime is uint64 type ... YYYYmmdd:hh:mm:ss
+ guildbank_money = fields[11].GetUInt64();
+
+ delete result;
+
+ uint64 dTime = time /1000000;
+ CreatedDay = dTime%100;
+ CreatedMonth = (dTime/100)%100;
+ CreatedYear = (dTime/10000)%10000;
+
+ // If the leader does not exist attempt to promote another member
+ if(!objmgr.GetPlayerAccountIdByGUID(leaderGuid ))
+ {
+ DelMember(leaderGuid);
+
+ // check no members case (disbanded)
+ if(members.empty())
+ return false;
+ }
+
+ sLog.outDebug("Guild %u Creation time Loaded day: %u, month: %u, year: %u", GuildId, CreatedDay, CreatedMonth, CreatedYear);
+ m_bankloaded = false;
+ m_eventlogloaded = false;
+ m_onlinemembers = 0;
+ RenumBankLogs();
+ RenumGuildEventlog();
+ return true;
+}
+
+bool Guild::LoadRanksFromDB(uint32 GuildId)
+{
+ Field *fields;
+ QueryResult *result = CharacterDatabase.PQuery("SELECT rname,rights,BankMoneyPerDay,rid FROM guild_rank WHERE guildid = '%u' ORDER BY rid ASC", GuildId);
+
+ if(!result)
+ return false;
+
+ bool broken_ranks = false;
+
+ do
+ {
+ fields = result->Fetch();
+
+ std::string rankName = fields[0].GetCppString();
+ uint32 rankRights = fields[1].GetUInt32();
+ uint32 rankMoney = fields[2].GetUInt32();
+ uint32 rankRID = fields[3].GetUInt32();
+
+ if(rankRID != m_ranks.size()+1) // guild_rank.rid always store rank+1
+ broken_ranks = true;
+
+ if(m_ranks.size()==GR_GUILDMASTER) // prevent loss leader rights
+ rankRights |= GR_RIGHT_ALL;
+
+ AddRank(rankName,rankRights,rankMoney);
+ }while( result->NextRow() );
+ delete result;
+
+ if(m_ranks.size()==0) // empty rank table?
+ {
+ AddRank("Guild Master",GR_RIGHT_ALL,0);
+ broken_ranks = true;
+ }
+
+ // guild_rank have wrong numbered ranks, repair
+ if(broken_ranks)
+ {
+ sLog.outError("Guild %u have broken `guild_rank` data, repairing...",GuildId);
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid='%u'", GuildId);
+ for(size_t i =0; i < m_ranks.size(); ++i)
+ {
+ // guild_rank.rid always store rank+1
+ std::string name = m_ranks[i].name;
+ uint32 rights = m_ranks[i].rights;
+ CharacterDatabase.escape_string(name);
+ CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", GuildId, i+1, name.c_str(), rights);
+ }
+ CharacterDatabase.CommitTransaction();
+ }
+
+ return true;
+}
+
+bool Guild::LoadMembersFromDB(uint32 GuildId)
+{
+ // 0 1 2 3 4 5
+ QueryResult *result = CharacterDatabase.PQuery("SELECT guild_member.guid,rank, pnote, offnote, BankResetTimeMoney,BankRemMoney,"
+ // 6 7 8 9 10 11
+ "BankResetTimeTab0, BankRemSlotsTab0, BankResetTimeTab1, BankRemSlotsTab1, BankResetTimeTab2, BankRemSlotsTab2,"
+ // 12 13 14 15 16 17
+ "BankResetTimeTab3, BankRemSlotsTab3, BankResetTimeTab4, BankRemSlotsTab4, BankResetTimeTab5, BankRemSlotsTab5,"
+ // 18
+ "logout_time FROM guild_member LEFT JOIN characters ON characters.guid = guild_member.guid WHERE guildid = '%u'", GuildId);
+
+ if(!result)
+ return false;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ MemberSlot newmember;
+ newmember.RankId = fields[1].GetUInt32();
+ uint64 guid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+
+ // Player does not exist
+ if(!FillPlayerData(guid, &newmember))
+ continue;
+
+ newmember.Pnote = fields[2].GetCppString();
+ newmember.OFFnote = fields[3].GetCppString();
+ newmember.BankResetTimeMoney = fields[4].GetUInt32();
+ newmember.BankRemMoney = fields[5].GetUInt32();
+ for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ {
+ newmember.BankResetTimeTab[i] = fields[6+(2*i)].GetUInt32();
+ newmember.BankRemSlotsTab[i] = fields[7+(2*i)].GetUInt32();
+ }
+ newmember.logout_time = fields[18].GetUInt64();
+ members[GUID_LOPART(guid)] = newmember;
+
+ }while( result->NextRow() );
+ delete result;
+
+ if(members.empty())
+ return false;
+
+ return true;
+}
+
+bool Guild::FillPlayerData(uint64 guid, MemberSlot* memslot)
+{
+ std::string plName;
+ uint32 plLevel;
+ uint32 plClass;
+ uint32 plZone;
+
+ Player* pl = objmgr.GetPlayer(guid);
+ if(pl)
+ {
+ plName = pl->GetName();
+ plLevel = pl->getLevel();
+ plClass = pl->getClass();
+ plZone = pl->GetZoneId();
+ }
+ else
+ {
+ if(!objmgr.GetPlayerNameByGUID(guid, plName)) // player doesn't exist
+ return false;
+
+ plLevel = Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL, guid);
+ if(plLevel<1||plLevel>255) // can be at broken `data` field
+ {
+ sLog.outError("Player (GUID: %u) has a broken data in field `characters`.`data`.",GUID_LOPART(guid));
+ return false;
+ }
+ plZone = Player::GetZoneIdFromDB(guid);
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT class FROM characters WHERE guid='%u'", GUID_LOPART(guid));
+ if(!result)
+ return false;
+ plClass = (*result)[0].GetUInt32();
+ if(plClass<CLASS_WARRIOR||plClass>=MAX_CLASSES) // can be at broken `class` field
+ {
+ sLog.outError("Player (GUID: %u) has a broken data in field `characters`.`class`.",GUID_LOPART(guid));
+ return false;
+ }
+
+ delete result;
+ }
+
+ memslot->name = plName;
+ memslot->level = plLevel;
+ memslot->Class = plClass;
+ memslot->zoneId = plZone;
+
+ return(true);
+}
+
+void Guild::LoadPlayerStatsByGuid(uint64 guid)
+{
+ MemberList::iterator itr = members.find(GUID_LOPART(guid));
+ if (itr == members.end() )
+ return;
+
+ Player *pl = ObjectAccessor::FindPlayer(guid);
+ if(!pl)
+ return;
+ itr->second.name = pl->GetName();
+ itr->second.level = pl->getLevel();
+ itr->second.Class = pl->getClass();
+}
+
+void Guild::SetLeader(uint64 guid)
+{
+ leaderGuid = guid;
+ this->ChangeRank(guid, GR_GUILDMASTER);
+
+ CharacterDatabase.PExecute("UPDATE guild SET leaderguid='%u' WHERE guildid='%u'", GUID_LOPART(guid), Id);
+}
+
+void Guild::DelMember(uint64 guid, bool isDisbanding)
+{
+ if(this->leaderGuid == guid && !isDisbanding)
+ {
+ std::ostringstream ss;
+ ss<<"SELECT guid FROM guild_member WHERE guildid='"<<Id<<"' AND guid!='"<<this->leaderGuid<<"' ORDER BY rank ASC LIMIT 1";
+ QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
+ if( result )
+ {
+ uint64 newLeaderGUID;
+ Player *newLeader;
+ std::string newLeaderName, oldLeaderName;
+
+ newLeaderGUID = (*result)[0].GetUInt64();
+ delete result;
+
+ this->SetLeader(newLeaderGUID);
+
+ newLeader = objmgr.GetPlayer(newLeaderGUID);
+ if(newLeader)
+ {
+ newLeader->SetRank(GR_GUILDMASTER);
+ newLeaderName = newLeader->GetName();
+ }
+ else
+ {
+ Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, GR_GUILDMASTER, newLeaderGUID);
+ objmgr.GetPlayerNameByGUID(newLeaderGUID, newLeaderName);
+ }
+
+ // when leader non-exist (at guild load with deleted leader only) not send broadcasts
+ if(objmgr.GetPlayerNameByGUID(guid, oldLeaderName))
+ {
+ WorldPacket data(SMSG_GUILD_EVENT, (1+1+oldLeaderName.size()+1+newLeaderName.size()+1));
+ data << (uint8)GE_LEADER_CHANGED;
+ data << (uint8)2;
+ data << oldLeaderName;
+ data << newLeaderName;
+ this->BroadcastPacket(&data);
+
+ data.Initialize(SMSG_GUILD_EVENT, (1+1+oldLeaderName.size()+1));
+ data << (uint8)GE_LEFT;
+ data << (uint8)1;
+ data << oldLeaderName;
+ this->BroadcastPacket(&data);
+ }
+
+ sLog.outDebug( "WORLD: Sent (SMSG_GUILD_EVENT)" );
+ }
+ else
+ {
+ this->Disband();
+ return;
+ }
+ }
+
+ members.erase(GUID_LOPART(guid));
+
+ Player *player = objmgr.GetPlayer(guid);
+ if(player)
+ {
+ player->SetInGuild(0);
+ player->SetRank(0);
+ }
+ else
+ {
+ Player::SetUInt32ValueInDB(PLAYER_GUILDID, 0, guid);
+ Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, GR_GUILDMASTER, guid);
+ }
+
+ CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guid = '%u'", GUID_LOPART(guid));
+}
+
+void Guild::ChangeRank(uint64 guid, uint32 newRank)
+{
+ MemberList::iterator itr = members.find(GUID_LOPART(guid));
+ if( itr != members.end() )
+ itr->second.RankId = newRank;
+
+ Player *player = objmgr.GetPlayer(guid);
+ if(player)
+ player->SetRank(newRank);
+ else
+ Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, newRank, guid);
+
+ CharacterDatabase.PExecute( "UPDATE guild_member SET rank='%u' WHERE guid='%u'", newRank, GUID_LOPART(guid) );
+}
+
+void Guild::SetPNOTE(uint64 guid,std::string pnote)
+{
+ MemberList::iterator itr = members.find(GUID_LOPART(guid));
+ if( itr == members.end() )
+ return;
+
+ itr->second.Pnote = pnote;
+
+ // pnote now can be used for encoding to DB
+ CharacterDatabase.escape_string(pnote);
+ CharacterDatabase.PExecute("UPDATE guild_member SET pnote = '%s' WHERE guid = '%u'", pnote.c_str(), itr->first);
+}
+
+void Guild::SetOFFNOTE(uint64 guid,std::string offnote)
+{
+ MemberList::iterator itr = members.find(GUID_LOPART(guid));
+ if( itr == members.end() )
+ return;
+ itr->second.OFFnote = offnote;
+ // offnote now can be used for encoding to DB
+ CharacterDatabase.escape_string(offnote);
+ CharacterDatabase.PExecute("UPDATE guild_member SET offnote = '%s' WHERE guid = '%u'", offnote.c_str(), itr->first);
+}
+
+void Guild::BroadcastToGuild(WorldSession *session, std::string msg, uint32 language)
+{
+ if (session && session->GetPlayer() && HasRankRight(session->GetPlayer()->GetRank(),GR_RIGHT_GCHATSPEAK))
+ {
+ WorldPacket data;
+ ChatHandler(session).FillMessageData(&data, CHAT_MSG_GUILD, language, 0, msg.c_str());
+
+ for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ Player *pl = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+
+ if (pl && pl->GetSession() && HasRankRight(pl->GetRank(),GR_RIGHT_GCHATLISTEN) && !pl->GetSocial()->HasIgnore(session->GetPlayer()->GetGUIDLow()) )
+ pl->GetSession()->SendPacket(&data);
+ }
+ }
+}
+
+void Guild::BroadcastToOfficers(WorldSession *session, std::string msg, uint32 language)
+{
+ if (session && session->GetPlayer() && HasRankRight(session->GetPlayer()->GetRank(),GR_RIGHT_OFFCHATSPEAK))
+ {
+ for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, session, CHAT_MSG_OFFICER, language, NULL, 0, msg.c_str(),NULL);
+
+ Player *pl = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+
+ if (pl && pl->GetSession() && HasRankRight(pl->GetRank(),GR_RIGHT_OFFCHATLISTEN) && !pl->GetSocial()->HasIgnore(session->GetPlayer()->GetGUIDLow()))
+ pl->GetSession()->SendPacket(&data);
+ }
+ }
+}
+
+void Guild::BroadcastPacket(WorldPacket *packet)
+{
+ for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+ if(player)
+ player->GetSession()->SendPacket(packet);
+ }
+}
+
+void Guild::BroadcastPacketToRank(WorldPacket *packet, uint32 rankId)
+{
+ for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ if (itr->second.RankId == rankId)
+ {
+ Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+ if(player)
+ player->GetSession()->SendPacket(packet);
+ }
+ }
+}
+
+void Guild::CreateRank(std::string name_,uint32 rights)
+{
+ if(m_ranks.size() >= GUILD_MAX_RANKS)
+ return;
+
+ AddRank(name_,rights,0);
+
+ for (int i = 0; i < purchased_tabs; ++i)
+ {
+ CreateBankRightForTab(m_ranks.size()-1, uint8(i));
+ }
+
+ // guild_rank.rid always store rank+1 value
+
+ // name now can be used for encoding to DB
+ CharacterDatabase.escape_string(name_);
+ CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", Id, m_ranks.size(), name_.c_str(), rights );
+}
+
+void Guild::AddRank(std::string name_,uint32 rights, uint32 money)
+{
+ m_ranks.push_back(RankInfo(name_,rights,money));
+}
+
+void Guild::DelRank()
+{
+ if(m_ranks.empty())
+ return;
+
+ // guild_rank.rid always store rank+1 value
+ uint32 rank = m_ranks.size()-1;
+ CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE rid>='%u' AND guildid='%u'", (rank+1), Id);
+
+ m_ranks.pop_back();
+}
+
+std::string Guild::GetRankName(uint32 rankId)
+{
+ if(rankId >= m_ranks.size())
+ return "<unknown>";
+
+ return m_ranks[rankId].name;
+}
+
+uint32 Guild::GetRankRights(uint32 rankId)
+{
+ if(rankId >= m_ranks.size())
+ return 0;
+
+ return m_ranks[rankId].rights;
+}
+
+void Guild::SetRankName(uint32 rankId, std::string name_)
+{
+ if(rankId >= m_ranks.size())
+ return;
+
+ m_ranks[rankId].name = name_;
+
+ // name now can be used for encoding to DB
+ CharacterDatabase.escape_string(name_);
+ CharacterDatabase.PExecute("UPDATE guild_rank SET rname='%s' WHERE rid='%u' AND guildid='%u'", name_.c_str(), (rankId+1), Id);
+}
+
+void Guild::SetRankRights(uint32 rankId, uint32 rights)
+{
+ if(rankId >= m_ranks.size())
+ return;
+
+ m_ranks[rankId].rights = rights;
+
+ CharacterDatabase.PExecute("UPDATE guild_rank SET rights='%u' WHERE rid='%u' AND guildid='%u'", rights, (rankId+1), Id);
+}
+
+void Guild::Disband()
+{
+ WorldPacket data(SMSG_GUILD_EVENT, 1);
+ data << (uint8)GE_DISBANDED;
+ this->BroadcastPacket(&data);
+
+ while (!members.empty())
+ {
+ MemberList::iterator itr = members.begin();
+ DelMember(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER), true);
+ }
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM guild WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid = '%u'",Id);
+ CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid = '%u'",Id);
+ CharacterDatabase.CommitTransaction();
+ objmgr.RemoveGuild(this);
+}
+
+void Guild::Roster(WorldSession *session)
+{
+ // we can only guess size
+ WorldPacket data(SMSG_GUILD_ROSTER, (4+MOTD.length()+1+GINFO.length()+1+4+m_ranks.size()*(4+4+GUILD_BANK_MAX_TABS*(4+4))+members.size()*50));
+ data << (uint32)members.size();
+ data << MOTD;
+ data << GINFO;
+
+ data << (uint32)m_ranks.size();
+ for (RankList::iterator ritr = m_ranks.begin(); ritr != m_ranks.end();++ritr)
+ {
+ data << (uint32)ritr->rights;
+ data << (uint32)ritr->BankMoneyPerDay; // count of: withdraw gold(gold/day) Note: in game set gold, in packet set bronze.
+ for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ {
+ data << (uint32)ritr->TabRight[i]; // for TAB_i rights: view tabs = 0x01, deposit items =0x02
+ data << (uint32)ritr->TabSlotPerDay[i]; // for TAB_i count of: withdraw items(stack/day)
+ }
+ }
+ for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ if (Player *pl = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)))
+ {
+ data << (uint64)pl->GetGUID();
+ data << (uint8)1;
+ data << (std::string)pl->GetName();
+ data << (uint32)itr->second.RankId;
+ data << (uint8)pl->getLevel();
+ data << (uint8)pl->getClass();
+ data << (uint8)0; // new 2.4.0
+ data << (uint32)pl->GetZoneId();
+ data << itr->second.Pnote;
+ data << itr->second.OFFnote;
+ }
+ else
+ {
+ data << uint64(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+ data << (uint8)0;
+ data << itr->second.name;
+ data << (uint32)itr->second.RankId;
+ data << (uint8)itr->second.level;
+ data << (uint8)itr->second.Class;
+ data << (uint8)0; // new 2.4.0
+ data << (uint32)itr->second.zoneId;
+ data << (float(time(NULL)-itr->second.logout_time) / DAY);
+ data << itr->second.Pnote;
+ data << itr->second.OFFnote;
+ }
+ }
+ session->SendPacket(&data);;
+ sLog.outDebug( "WORLD: Sent (SMSG_GUILD_ROSTER)" );
+}
+
+void Guild::Query(WorldSession *session)
+{
+ WorldPacket data(SMSG_GUILD_QUERY_RESPONSE, (8*32+200));// we can only guess size
+
+ data << Id;
+ data << name;
+ RankList::iterator itr;
+ for (size_t i = 0 ; i < 10; ++i) // show always 10 ranks
+ {
+ if(i < m_ranks.size())
+ data << m_ranks[i].name;
+ else
+ data << (uint8)0; // null string
+ }
+
+ data << uint32(EmblemStyle);
+ data << uint32(EmblemColor);
+ data << uint32(BorderStyle);
+ data << uint32(BorderColor);
+ data << uint32(BackgroundColor);
+
+ session->SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent (SMSG_GUILD_QUERY_RESPONSE)" );
+}
+
+void Guild::SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor, uint32 backgroundColor)
+{
+ this->EmblemStyle = emblemStyle;
+ this->EmblemColor = emblemColor;
+ this->BorderStyle = borderStyle;
+ this->BorderColor = borderColor;
+ this->BackgroundColor = backgroundColor;
+
+ CharacterDatabase.PExecute("UPDATE guild SET EmblemStyle=%u, EmblemColor=%u, BorderStyle=%u, BorderColor=%u, BackgroundColor=%u WHERE guildid = %u", EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, Id);
+}
+
+void Guild::UpdateLogoutTime(uint64 guid)
+{
+ MemberList::iterator itr = members.find(GUID_LOPART(guid));
+ if (itr == members.end() )
+ return;
+
+ itr->second.logout_time = time(NULL);
+
+ if (m_onlinemembers > 0)
+ --m_onlinemembers;
+ else
+ {
+ UnloadGuildBank();
+ UnloadGuildEventlog();
+ }
+}
+
+// *************************************************
+// Guild Eventlog part
+// *************************************************
+// Display guild eventlog
+void Guild::DisplayGuildEventlog(WorldSession *session)
+{
+ // Load guild eventlog, if not already done
+ if (!m_eventlogloaded)
+ LoadGuildEventLogFromDB();
+
+ // Sending result
+ WorldPacket data(MSG_GUILD_EVENT_LOG_QUERY, 0);
+ // count, max count == 100
+ data << uint8(m_GuildEventlog.size());
+ for (GuildEventlog::const_iterator itr = m_GuildEventlog.begin(); itr != m_GuildEventlog.end(); ++itr)
+ {
+ // Event type
+ data << uint8((*itr)->EventType);
+ // Player 1
+ data << uint64((*itr)->PlayerGuid1);
+ // Player 2 not for left/join guild events
+ if( (*itr)->EventType != GUILD_EVENT_LOG_JOIN_GUILD && (*itr)->EventType != GUILD_EVENT_LOG_LEAVE_GUILD )
+ data << uint64((*itr)->PlayerGuid2);
+ // New Rank - only for promote/demote guild events
+ if( (*itr)->EventType == GUILD_EVENT_LOG_PROMOTE_PLAYER || (*itr)->EventType == GUILD_EVENT_LOG_DEMOTE_PLAYER )
+ data << uint8((*itr)->NewRank);
+ // Event timestamp
+ data << uint32(time(NULL)-(*itr)->TimeStamp);
+ }
+ session->SendPacket(&data);
+ sLog.outDebug("WORLD: Sent (MSG_GUILD_EVENT_LOG_QUERY)");
+}
+
+// Load guild eventlog from DB
+void Guild::LoadGuildEventLogFromDB()
+{
+ // Return if already loaded
+ if (m_eventlogloaded)
+ return;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp FROM guild_eventlog WHERE guildid=%u ORDER BY LogGuid DESC LIMIT %u", Id, GUILD_EVENTLOG_MAX_ENTRIES);
+ if(!result)
+ return;
+ do
+ {
+ Field *fields = result->Fetch();
+ GuildEventlogEntry *NewEvent = new GuildEventlogEntry;
+ // Fill entry
+ NewEvent->LogGuid = fields[0].GetUInt32();
+ NewEvent->EventType = fields[1].GetUInt8();
+ NewEvent->PlayerGuid1 = fields[2].GetUInt32();
+ NewEvent->PlayerGuid2 = fields[3].GetUInt32();
+ NewEvent->NewRank = fields[4].GetUInt8();
+ NewEvent->TimeStamp = fields[5].GetUInt64();
+ // Add entry to map
+ m_GuildEventlog.push_front(NewEvent);
+
+ } while( result->NextRow() );
+ delete result;
+
+ // Check lists size in case to many event entries in db
+ // This cases can happen only if a crash occured somewhere and table has too many log entries
+ if (!m_GuildEventlog.empty())
+ {
+ CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid=%u AND LogGuid < %u", Id, m_GuildEventlog.front()->LogGuid);
+ }
+ m_eventlogloaded = true;
+}
+
+// Unload guild eventlog
+void Guild::UnloadGuildEventlog()
+{
+ if (!m_eventlogloaded)
+ return;
+ GuildEventlogEntry *EventLogEntry;
+ if( !m_GuildEventlog.empty() )
+ {
+ do
+ {
+ EventLogEntry = *(m_GuildEventlog.begin());
+ m_GuildEventlog.pop_front();
+ delete EventLogEntry;
+ }while( !m_GuildEventlog.empty() );
+ }
+ m_eventlogloaded = false;
+}
+
+// This will renum guids used at load to prevent always going up until infinit
+void Guild::RenumGuildEventlog()
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT Min(LogGuid), Max(LogGuid) FROM guild_eventlog WHERE guildid = %u", Id);
+ if(!result)
+ return;
+
+ Field *fields = result->Fetch();
+ CharacterDatabase.PExecute("UPDATE guild_eventlog SET LogGuid=LogGuid-%u+1 WHERE guildid=%u ORDER BY LogGuid %s",fields[0].GetUInt32(), Id, fields[0].GetUInt32()?"ASC":"DESC");
+ GuildEventlogMaxGuid = fields[1].GetUInt32()+1;
+ delete result;
+}
+
+// Add entry to guild eventlog
+void Guild::LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid2, uint8 NewRank)
+{
+ GuildEventlogEntry *NewEvent = new GuildEventlogEntry;
+ // Fill entry
+ NewEvent->LogGuid = GuildEventlogMaxGuid++;
+ NewEvent->EventType = EventType;
+ NewEvent->PlayerGuid1 = PlayerGuid1;
+ NewEvent->PlayerGuid2 = PlayerGuid2;
+ NewEvent->NewRank = NewRank;
+ NewEvent->TimeStamp = uint32(time(NULL));
+ // Check max entry limit and delete from db if needed
+ if (m_GuildEventlog.size() > GUILD_EVENTLOG_MAX_ENTRIES)
+ {
+ GuildEventlogEntry *OldEvent = *(m_GuildEventlog.begin());
+ m_GuildEventlog.pop_front();
+ CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, OldEvent->LogGuid);
+ delete OldEvent;
+ }
+ // Add entry to map
+ m_GuildEventlog.push_back(NewEvent);
+ // Add new eventlog entry into DB
+ CharacterDatabase.PExecute("INSERT INTO guild_eventlog (guildid, LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','" I64FMTD "')",
+ Id, NewEvent->LogGuid, uint32(NewEvent->EventType), NewEvent->PlayerGuid1, NewEvent->PlayerGuid2, uint32(NewEvent->NewRank), NewEvent->TimeStamp);
+}
+
+// *************************************************
+// Guild Bank part
+// *************************************************
+// Bank content related
+void Guild::DisplayGuildBankContent(WorldSession *session, uint8 TabId)
+{
+ WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
+
+ GuildBankTab const* tab = GetBankTab(TabId);
+ if (!tab)
+ return;
+
+ if(!IsMemberHaveRights(session->GetPlayer()->GetGUIDLow(),TabId,GUILD_BANK_RIGHT_VIEW_TAB))
+ return;
+
+ data << uint64(GetGuildBankMoney());
+ data << uint8(TabId);
+ // remaining slots for today
+ data << uint32(GetMemberSlotWithdrawRem(session->GetPlayer()->GetGUIDLow(), TabId));
+ data << uint8(0); // Tell client this is a tab content packet
+
+ data << uint8(GUILD_BANK_MAX_SLOTS);
+
+ for (int i=0; i<GUILD_BANK_MAX_SLOTS; ++i)
+ AppendDisplayGuildBankSlot(data, tab, i);
+
+ session->SendPacket(&data);
+
+ sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
+}
+
+void Guild::DisplayGuildBankMoneyUpdate()
+{
+ WorldPacket data(SMSG_GUILD_BANK_LIST, 8+1+4+1+1);
+
+ data << uint64(GetGuildBankMoney());
+ data << uint8(0);
+ // remaining slots for today
+
+ size_t rempos = data.wpos();
+ data << uint32(0); // will be filled later
+ data << uint8(0); // Tell client this is a tab content packet
+
+ data << uint8(0); // not send items
+
+ BroadcastPacket(&data);
+
+ sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
+}
+
+void Guild::DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2)
+{
+ GuildBankTab const* tab = GetBankTab(TabId);
+ if (!tab)
+ return;
+
+ WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
+
+ data << uint64(GetGuildBankMoney());
+ data << uint8(TabId);
+ // remaining slots for today
+
+ size_t rempos = data.wpos();
+ data << uint32(0); // will be filled later
+ data << uint8(0); // Tell client this is a tab content packet
+
+ if(slot2==-1) // single item in slot1
+ {
+ data << uint8(1);
+
+ AppendDisplayGuildBankSlot(data, tab, slot1);
+ }
+ else // 2 items (in slot1 and slot2)
+ {
+ data << uint8(2);
+
+ if(slot1 > slot2)
+ std::swap(slot1,slot2);
+
+ AppendDisplayGuildBankSlot(data, tab, slot1);
+ AppendDisplayGuildBankSlot(data, tab, slot2);
+ }
+
+ for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+ if(!player)
+ continue;
+
+ if(!IsMemberHaveRights(itr->first,TabId,GUILD_BANK_RIGHT_VIEW_TAB))
+ continue;
+
+ data.put<uint32>(rempos,uint32(GetMemberSlotWithdrawRem(player->GetGUIDLow(), TabId)));
+
+ player->GetSession()->SendPacket(&data);
+ }
+
+ sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
+}
+
+void Guild::DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots)
+{
+ GuildBankTab const* tab = GetBankTab(TabId);
+ if (!tab)
+ return;
+
+ WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
+
+ data << uint64(GetGuildBankMoney());
+ data << uint8(TabId);
+ // remaining slots for today
+
+ size_t rempos = data.wpos();
+ data << uint32(0); // will be filled later
+ data << uint8(0); // Tell client this is a tab content packet
+
+ data << uint8(slots.size()); // updates count
+
+ for(GuildItemPosCountVec::const_iterator itr = slots.begin(); itr != slots.end(); ++itr)
+ AppendDisplayGuildBankSlot(data, tab, itr->slot);
+
+ for(MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ {
+ Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
+ if(!player)
+ continue;
+
+ if(!IsMemberHaveRights(itr->first,TabId,GUILD_BANK_RIGHT_VIEW_TAB))
+ continue;
+
+ data.put<uint32>(rempos,uint32(GetMemberSlotWithdrawRem(player->GetGUIDLow(), TabId)));
+
+ player->GetSession()->SendPacket(&data);
+ }
+
+ sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
+}
+
+Item* Guild::GetItem(uint8 TabId, uint8 SlotId)
+{
+ if (TabId >= m_TabListMap.size() || SlotId >= GUILD_BANK_MAX_SLOTS)
+ return NULL;
+ return m_TabListMap[TabId]->Slots[SlotId];
+}
+
+// *************************************************
+// Tab related
+
+void Guild::DisplayGuildBankTabsInfo(WorldSession *session)
+{
+ // Time to load bank if not already done
+ if (!m_bankloaded)
+ LoadGuildBankFromDB();
+
+ WorldPacket data(SMSG_GUILD_BANK_LIST, 500);
+
+ data << uint64(GetGuildBankMoney());
+ data << uint8(0); // TabInfo packet must be for TabId 0
+ data << uint32(0xFFFFFFFF); // bit 9 must be set for this packet to work
+ data << uint8(1); // Tell Client this is a TabInfo packet
+
+ data << uint8(purchased_tabs); // here is the number of tabs
+
+ for(int i = 0; i < purchased_tabs; ++i)
+ {
+ data << m_TabListMap[i]->Name.c_str();
+ data << m_TabListMap[i]->Icon.c_str();
+ }
+ data << uint8(0); // Do not send tab content
+ session->SendPacket(&data);
+
+ sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
+}
+
+void Guild::CreateNewBankTab()
+{
+ if (purchased_tabs >= GUILD_BANK_MAX_TABS)
+ return;
+
+ ++purchased_tabs;
+
+ GuildBankTab* AnotherTab = new GuildBankTab;
+ memset(AnotherTab->Slots, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*));
+ m_TabListMap.resize(purchased_tabs);
+ m_TabListMap[purchased_tabs-1] = AnotherTab;
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid='%u' AND TabId='%u'", Id, uint32(purchased_tabs-1));
+ CharacterDatabase.PExecute("INSERT INTO guild_bank_tab (guildid,TabId) VALUES ('%u','%u')", Id, uint32(purchased_tabs-1));
+ CharacterDatabase.CommitTransaction();
+}
+
+void Guild::SetGuildBankTabInfo(uint8 TabId, std::string Name, std::string Icon)
+{
+ if (TabId >= GUILD_BANK_MAX_TABS)
+ return;
+ if (TabId >= m_TabListMap.size())
+ return;
+
+ if (!m_TabListMap[TabId])
+ return;
+
+ if(m_TabListMap[TabId]->Name == Name && m_TabListMap[TabId]->Icon == Icon)
+ return;
+
+ m_TabListMap[TabId]->Name = Name;
+ m_TabListMap[TabId]->Icon = Icon;
+
+ CharacterDatabase.escape_string(Name);
+ CharacterDatabase.escape_string(Icon);
+ CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabName='%s',TabIcon='%s' WHERE guildid='%u' AND TabId='%u'", Name.c_str(), Icon.c_str(), Id, uint32(TabId));
+}
+
+void Guild::CreateBankRightForTab(uint32 rankId, uint8 TabId)
+{
+ sLog.outDebug("CreateBankRightForTab. rank: %u, TabId: %u", rankId, uint32(TabId));
+ if (rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS)
+ return;
+
+ m_ranks[rankId].TabRight[TabId]=0;
+ m_ranks[rankId].TabSlotPerDay[TabId]=0;
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u' AND TabId = '%u' AND rid = '%u'", Id, uint32(TabId), rankId);
+ CharacterDatabase.PExecute("INSERT INTO guild_bank_right (guildid,TabId,rid) VALUES ('%u','%u','%u')", Id, uint32(TabId), rankId);
+ CharacterDatabase.CommitTransaction();
+}
+
+uint32 Guild::GetBankRights(uint32 rankId, uint8 TabId) const
+{
+ if(rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS)
+ return 0;
+
+ return m_ranks[rankId].TabRight[TabId];
+}
+
+// *************************************************
+// Guild bank loading/unloading related
+
+// This load should be called when the bank is first accessed by a guild member
+void Guild::LoadGuildBankFromDB()
+{
+ if (m_bankloaded)
+ return;
+
+ m_bankloaded = true;
+ LoadGuildBankEventLogFromDB();
+
+ // 0 1 2 3
+ QueryResult *result = CharacterDatabase.PQuery("SELECT TabId, TabName, TabIcon, TabText FROM guild_bank_tab WHERE guildid='%u' ORDER BY TabId", Id);
+ if(!result)
+ {
+ purchased_tabs = 0;
+ return;
+ }
+
+ m_TabListMap.resize(purchased_tabs);
+ do
+ {
+ Field *fields = result->Fetch();
+ uint8 TabId = fields[0].GetUInt8();
+
+ GuildBankTab *NewTab = new GuildBankTab;
+ memset(NewTab->Slots, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*));
+
+ NewTab->Name = fields[1].GetCppString();
+ NewTab->Icon = fields[2].GetCppString();
+ NewTab->Text = fields[3].GetCppString();
+
+ m_TabListMap[TabId] = NewTab;
+ }while( result->NextRow() );
+
+ delete result;
+
+ // 0 1 2 3
+ result = CharacterDatabase.PQuery("SELECT TabId, SlotId, item_guid, item_entry FROM guild_bank_item WHERE guildid='%u' ORDER BY TabId", Id);
+ if(!result)
+ return;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ uint8 TabId = fields[0].GetUInt8();
+ uint8 SlotId = fields[1].GetUInt8();
+ uint32 ItemGuid = fields[2].GetUInt32();
+ uint32 ItemEntry = fields[3].GetUInt32();
+
+ if (TabId >= purchased_tabs || TabId >= GUILD_BANK_MAX_TABS)
+ {
+ sLog.outError( "Guild::LoadGuildBankFromDB: Invalid tab for item (GUID: %u id: #%u) in guild bank, skipped.", ItemGuid,ItemEntry);
+ continue;
+ }
+
+ if (SlotId >= GUILD_BANK_MAX_SLOTS)
+ {
+ sLog.outError( "Guild::LoadGuildBankFromDB: Invalid slot for item (GUID: %u id: #%u) in guild bank, skipped.", ItemGuid,ItemEntry);
+ continue;
+ }
+
+ ItemPrototype const *proto = objmgr.GetItemPrototype(ItemEntry);
+
+ if(!proto)
+ {
+ sLog.outError( "Guild::LoadGuildBankFromDB: Unknown item (GUID: %u id: #%u) in guild bank, skipped.", ItemGuid,ItemEntry);
+ continue;
+ }
+
+ Item *pItem = NewItemOrBag(proto);
+ if(!pItem->LoadFromDB(ItemGuid, 0))
+ {
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid='%u' AND TabId='%u' AND SlotId='%u'", Id, uint32(TabId), uint32(SlotId));
+ sLog.outError("Item GUID %u not found in item_instance, deleting from Guild Bank!", ItemGuid);
+ delete pItem;
+ continue;
+ }
+
+ pItem->AddToWorld();
+ m_TabListMap[TabId]->Slots[SlotId] = pItem;
+ }while( result->NextRow() );
+
+ delete result;
+}
+
+// This unload should be called when the last member of the guild gets offline
+void Guild::UnloadGuildBank()
+{
+ if (!m_bankloaded)
+ return;
+ for (uint8 i = 0 ; i < purchased_tabs ; ++i )
+ {
+ for (uint8 j = 0 ; j < GUILD_BANK_MAX_SLOTS ; ++j)
+ {
+ if (m_TabListMap[i]->Slots[j])
+ {
+ m_TabListMap[i]->Slots[j]->RemoveFromWorld();
+ delete m_TabListMap[i]->Slots[j];
+ }
+ }
+ delete m_TabListMap[i];
+ }
+ m_TabListMap.clear();
+
+ UnloadGuildBankEventLog();
+ m_bankloaded = false;
+}
+
+// *************************************************
+// Money deposit/withdraw related
+
+void Guild::SendMoneyInfo(WorldSession *session, uint32 LowGuid)
+{
+ WorldPacket data(MSG_GUILD_BANK_MONEY_WITHDRAWN, 4);
+ data << uint32(GetMemberMoneyWithdrawRem(LowGuid));
+ session->SendPacket(&data);
+ sLog.outDebug("WORLD: Sent MSG_GUILD_BANK_MONEY_WITHDRAWN");
+}
+
+bool Guild::MemberMoneyWithdraw(uint32 amount, uint32 LowGuid)
+{
+ uint32 MoneyWithDrawRight = GetMemberMoneyWithdrawRem(LowGuid);
+
+ if (MoneyWithDrawRight < amount || GetGuildBankMoney() < amount)
+ return false;
+
+ SetBankMoney(GetGuildBankMoney()-amount);
+
+ if (MoneyWithDrawRight < WITHDRAW_MONEY_UNLIMITED)
+ {
+ MemberList::iterator itr = members.find(LowGuid);
+ if (itr == members.end() )
+ return false;
+ itr->second.BankRemMoney -= amount;
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankRemMoney='%u' WHERE guildid='%u' AND guid='%u'",
+ itr->second.BankRemMoney, Id, LowGuid);
+ }
+ return true;
+}
+
+void Guild::SetBankMoney(int64 money)
+{
+ if (money < 0) // I don't know how this happens, it does!!
+ money = 0;
+ guildbank_money = money;
+
+ CharacterDatabase.PExecute("UPDATE guild SET BankMoney='" I64FMTD "' WHERE guildid='%u'", money, Id);
+}
+
+// *************************************************
+// Item per day and money per day related
+
+bool Guild::MemberItemWithdraw(uint8 TabId, uint32 LowGuid)
+{
+ uint32 SlotsWithDrawRight = GetMemberSlotWithdrawRem(LowGuid, TabId);
+
+ if (SlotsWithDrawRight == 0)
+ return false;
+
+ if (SlotsWithDrawRight < WITHDRAW_SLOT_UNLIMITED)
+ {
+ MemberList::iterator itr = members.find(LowGuid);
+ if (itr == members.end() )
+ return false;
+ --itr->second.BankRemSlotsTab[TabId];
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankRemSlotsTab%u='%u' WHERE guildid='%u' AND guid='%u'",
+ uint32(TabId), itr->second.BankRemSlotsTab[TabId], Id, LowGuid);
+ }
+ return true;
+}
+
+bool Guild::IsMemberHaveRights(uint32 LowGuid, uint8 TabId, uint32 rights) const
+{
+ MemberList::const_iterator itr = members.find(LowGuid);
+ if (itr == members.end() )
+ return false;
+
+ if (itr->second.RankId == GR_GUILDMASTER)
+ return true;
+
+ return (GetBankRights(itr->second.RankId,TabId) & rights)==rights;
+}
+
+uint32 Guild::GetMemberSlotWithdrawRem(uint32 LowGuid, uint8 TabId)
+{
+ MemberList::iterator itr = members.find(LowGuid);
+ if (itr == members.end() )
+ return 0;
+
+ if (itr->second.RankId == GR_GUILDMASTER)
+ return WITHDRAW_SLOT_UNLIMITED;
+
+ if((GetBankRights(itr->second.RankId,TabId) & GUILD_BANK_RIGHT_VIEW_TAB)!=GUILD_BANK_RIGHT_VIEW_TAB)
+ return 0;
+
+ uint32 curTime = uint32(time(NULL)/MINUTE);
+ if (curTime - itr->second.BankResetTimeTab[TabId] >= 24*HOUR/MINUTE)
+ {
+ itr->second.BankResetTimeTab[TabId] = curTime;
+ itr->second.BankRemSlotsTab[TabId] = GetBankSlotPerDay(itr->second.RankId, TabId);
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeTab%u='%u',BankRemSlotsTab%u='%u' WHERE guildid='%u' AND guid='%u'",
+ uint32(TabId), itr->second.BankResetTimeTab[TabId], uint32(TabId), itr->second.BankRemSlotsTab[TabId], Id, LowGuid);
+ }
+ return itr->second.BankRemSlotsTab[TabId];
+}
+
+uint32 Guild::GetMemberMoneyWithdrawRem(uint32 LowGuid)
+{
+ MemberList::iterator itr = members.find(LowGuid);
+ if (itr == members.end() )
+ return 0;
+
+ if (itr->second.RankId == GR_GUILDMASTER)
+ return WITHDRAW_MONEY_UNLIMITED;
+
+ uint32 curTime = uint32(time(NULL)/MINUTE); // minutes
+ // 24 hours
+ if (curTime > itr->second.BankResetTimeMoney + 24*HOUR/MINUTE)
+ {
+ itr->second.BankResetTimeMoney = curTime;
+ itr->second.BankRemMoney = GetBankMoneyPerDay(itr->second.RankId);
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeMoney='%u',BankRemMoney='%u' WHERE guildid='%u' AND guid='%u'",
+ itr->second.BankResetTimeMoney, itr->second.BankRemMoney, Id, LowGuid);
+ }
+ return itr->second.BankRemMoney;
+}
+
+void Guild::SetBankMoneyPerDay(uint32 rankId, uint32 money)
+{
+ if (rankId >= m_ranks.size())
+ return;
+
+ if (rankId == GR_GUILDMASTER)
+ money = WITHDRAW_MONEY_UNLIMITED;
+
+ m_ranks[rankId].BankMoneyPerDay = money;
+
+ for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ if (itr->second.RankId == rankId)
+ itr->second.BankResetTimeMoney = 0;
+
+ CharacterDatabase.PExecute("UPDATE guild_rank SET BankMoneyPerDay='%u' WHERE rid='%u' AND guildid='%u'", money, (rankId+1), Id);
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeMoney='0' WHERE guildid='%u' AND rank='%u'", Id, rankId);
+}
+
+void Guild::SetBankRightsAndSlots(uint32 rankId, uint8 TabId, uint32 right, uint32 nbSlots, bool db)
+{
+ if(rankId >= m_ranks.size() ||
+ TabId >= GUILD_BANK_MAX_TABS ||
+ TabId >= purchased_tabs)
+ return;
+
+ if (rankId == GR_GUILDMASTER)
+ {
+ nbSlots = WITHDRAW_SLOT_UNLIMITED;
+ right = GUILD_BANK_RIGHT_FULL;
+ }
+
+ m_ranks[rankId].TabSlotPerDay[TabId]=nbSlots;
+ m_ranks[rankId].TabRight[TabId]=right;
+
+ if (db)
+ {
+ for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr)
+ if (itr->second.RankId == rankId)
+ for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ itr->second.BankResetTimeTab[i] = 0;
+
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid='%u' AND TabId='%u' AND rid='%u'", Id, uint32(TabId), rankId);
+ CharacterDatabase.PExecute("INSERT INTO guild_bank_right (guildid,TabId,rid,gbright,SlotPerDay) VALUES "
+ "('%u','%u','%u','%u','%u')", Id, uint32(TabId), rankId, m_ranks[rankId].TabRight[TabId], m_ranks[rankId].TabSlotPerDay[TabId]);
+ CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeTab%u='0' WHERE guildid='%u' AND rank='%u'", uint32(TabId), Id, rankId);
+ }
+}
+
+uint32 Guild::GetBankMoneyPerDay(uint32 rankId)
+{
+ if(rankId >= m_ranks.size())
+ return 0;
+
+ if (rankId == GR_GUILDMASTER)
+ return WITHDRAW_MONEY_UNLIMITED;
+ return m_ranks[rankId].BankMoneyPerDay;
+}
+
+uint32 Guild::GetBankSlotPerDay(uint32 rankId, uint8 TabId)
+{
+ if(rankId >= m_ranks.size() || TabId >= GUILD_BANK_MAX_TABS)
+ return 0;
+
+ if (rankId == GR_GUILDMASTER)
+ return WITHDRAW_SLOT_UNLIMITED;
+ return m_ranks[rankId].TabSlotPerDay[TabId];
+}
+
+// *************************************************
+// Rights per day related
+
+void Guild::LoadBankRightsFromDB(uint32 GuildId)
+{
+ // 0 1 2 3
+ QueryResult *result = CharacterDatabase.PQuery("SELECT TabId, rid, gbright, SlotPerDay FROM guild_bank_right WHERE guildid = '%u' ORDER BY TabId", GuildId);
+
+ if(!result)
+ return;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ uint8 TabId = fields[0].GetUInt8();
+ uint32 rankId = fields[1].GetUInt32();
+ uint16 right = fields[2].GetUInt16();
+ uint16 SlotPerDay = fields[3].GetUInt16();
+
+ SetBankRightsAndSlots(rankId, TabId, right, SlotPerDay, false);
+
+ }while( result->NextRow() );
+ delete result;
+
+ return;
+}
+
+// *************************************************
+// Bank log related
+
+void Guild::LoadGuildBankEventLogFromDB()
+{
+ // We can't add a limit as in Guild::LoadGuildEventLogFromDB since we fetch both money and bank log and know nothing about the composition
+ // 0 1 2 3 4 5 6 7
+ QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, LogEntry, TabId, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp FROM guild_bank_eventlog WHERE guildid='%u' ORDER BY TimeStamp DESC", Id);
+ if(!result)
+ return;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ GuildBankEvent *NewEvent = new GuildBankEvent;
+
+ NewEvent->LogGuid = fields[0].GetUInt32();
+ NewEvent->LogEntry = fields[1].GetUInt8();
+ uint8 TabId = fields[2].GetUInt8();
+ NewEvent->PlayerGuid = fields[3].GetUInt32();
+ NewEvent->ItemOrMoney = fields[4].GetUInt32();
+ NewEvent->ItemStackCount = fields[5].GetUInt8();
+ NewEvent->DestTabId = fields[6].GetUInt8();
+ NewEvent->TimeStamp = fields[7].GetUInt64();
+
+ if (TabId >= GUILD_BANK_MAX_TABS)
+ {
+ sLog.outError( "Guild::LoadGuildBankEventLogFromDB: Invalid tabid '%u' for guild bank log entry (guild: '%s', LogGuid: %u), skipped.", TabId, GetName().c_str(), NewEvent->LogGuid);
+ delete NewEvent;
+ continue;
+ }
+ if (NewEvent->isMoneyEvent() && m_GuildBankEventLog_Money.size() >= GUILD_BANK_MAX_LOGS
+ || m_GuildBankEventLog_Item[TabId].size() >= GUILD_BANK_MAX_LOGS)
+ {
+ delete NewEvent;
+ continue;
+ }
+ if (NewEvent->isMoneyEvent())
+ m_GuildBankEventLog_Money.push_front(NewEvent);
+ else
+ m_GuildBankEventLog_Item[TabId].push_front(NewEvent);
+
+ }while( result->NextRow() );
+ delete result;
+
+ // Check lists size in case to many event entries in db for a tab or for money
+ // This cases can happen only if a crash occured somewhere and table has too many log entries
+ if (!m_GuildBankEventLog_Money.empty())
+ {
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid=%u AND LogGuid < %u",
+ Id, m_GuildBankEventLog_Money.front()->LogGuid);
+ }
+ for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ {
+ if (!m_GuildBankEventLog_Item[i].empty())
+ {
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid=%u AND LogGuid < %u",
+ Id, m_GuildBankEventLog_Item[i].front()->LogGuid);
+ }
+ }
+}
+
+void Guild::UnloadGuildBankEventLog()
+{
+ GuildBankEvent *EventLogEntry;
+ if( !m_GuildBankEventLog_Money.empty() )
+ {
+ do
+ {
+ EventLogEntry = *(m_GuildBankEventLog_Money.begin());
+ m_GuildBankEventLog_Money.pop_front();
+ delete EventLogEntry;
+ }while( !m_GuildBankEventLog_Money.empty() );
+ }
+
+ for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ {
+ if( !m_GuildBankEventLog_Item[i].empty() )
+ {
+ do
+ {
+ EventLogEntry = *(m_GuildBankEventLog_Item[i].begin());
+ m_GuildBankEventLog_Item[i].pop_front();
+ delete EventLogEntry;
+ }while( !m_GuildBankEventLog_Item[i].empty() );
+ }
+ }
+}
+
+void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId)
+{
+ if (TabId > GUILD_BANK_MAX_TABS)
+ return;
+
+ if (TabId == GUILD_BANK_MAX_TABS)
+ {
+ // Here we display money logs
+ WorldPacket data(MSG_GUILD_BANK_LOG_QUERY, m_GuildBankEventLog_Money.size()*(4*4+1)+1+1);
+ data << uint8(TabId); // Here GUILD_BANK_MAX_TABS
+ data << uint8(m_GuildBankEventLog_Money.size()); // number of log entries
+ for (GuildBankEventLog::const_iterator itr = m_GuildBankEventLog_Money.begin(); itr != m_GuildBankEventLog_Money.end(); ++itr)
+ {
+ data << uint8((*itr)->LogEntry);
+ data << uint64(MAKE_NEW_GUID((*itr)->PlayerGuid,0,HIGHGUID_PLAYER));
+ data << uint32((*itr)->ItemOrMoney);
+ data << uint32(time(NULL)-(*itr)->TimeStamp);
+ }
+ session->SendPacket(&data);
+ }
+ else
+ {
+ // here we display current tab logs
+ WorldPacket data(MSG_GUILD_BANK_LOG_QUERY, m_GuildBankEventLog_Item[TabId].size()*(4*4+1+1)+1+1);
+ data << uint8(TabId); // Here a real Tab Id
+ // number of log entries
+ data << uint8(m_GuildBankEventLog_Item[TabId].size());
+ for (GuildBankEventLog::const_iterator itr = m_GuildBankEventLog_Item[TabId].begin(); itr != m_GuildBankEventLog_Item[TabId].end(); ++itr)
+ {
+ data << uint8((*itr)->LogEntry);
+ data << uint64(MAKE_NEW_GUID((*itr)->PlayerGuid,0,HIGHGUID_PLAYER));
+ data << uint32((*itr)->ItemOrMoney);
+ data << uint8((*itr)->ItemStackCount);
+ if ((*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM || (*itr)->LogEntry == GUILD_BANK_LOG_MOVE_ITEM2)
+ data << uint8((*itr)->DestTabId); // moved tab
+ data << uint32(time(NULL)-(*itr)->TimeStamp);
+ }
+ session->SendPacket(&data);
+ }
+ sLog.outDebug("WORLD: Sent (MSG_GUILD_BANK_LOG_QUERY)");
+}
+
+void Guild::LogBankEvent(uint8 LogEntry, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount, uint8 DestTabId)
+{
+ GuildBankEvent *NewEvent = new GuildBankEvent;
+
+ NewEvent->LogGuid = LogMaxGuid++;
+ NewEvent->LogEntry = LogEntry;
+ NewEvent->PlayerGuid = PlayerGuidLow;
+ NewEvent->ItemOrMoney = ItemOrMoney;
+ NewEvent->ItemStackCount = ItemStackCount;
+ NewEvent->DestTabId = DestTabId;
+ NewEvent->TimeStamp = uint32(time(NULL));
+
+ if (NewEvent->isMoneyEvent())
+ {
+ if (m_GuildBankEventLog_Money.size() > GUILD_BANK_MAX_LOGS)
+ {
+ GuildBankEvent *OldEvent = *(m_GuildBankEventLog_Money.begin());
+ m_GuildBankEventLog_Money.pop_front();
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, OldEvent->LogGuid);
+ delete OldEvent;
+ }
+ m_GuildBankEventLog_Money.push_back(NewEvent);
+ }
+ else
+ {
+ if (m_GuildBankEventLog_Item[TabId].size() > GUILD_BANK_MAX_LOGS)
+ {
+ GuildBankEvent *OldEvent = *(m_GuildBankEventLog_Item[TabId].begin());
+ m_GuildBankEventLog_Item[TabId].pop_front();
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u'", Id, OldEvent->LogGuid);
+ delete OldEvent;
+ }
+ m_GuildBankEventLog_Item[TabId].push_back(NewEvent);
+ }
+ CharacterDatabase.PExecute("INSERT INTO guild_bank_eventlog (guildid,LogGuid,LogEntry,TabId,PlayerGuid,ItemOrMoney,ItemStackCount,DestTabId,TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','%u','%u','" I64FMTD "')",
+ Id, NewEvent->LogGuid, uint32(NewEvent->LogEntry), uint32(TabId), NewEvent->PlayerGuid, NewEvent->ItemOrMoney, uint32(NewEvent->ItemStackCount), uint32(NewEvent->DestTabId), NewEvent->TimeStamp);
+}
+
+// This will renum guids used at load to prevent always going up until infinit
+void Guild::RenumBankLogs()
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT Min(LogGuid), Max(LogGuid) FROM guild_bank_eventlog WHERE guildid = %u", Id);
+ if(!result)
+ return;
+
+ Field *fields = result->Fetch();
+ CharacterDatabase.PExecute("UPDATE guild_bank_eventlog SET LogGuid=LogGuid-%u+1 WHERE guildid=%u ORDER BY LogGuid %s",fields[0].GetUInt32(), Id, fields[0].GetUInt32()?"ASC":"DESC");
+ LogMaxGuid = fields[1].GetUInt32()+1;
+ delete result;
+}
+
+bool Guild::AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot , uint32 GUIDLow, uint32 Entry )
+{
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid = '%u' AND TabId = '%u'AND SlotId = '%u'", GuildId, BankTab, BankTabSlot);
+ CharacterDatabase.PExecute("INSERT INTO guild_bank_item (guildid,TabId,SlotId,item_guid,item_entry) "
+ "VALUES ('%u', '%u', '%u', '%u', '%u')", GuildId, BankTab, BankTabSlot, GUIDLow, Entry);
+ return true;
+}
+
+void Guild::AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *tab, int slot )
+{
+ Item *pItem = tab->Slots[slot];
+ uint32 entry = pItem ? pItem->GetEntry() : 0;
+
+ data << uint8(slot);
+ data << uint32(entry);
+ if (entry)
+ {
+ // random item property id +8
+ data << (uint32) pItem->GetItemRandomPropertyId();
+ if (pItem->GetItemRandomPropertyId())
+ // SuffixFactor +4
+ data << (uint32) pItem->GetItemSuffixFactor();
+ // +12 // ITEM_FIELD_STACK_COUNT
+ data << uint8(pItem->GetCount());
+ data << uint32(0); // +16 // Unknown value
+ data << uint8(0); // unknown 2.4.2
+ if (uint32 Enchant0 = pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT))
+ {
+ data << uint8(1); // number of enchantments (max 3) why max 3?
+ data << uint8(PERM_ENCHANTMENT_SLOT); // enchantment slot (range: 0:2)
+ data << uint32(Enchant0); // enchantment id
+ }
+ else
+ data << uint8(0); // no enchantments (0)
+ }
+}
+
+Item* Guild::StoreItem(uint8 tabId, GuildItemPosCountVec const& dest, Item* pItem )
+{
+ if( !pItem )
+ return NULL;
+
+ Item* lastItem = pItem;
+
+ for(GuildItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); )
+ {
+ uint8 slot = itr->slot;
+ uint32 count = itr->count;
+
+ ++itr;
+
+ if(itr == dest.end())
+ {
+ lastItem = _StoreItem(tabId,slot,pItem,count,false);
+ break;
+ }
+
+ lastItem = _StoreItem(tabId,slot,pItem,count,true);
+ }
+
+ return lastItem;
+}
+
+// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
+Item* Guild::_StoreItem( uint8 tab, uint8 slot, Item *pItem, uint32 count, bool clone )
+{
+ if( !pItem )
+ return NULL;
+
+ sLog.outDebug( "GUILD STORAGE: StoreItem tab = %u, slot = %u, item = %u, count = %u", tab, slot, pItem->GetEntry(), count);
+
+ Item* pItem2 = m_TabListMap[tab]->Slots[slot];
+
+ if( !pItem2 )
+ {
+ if(clone)
+ pItem = pItem->CloneItem(count);
+ else
+ pItem->SetCount(count);
+
+ if(!pItem)
+ return NULL;
+
+ m_TabListMap[tab]->Slots[slot] = pItem;
+
+ pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, 0);
+ pItem->SetUInt64Value(ITEM_FIELD_OWNER, 0);
+ AddGBankItemToDB(GetId(), tab, slot, pItem->GetGUIDLow(), pItem->GetEntry());
+ pItem->FSetState(ITEM_NEW);
+ pItem->SaveToDB(); // not in onventory and can be save standalone
+
+ return pItem;
+ }
+ else
+ {
+ pItem2->SetCount( pItem2->GetCount() + count );
+ pItem2->FSetState(ITEM_CHANGED);
+ pItem2->SaveToDB(); // not in onventory and can be save standalone
+
+ if(!clone)
+ {
+ pItem->RemoveFromWorld();
+ pItem->DeleteFromDB();
+ delete pItem;
+ }
+
+ return pItem2;
+ }
+}
+
+void Guild::RemoveItem(uint8 tab, uint8 slot )
+{
+ m_TabListMap[tab]->Slots[slot] = NULL;
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE guildid='%u' AND TabId='%u' AND SlotId='%u'",
+ GetId(), uint32(tab), uint32(slot));
+}
+
+uint8 Guild::_CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32& count, bool swap, Item* pSrcItem ) const
+{
+ Item* pItem2 = m_TabListMap[tab]->Slots[slot];
+
+ // ignore move item (this slot will be empty at move)
+ if(pItem2==pSrcItem)
+ pItem2 = NULL;
+
+ uint32 need_space;
+
+ // empty specific slot - check item fit to slot
+ if( !pItem2 || swap )
+ {
+ // non empty stack with space
+ need_space = pSrcItem->GetMaxStackCount();
+ }
+ // non empty slot, check item type
+ else
+ {
+ // check item type
+ if(pItem2->GetEntry() != pSrcItem->GetEntry())
+ return EQUIP_ERR_ITEM_CANT_STACK;
+
+ // check free space
+ if(pItem2->GetCount() >= pSrcItem->GetMaxStackCount())
+ return EQUIP_ERR_ITEM_CANT_STACK;
+
+ need_space = pSrcItem->GetMaxStackCount() - pItem2->GetCount();
+ }
+
+ if(need_space > count)
+ need_space = count;
+
+ GuildItemPosCount newPosition = GuildItemPosCount(slot,need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+ }
+
+ return EQUIP_ERR_OK;
+}
+
+uint8 Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32& count, bool merge, Item* pSrcItem, uint8 skip_slot ) const
+{
+ for(uint32 j = 0; j < GUILD_BANK_MAX_SLOTS; j++)
+ {
+ // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
+ if(j==skip_slot)
+ continue;
+
+ Item* pItem2 = m_TabListMap[tab]->Slots[j];
+
+ // ignore move item (this slot will be empty at move)
+ if(pItem2==pSrcItem)
+ pItem2 = NULL;
+
+ // if merge skip empty, if !merge skip non-empty
+ if((pItem2!=NULL)!=merge)
+ continue;
+
+ if( pItem2 )
+ {
+ if(pItem2->GetEntry() == pSrcItem->GetEntry() && pItem2->GetCount() < pSrcItem->GetMaxStackCount() )
+ {
+ uint32 need_space = pSrcItem->GetMaxStackCount() - pItem2->GetCount();
+ if(need_space > count)
+ need_space = count;
+
+ GuildItemPosCount newPosition = GuildItemPosCount(j,need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ else
+ {
+ uint32 need_space = pSrcItem->GetMaxStackCount();
+ if(need_space > count)
+ need_space = count;
+
+ GuildItemPosCount newPosition = GuildItemPosCount(j,need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ return EQUIP_ERR_OK;
+}
+
+uint8 Guild::CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32 count, Item *pItem, bool swap ) const
+{
+ sLog.outDebug( "GUILD STORAGE: CanStoreItem tab = %u, slot = %u, item = %u, count = %u", tab, slot, pItem->GetEntry(), count);
+
+ if(count > pItem->GetCount())
+ return EQUIP_ERR_COULDNT_SPLIT_ITEMS;
+
+ if(pItem->IsSoulBound())
+ return EQUIP_ERR_CANT_DROP_SOULBOUND;
+
+ // in specific slot
+ if( slot != NULL_SLOT )
+ {
+ uint8 res = _CanStoreItem_InSpecificSlot(tab,slot,dest,count,swap,pItem);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+
+ // not specific slot or have spece for partly store only in specific slot
+
+ // search stack in tab for merge to
+ if( pItem->GetMaxStackCount() > 1 )
+ {
+ uint8 res = _CanStoreItem_InTab(tab,dest,count,true,pItem,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+
+ // search free slot in bag for place to
+ uint8 res = _CanStoreItem_InTab(tab,dest,count,false,pItem,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+
+ return EQUIP_ERR_BANK_FULL;
+}
+
+void Guild::SetGuildBankTabText(uint8 TabId, std::string text)
+{
+ if (TabId >= GUILD_BANK_MAX_TABS)
+ return;
+ if (TabId >= m_TabListMap.size())
+ return;
+ if (!m_TabListMap[TabId])
+ return;
+
+ if(m_TabListMap[TabId]->Text==text)
+ return;
+
+ utf8truncate(text,500); // DB and client size limitation
+
+ m_TabListMap[TabId]->Text = text;
+
+ CharacterDatabase.escape_string(text);
+ CharacterDatabase.PExecute("UPDATE guild_bank_tab SET TabText='%s' WHERE guildid='%u' AND TabId='%u'", text.c_str(), Id, uint32(TabId));
+}
+
+void Guild::SendGuildBankTabText(WorldSession *session, uint8 TabId)
+{
+ if (TabId > GUILD_BANK_MAX_TABS)
+ return;
+
+ GuildBankTab const *tab = GetBankTab(TabId);
+ if (!tab)
+ return;
+
+ WorldPacket data(MSG_QUERY_GUILD_BANK_TEXT, 1+tab->Text.size()+1);
+ data << uint8(TabId);
+ data << tab->Text;
+ session->SendPacket(&data);
+}
+
+bool GuildItemPosCount::isContainedIn(GuildItemPosCountVec const &vec) const
+{
+ for(GuildItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr)
+ if(itr->slot == this->slot)
+ return true;
+
+ return false;
+}
+
diff --git a/src/game/Guild.h b/src/game/Guild.h
index 6ec58efb196..182e1f45e6e 100644
--- a/src/game/Guild.h
+++ b/src/game/Guild.h
@@ -1,437 +1,437 @@
-/*
- * 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
- */
-
-#ifndef MANGOSSERVER_GUILD_H
-#define MANGOSSERVER_GUILD_H
-
-#define WITHDRAW_MONEY_UNLIMITED 0xFFFFFFFF
-#define WITHDRAW_SLOT_UNLIMITED 0xFFFFFFFF
-
-#include "Item.h"
-
-class Item;
-
-enum GuildDefaultRanks
-{
- GR_GUILDMASTER = 0,
- GR_OFFICER = 1,
- //GR_VETERAN = 2, -- not used anywhere and possible incorrect in modified rank list
- //GR_MEMBER = 3,
- //GR_INITIATE = 4, -- use Guild::GetLowestRank() instead for lowest rank
-};
-
-enum GuildRankRights
-{
- GR_RIGHT_EMPTY = 0x00000040,
- GR_RIGHT_GCHATLISTEN = 0x00000041,
- GR_RIGHT_GCHATSPEAK = 0x00000042,
- GR_RIGHT_OFFCHATLISTEN = 0x00000044,
- GR_RIGHT_OFFCHATSPEAK = 0x00000048,
- GR_RIGHT_PROMOTE = 0x000000C0,
- GR_RIGHT_DEMOTE = 0x00000140,
- GR_RIGHT_INVITE = 0x00000050,
- GR_RIGHT_REMOVE = 0x00000060,
- GR_RIGHT_SETMOTD = 0x00001040,
- GR_RIGHT_EPNOTE = 0x00002040,
- GR_RIGHT_VIEWOFFNOTE = 0x00004040,
- GR_RIGHT_EOFFNOTE = 0x00008040,
- GR_RIGHT_MODIFY_GUILD_INFO = 0x00010040,
- GR_RIGHT_REPAIR_FROM_GUILD = 0x00020000, // unused in 2.4.x?, Remove money withdraw capacity
- GR_RIGHT_WITHDRAW_REPAIR = 0x00040000, // withdraw for repair
- GR_RIGHT_WITHDRAW_GOLD = 0x00080000, // withdraw gold
- GR_RIGHT_ALL = 0x000FF1FF
-};
-
-enum Typecommand
-{
- GUILD_CREATE_S = 0x00,
- GUILD_INVITE_S = 0x01,
- GUILD_QUIT_S = 0x03,
- GUILD_FOUNDER_S = 0x0E,
- GUILD_UNK1 = 0x10,
- GUILD_BANK_S = 0x15,
- GUILD_UNK3 = 0x16
-};
-
-enum CommandErrors
-{
- GUILD_PLAYER_NO_MORE_IN_GUILD = 0x00,
- GUILD_INTERNAL = 0x01,
- GUILD_ALREADY_IN_GUILD = 0x02,
- ALREADY_IN_GUILD = 0x03,
- INVITED_TO_GUILD = 0x04,
- ALREADY_INVITED_TO_GUILD = 0x05,
- GUILD_NAME_INVALID = 0x06,
- GUILD_NAME_EXISTS = 0x07,
- GUILD_LEADER_LEAVE = 0x08,
- GUILD_PERMISSIONS = 0x08,
- GUILD_PLAYER_NOT_IN_GUILD = 0x09,
- GUILD_PLAYER_NOT_IN_GUILD_S = 0x0A,
- GUILD_PLAYER_NOT_FOUND = 0x0B,
- GUILD_NOT_ALLIED = 0x0C,
- GUILD_RANK_TOO_HIGH_S = 0x0D,
- GUILD_ALREADY_LOWEST_RANK_S = 0x0E,
- GUILD_TEMP_ERROR = 0x11,
- GUILD_RANK_IN_USE = 0x12,
- GUILD_IGNORE = 0x13,
- GUILD_ERR_UNK1 = 0x17,
- GUILD_WITHDRAW_TOO_MUCH = 0x18,
- GUILD_BANK_NO_MONEY = 0x19,
- GUILD_BANK_TAB_IS_FULL = 0x1B,
- GUILD_BANK_ITEM_NOT_FOUND = 0x1C
-};
-
-enum GuildEvents
-{
- GE_PROMOTION = 0x00,
- GE_DEMOTION = 0x01,
- GE_MOTD = 0x02,
- GE_JOINED = 0x03,
- GE_LEFT = 0x04,
- GE_REMOVED = 0x05,
- GE_LEADER_IS = 0x06,
- GE_LEADER_CHANGED = 0x07,
- GE_DISBANDED = 0x08,
- GE_TABARDCHANGE = 0x09,
- GE_UNK1 = 0x0A, // string, string
- GE_UNK2 = 0x0B,
- GE_SIGNED_ON = 0x0C,
- GE_SIGNED_OFF = 0x0D,
- GE_UNK3 = 0x0E,
- GE_BANKTAB_PURCHASED= 0x0F,
- GE_UNK5 = 0x10,
- GE_UNK6 = 0x11, // string 0000000000002710 is 1 gold
- GE_UNK7 = 0x12
-};
-
-enum PetitionTurns
-{
- PETITION_TURN_OK = 0,
- PETITION_TURN_ALREADY_IN_GUILD = 2,
- PETITION_TURN_NEED_MORE_SIGNATURES = 4,
-};
-
-enum PetitionSigns
-{
- PETITION_SIGN_OK = 0,
- PETITION_SIGN_ALREADY_SIGNED = 1,
- PETITION_SIGN_ALREADY_IN_GUILD = 2,
- PETITION_SIGN_CANT_SIGN_OWN = 3,
- PETITION_SIGN_NOT_SERVER = 4,
-};
-
-enum GuildBankRights
-{
- GUILD_BANK_RIGHT_VIEW_TAB = 0x01,
- GUILD_BANK_RIGHT_PUT_ITEM = 0x02,
- GUILD_BANK_RIGHT_UPDATE_TEXT = 0x04,
-
- GUILD_BANK_RIGHT_DEPOSIT_ITEM = GUILD_BANK_RIGHT_VIEW_TAB | GUILD_BANK_RIGHT_PUT_ITEM,
- GUILD_BANK_RIGHT_FULL = 0xFF,
-};
-
-enum GuildBankLogEntries
-{
- GUILD_BANK_LOG_DEPOSIT_ITEM = 1,
- GUILD_BANK_LOG_WITHDRAW_ITEM = 2,
- GUILD_BANK_LOG_MOVE_ITEM = 3,
- GUILD_BANK_LOG_DEPOSIT_MONEY = 4,
- GUILD_BANK_LOG_WITHDRAW_MONEY = 5,
- GUILD_BANK_LOG_REPAIR_MONEY = 6,
- GUILD_BANK_LOG_MOVE_ITEM2 = 7,
-};
-
-enum GuildEventLogEntryTypes
-{
- GUILD_EVENT_LOG_INVITE_PLAYER = 1,
- GUILD_EVENT_LOG_JOIN_GUILD = 2,
- GUILD_EVENT_LOG_PROMOTE_PLAYER = 3,
- GUILD_EVENT_LOG_DEMOTE_PLAYER = 4,
- GUILD_EVENT_LOG_UNINVITE_PLAYER = 5,
- GUILD_EVENT_LOG_LEAVE_GUILD = 6,
-};
-
-struct GuildEventlogEntry
-{
- uint32 LogGuid;
- uint8 EventType;
- uint32 PlayerGuid1;
- uint32 PlayerGuid2;
- uint8 NewRank;
- uint64 TimeStamp;
-};
-
-enum GuildEmblem
-{
- ERR_GUILDEMBLEM_SUCCESS = 0,
- ERR_GUILDEMBLEM_INVALID_TABARD_COLORS = 1,
- ERR_GUILDEMBLEM_NOGUILD = 2,
- ERR_GUILDEMBLEM_NOTGUILDMASTER = 3,
- ERR_GUILDEMBLEM_NOTENOUGHMONEY = 4,
- ERR_GUILDEMBLEM_INVALIDVENDOR = 5
-};
-
-struct GuildBankEvent
-{
- uint32 LogGuid;
- uint8 LogEntry;
- uint8 TabId;
- uint32 PlayerGuid;
- uint32 ItemOrMoney;
- uint8 ItemStackCount;
- uint8 DestTabId;
- uint64 TimeStamp;
-
- const bool isMoneyEvent()
- {
- return LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY ||
- LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY ||
- LogEntry == GUILD_BANK_LOG_REPAIR_MONEY;
- }
-};
-
-struct GuildBankTab
-{
- Item* Slots[GUILD_BANK_MAX_SLOTS];
- std::string Name;
- std::string Icon;
- std::string Text;
-};
-
-struct GuildItemPosCount
-{
- GuildItemPosCount(uint8 _slot, uint8 _count) : slot(_slot), count(_count) {}
-
- bool isContainedIn(std::vector<GuildItemPosCount> const& vec) const;
-
- uint8 slot;
- uint8 count;
-};
-typedef std::vector<GuildItemPosCount> GuildItemPosCountVec;
-
-struct MemberSlot
-{
- uint64 logout_time;
- std::string name;
- std::string Pnote;
- std::string OFFnote;
- uint32 RankId;
- uint32 zoneId;
- uint8 level;
- uint8 Class;
- uint32 BankResetTimeMoney;
- uint32 BankRemMoney;
- uint32 BankResetTimeTab[GUILD_BANK_MAX_TABS];
- uint32 BankRemSlotsTab[GUILD_BANK_MAX_TABS];
-};
-
-struct RankInfo
-{
- RankInfo(std::string _name, uint32 _rights, uint32 _money) : name(_name), rights(_rights), BankMoneyPerDay(_money)
- {
- for(uint8 i = 0; i < GUILD_BANK_MAX_TABS; ++i)
- {
- TabRight[i] = 0;
- TabSlotPerDay[i] = 0;
- }
- }
-
- std::string name;
- uint32 rights;
- uint32 BankMoneyPerDay;
- uint32 TabRight[GUILD_BANK_MAX_TABS];
- uint32 TabSlotPerDay[GUILD_BANK_MAX_TABS];
-};
-
-class Guild
-{
- public:
- Guild();
- ~Guild();
-
- bool create(uint64 lGuid, std::string gname);
- void Disband();
-
- typedef std::map<uint32, MemberSlot> MemberList;
- typedef std::vector<RankInfo> RankList;
-
- uint32 GetId(){ return Id; }
- const uint64& GetLeader(){ return leaderGuid; }
- std::string GetName(){ return name; }
- std::string GetMOTD(){ return MOTD; }
- std::string GetGINFO(){ return GINFO; }
-
- uint32 GetCreatedYear(){ return CreatedYear; }
- uint32 GetCreatedMonth(){ return CreatedMonth; }
- uint32 GetCreatedDay(){ return CreatedDay; }
-
- uint32 GetEmblemStyle(){ return EmblemStyle; }
- uint32 GetEmblemColor(){ return EmblemColor; }
- uint32 GetBorderStyle(){ return BorderStyle; }
- uint32 GetBorderColor(){ return BorderColor; }
- uint32 GetBackgroundColor(){ return BackgroundColor; }
-
- void SetLeader(uint64 guid);
- bool AddMember(uint64 plGuid, uint32 plRank);
- void ChangeRank(uint64 guid, uint32 newRank);
- void DelMember(uint64 guid, bool isDisbanding=false);
- uint32 GetLowestRank() const { return GetNrRanks()-1; }
-
- void SetMOTD(std::string motd);
- void SetGINFO(std::string ginfo);
- void SetPNOTE(uint64 guid,std::string pnote);
- void SetOFFNOTE(uint64 guid,std::string offnote);
- void SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor, uint32 backgroundColor);
-
- uint32 GetMemberSize() const { return members.size(); }
-
- bool LoadGuildFromDB(uint32 GuildId);
- bool LoadRanksFromDB(uint32 GuildId);
- bool LoadMembersFromDB(uint32 GuildId);
-
- bool FillPlayerData(uint64 guid, MemberSlot* memslot);
- void LoadPlayerStatsByGuid(uint64 guid);
-
- void BroadcastToGuild(WorldSession *session, std::string msg, uint32 language = LANG_UNIVERSAL);
- void BroadcastToOfficers(WorldSession *session, std::string msg, uint32 language = LANG_UNIVERSAL);
- void BroadcastPacketToRank(WorldPacket *packet, uint32 rankId);
- void BroadcastPacket(WorldPacket *packet);
-
- void CreateRank(std::string name,uint32 rights);
- void DelRank();
- std::string GetRankName(uint32 rankId);
- uint32 GetRankRights(uint32 rankId);
- uint32 GetNrRanks() const { return m_ranks.size(); }
-
- void SetRankName(uint32 rankId, std::string name);
- void SetRankRights(uint32 rankId, uint32 rights);
- bool HasRankRight(uint32 rankId, uint32 right)
- {
- return ((GetRankRights(rankId) & right) != GR_RIGHT_EMPTY) ? true : false;
- }
-
- void Roster(WorldSession *session);
- void Query(WorldSession *session);
-
- void UpdateLogoutTime(uint64 guid);
- // Guild eventlog
- void LoadGuildEventLogFromDB();
- void UnloadGuildEventlog();
- void DisplayGuildEventlog(WorldSession *session);
- void LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid2, uint8 NewRank);
- void RenumGuildEventlog();
-
- // ** Guild bank **
- // Content & item deposit/withdraw
- void DisplayGuildBankContent(WorldSession *session, uint8 TabId);
- void DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2 = -1);
- void DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots);
- void DisplayGuildBankMoneyUpdate();
-
- Item* GetItem(uint8 TabId, uint8 SlotId);
- uint8 CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32 count, Item *pItem, bool swap = false) const;
- Item* StoreItem( uint8 tab, GuildItemPosCountVec const& pos, Item *pItem );
- void RemoveItem(uint8 tab, uint8 slot );
-
- // Tabs
- void DisplayGuildBankTabsInfo(WorldSession *session);
- void CreateNewBankTab();
- void SetGuildBankTabText(uint8 TabId, std::string text);
- void SendGuildBankTabText(WorldSession *session, uint8 TabId);
- void SetGuildBankTabInfo(uint8 TabId, std::string name, std::string icon);
- void CreateBankRightForTab(uint32 rankid, uint8 TabId);
- const GuildBankTab *GetBankTab(uint8 index) { if(index >= m_TabListMap.size()) return NULL; return m_TabListMap[index]; }
- const uint8 GetPurchasedTabs() const { return purchased_tabs; }
- uint32 GetBankRights(uint32 rankId, uint8 TabId) const;
- bool IsMemberHaveRights(uint32 LowGuid, uint8 TabId,uint32 rights) const;
- bool CanMemberViewTab(uint32 LowGuid, uint8 TabId) const;
- // Load/unload
- void LoadGuildBankFromDB();
- void UnloadGuildBank();
- void IncOnlineMemberCount() { ++m_onlinemembers; }
- // Money deposit/withdraw
- void SendMoneyInfo(WorldSession *session, uint32 LowGuid);
- bool MemberMoneyWithdraw(uint32 amount, uint32 LowGuid);
- uint64 GetGuildBankMoney() { return guildbank_money; }
- void SetBankMoney(int64 money);
- // per days
- bool MemberItemWithdraw(uint8 TabId, uint32 LowGuid);
- uint32 GetMemberSlotWithdrawRem(uint32 LowGuid, uint8 TabId);
- uint32 GetMemberMoneyWithdrawRem(uint32 LowGuid);
- void SetBankMoneyPerDay(uint32 rankId, uint32 money);
- void SetBankRightsAndSlots(uint32 rankId, uint8 TabId, uint32 right, uint32 SlotPerDay, bool db);
- uint32 GetBankMoneyPerDay(uint32 rankId);
- uint32 GetBankSlotPerDay(uint32 rankId, uint8 TabId);
- // rights per day
- void LoadBankRightsFromDB(uint32 GuildId);
- // logs
- void LoadGuildBankEventLogFromDB();
- void UnloadGuildBankEventLog();
- void DisplayGuildBankLogs(WorldSession *session, uint8 TabId);
- void LogBankEvent(uint8 LogEntry, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount=0, uint8 DestTabId=0);
- void RenumBankLogs();
- bool AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot , uint32 GUIDLow, uint32 Entry );
-
- protected:
- void AddRank(std::string name,uint32 rights,uint32 money);
-
- uint32 Id;
- std::string name;
- uint64 leaderGuid;
- std::string MOTD;
- std::string GINFO;
- uint32 CreatedYear;
- uint32 CreatedMonth;
- uint32 CreatedDay;
-
- uint32 EmblemStyle;
- uint32 EmblemColor;
- uint32 BorderStyle;
- uint32 BorderColor;
- uint32 BackgroundColor;
-
- RankList m_ranks;
-
- MemberList members;
-
- typedef std::vector<GuildBankTab*> TabListMap;
- TabListMap m_TabListMap;
-
- /** These are actually ordered lists. The first element is the oldest entry.*/
- typedef std::list<GuildEventlogEntry*> GuildEventlog;
- typedef std::list<GuildBankEvent*> GuildBankEventLog;
- GuildEventlog m_GuildEventlog;
- GuildBankEventLog m_GuildBankEventLog_Money;
- GuildBankEventLog m_GuildBankEventLog_Item[GUILD_BANK_MAX_TABS];
-
- bool m_bankloaded;
- bool m_eventlogloaded;
- uint32 m_onlinemembers;
- uint64 guildbank_money;
- uint8 purchased_tabs;
-
- uint32 LogMaxGuid;
- uint32 GuildEventlogMaxGuid;
- private:
- // internal common parts for CanStore/StoreItem functions
- void AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *tab, int32 slot );
- uint8 _CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32& count, bool swap, Item *pSrcItem ) const;
- uint8 _CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec& dest, uint32& count, bool merge, Item *pSrcItem, uint8 skip_slot ) const;
- Item* _StoreItem( uint8 tab, uint8 slot, Item *pItem, uint32 count, bool clone );
-};
-#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
+ */
+
+#ifndef MANGOSSERVER_GUILD_H
+#define MANGOSSERVER_GUILD_H
+
+#define WITHDRAW_MONEY_UNLIMITED 0xFFFFFFFF
+#define WITHDRAW_SLOT_UNLIMITED 0xFFFFFFFF
+
+#include "Item.h"
+
+class Item;
+
+enum GuildDefaultRanks
+{
+ GR_GUILDMASTER = 0,
+ GR_OFFICER = 1,
+ //GR_VETERAN = 2, -- not used anywhere and possible incorrect in modified rank list
+ //GR_MEMBER = 3,
+ //GR_INITIATE = 4, -- use Guild::GetLowestRank() instead for lowest rank
+};
+
+enum GuildRankRights
+{
+ GR_RIGHT_EMPTY = 0x00000040,
+ GR_RIGHT_GCHATLISTEN = 0x00000041,
+ GR_RIGHT_GCHATSPEAK = 0x00000042,
+ GR_RIGHT_OFFCHATLISTEN = 0x00000044,
+ GR_RIGHT_OFFCHATSPEAK = 0x00000048,
+ GR_RIGHT_PROMOTE = 0x000000C0,
+ GR_RIGHT_DEMOTE = 0x00000140,
+ GR_RIGHT_INVITE = 0x00000050,
+ GR_RIGHT_REMOVE = 0x00000060,
+ GR_RIGHT_SETMOTD = 0x00001040,
+ GR_RIGHT_EPNOTE = 0x00002040,
+ GR_RIGHT_VIEWOFFNOTE = 0x00004040,
+ GR_RIGHT_EOFFNOTE = 0x00008040,
+ GR_RIGHT_MODIFY_GUILD_INFO = 0x00010040,
+ GR_RIGHT_REPAIR_FROM_GUILD = 0x00020000, // unused in 2.4.x?, Remove money withdraw capacity
+ GR_RIGHT_WITHDRAW_REPAIR = 0x00040000, // withdraw for repair
+ GR_RIGHT_WITHDRAW_GOLD = 0x00080000, // withdraw gold
+ GR_RIGHT_ALL = 0x000FF1FF
+};
+
+enum Typecommand
+{
+ GUILD_CREATE_S = 0x00,
+ GUILD_INVITE_S = 0x01,
+ GUILD_QUIT_S = 0x03,
+ GUILD_FOUNDER_S = 0x0E,
+ GUILD_UNK1 = 0x10,
+ GUILD_BANK_S = 0x15,
+ GUILD_UNK3 = 0x16
+};
+
+enum CommandErrors
+{
+ GUILD_PLAYER_NO_MORE_IN_GUILD = 0x00,
+ GUILD_INTERNAL = 0x01,
+ GUILD_ALREADY_IN_GUILD = 0x02,
+ ALREADY_IN_GUILD = 0x03,
+ INVITED_TO_GUILD = 0x04,
+ ALREADY_INVITED_TO_GUILD = 0x05,
+ GUILD_NAME_INVALID = 0x06,
+ GUILD_NAME_EXISTS = 0x07,
+ GUILD_LEADER_LEAVE = 0x08,
+ GUILD_PERMISSIONS = 0x08,
+ GUILD_PLAYER_NOT_IN_GUILD = 0x09,
+ GUILD_PLAYER_NOT_IN_GUILD_S = 0x0A,
+ GUILD_PLAYER_NOT_FOUND = 0x0B,
+ GUILD_NOT_ALLIED = 0x0C,
+ GUILD_RANK_TOO_HIGH_S = 0x0D,
+ GUILD_ALREADY_LOWEST_RANK_S = 0x0E,
+ GUILD_TEMP_ERROR = 0x11,
+ GUILD_RANK_IN_USE = 0x12,
+ GUILD_IGNORE = 0x13,
+ GUILD_ERR_UNK1 = 0x17,
+ GUILD_WITHDRAW_TOO_MUCH = 0x18,
+ GUILD_BANK_NO_MONEY = 0x19,
+ GUILD_BANK_TAB_IS_FULL = 0x1B,
+ GUILD_BANK_ITEM_NOT_FOUND = 0x1C
+};
+
+enum GuildEvents
+{
+ GE_PROMOTION = 0x00,
+ GE_DEMOTION = 0x01,
+ GE_MOTD = 0x02,
+ GE_JOINED = 0x03,
+ GE_LEFT = 0x04,
+ GE_REMOVED = 0x05,
+ GE_LEADER_IS = 0x06,
+ GE_LEADER_CHANGED = 0x07,
+ GE_DISBANDED = 0x08,
+ GE_TABARDCHANGE = 0x09,
+ GE_UNK1 = 0x0A, // string, string
+ GE_UNK2 = 0x0B,
+ GE_SIGNED_ON = 0x0C,
+ GE_SIGNED_OFF = 0x0D,
+ GE_UNK3 = 0x0E,
+ GE_BANKTAB_PURCHASED= 0x0F,
+ GE_UNK5 = 0x10,
+ GE_UNK6 = 0x11, // string 0000000000002710 is 1 gold
+ GE_UNK7 = 0x12
+};
+
+enum PetitionTurns
+{
+ PETITION_TURN_OK = 0,
+ PETITION_TURN_ALREADY_IN_GUILD = 2,
+ PETITION_TURN_NEED_MORE_SIGNATURES = 4,
+};
+
+enum PetitionSigns
+{
+ PETITION_SIGN_OK = 0,
+ PETITION_SIGN_ALREADY_SIGNED = 1,
+ PETITION_SIGN_ALREADY_IN_GUILD = 2,
+ PETITION_SIGN_CANT_SIGN_OWN = 3,
+ PETITION_SIGN_NOT_SERVER = 4,
+};
+
+enum GuildBankRights
+{
+ GUILD_BANK_RIGHT_VIEW_TAB = 0x01,
+ GUILD_BANK_RIGHT_PUT_ITEM = 0x02,
+ GUILD_BANK_RIGHT_UPDATE_TEXT = 0x04,
+
+ GUILD_BANK_RIGHT_DEPOSIT_ITEM = GUILD_BANK_RIGHT_VIEW_TAB | GUILD_BANK_RIGHT_PUT_ITEM,
+ GUILD_BANK_RIGHT_FULL = 0xFF,
+};
+
+enum GuildBankLogEntries
+{
+ GUILD_BANK_LOG_DEPOSIT_ITEM = 1,
+ GUILD_BANK_LOG_WITHDRAW_ITEM = 2,
+ GUILD_BANK_LOG_MOVE_ITEM = 3,
+ GUILD_BANK_LOG_DEPOSIT_MONEY = 4,
+ GUILD_BANK_LOG_WITHDRAW_MONEY = 5,
+ GUILD_BANK_LOG_REPAIR_MONEY = 6,
+ GUILD_BANK_LOG_MOVE_ITEM2 = 7,
+};
+
+enum GuildEventLogEntryTypes
+{
+ GUILD_EVENT_LOG_INVITE_PLAYER = 1,
+ GUILD_EVENT_LOG_JOIN_GUILD = 2,
+ GUILD_EVENT_LOG_PROMOTE_PLAYER = 3,
+ GUILD_EVENT_LOG_DEMOTE_PLAYER = 4,
+ GUILD_EVENT_LOG_UNINVITE_PLAYER = 5,
+ GUILD_EVENT_LOG_LEAVE_GUILD = 6,
+};
+
+struct GuildEventlogEntry
+{
+ uint32 LogGuid;
+ uint8 EventType;
+ uint32 PlayerGuid1;
+ uint32 PlayerGuid2;
+ uint8 NewRank;
+ uint64 TimeStamp;
+};
+
+enum GuildEmblem
+{
+ ERR_GUILDEMBLEM_SUCCESS = 0,
+ ERR_GUILDEMBLEM_INVALID_TABARD_COLORS = 1,
+ ERR_GUILDEMBLEM_NOGUILD = 2,
+ ERR_GUILDEMBLEM_NOTGUILDMASTER = 3,
+ ERR_GUILDEMBLEM_NOTENOUGHMONEY = 4,
+ ERR_GUILDEMBLEM_INVALIDVENDOR = 5
+};
+
+struct GuildBankEvent
+{
+ uint32 LogGuid;
+ uint8 LogEntry;
+ uint8 TabId;
+ uint32 PlayerGuid;
+ uint32 ItemOrMoney;
+ uint8 ItemStackCount;
+ uint8 DestTabId;
+ uint64 TimeStamp;
+
+ const bool isMoneyEvent()
+ {
+ return LogEntry == GUILD_BANK_LOG_DEPOSIT_MONEY ||
+ LogEntry == GUILD_BANK_LOG_WITHDRAW_MONEY ||
+ LogEntry == GUILD_BANK_LOG_REPAIR_MONEY;
+ }
+};
+
+struct GuildBankTab
+{
+ Item* Slots[GUILD_BANK_MAX_SLOTS];
+ std::string Name;
+ std::string Icon;
+ std::string Text;
+};
+
+struct GuildItemPosCount
+{
+ GuildItemPosCount(uint8 _slot, uint8 _count) : slot(_slot), count(_count) {}
+
+ bool isContainedIn(std::vector<GuildItemPosCount> const& vec) const;
+
+ uint8 slot;
+ uint8 count;
+};
+typedef std::vector<GuildItemPosCount> GuildItemPosCountVec;
+
+struct MemberSlot
+{
+ uint64 logout_time;
+ std::string name;
+ std::string Pnote;
+ std::string OFFnote;
+ uint32 RankId;
+ uint32 zoneId;
+ uint8 level;
+ uint8 Class;
+ uint32 BankResetTimeMoney;
+ uint32 BankRemMoney;
+ uint32 BankResetTimeTab[GUILD_BANK_MAX_TABS];
+ uint32 BankRemSlotsTab[GUILD_BANK_MAX_TABS];
+};
+
+struct RankInfo
+{
+ RankInfo(std::string _name, uint32 _rights, uint32 _money) : name(_name), rights(_rights), BankMoneyPerDay(_money)
+ {
+ for(uint8 i = 0; i < GUILD_BANK_MAX_TABS; ++i)
+ {
+ TabRight[i] = 0;
+ TabSlotPerDay[i] = 0;
+ }
+ }
+
+ std::string name;
+ uint32 rights;
+ uint32 BankMoneyPerDay;
+ uint32 TabRight[GUILD_BANK_MAX_TABS];
+ uint32 TabSlotPerDay[GUILD_BANK_MAX_TABS];
+};
+
+class Guild
+{
+ public:
+ Guild();
+ ~Guild();
+
+ bool create(uint64 lGuid, std::string gname);
+ void Disband();
+
+ typedef std::map<uint32, MemberSlot> MemberList;
+ typedef std::vector<RankInfo> RankList;
+
+ uint32 GetId(){ return Id; }
+ const uint64& GetLeader(){ return leaderGuid; }
+ std::string GetName(){ return name; }
+ std::string GetMOTD(){ return MOTD; }
+ std::string GetGINFO(){ return GINFO; }
+
+ uint32 GetCreatedYear(){ return CreatedYear; }
+ uint32 GetCreatedMonth(){ return CreatedMonth; }
+ uint32 GetCreatedDay(){ return CreatedDay; }
+
+ uint32 GetEmblemStyle(){ return EmblemStyle; }
+ uint32 GetEmblemColor(){ return EmblemColor; }
+ uint32 GetBorderStyle(){ return BorderStyle; }
+ uint32 GetBorderColor(){ return BorderColor; }
+ uint32 GetBackgroundColor(){ return BackgroundColor; }
+
+ void SetLeader(uint64 guid);
+ bool AddMember(uint64 plGuid, uint32 plRank);
+ void ChangeRank(uint64 guid, uint32 newRank);
+ void DelMember(uint64 guid, bool isDisbanding=false);
+ uint32 GetLowestRank() const { return GetNrRanks()-1; }
+
+ void SetMOTD(std::string motd);
+ void SetGINFO(std::string ginfo);
+ void SetPNOTE(uint64 guid,std::string pnote);
+ void SetOFFNOTE(uint64 guid,std::string offnote);
+ void SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor, uint32 backgroundColor);
+
+ uint32 GetMemberSize() const { return members.size(); }
+
+ bool LoadGuildFromDB(uint32 GuildId);
+ bool LoadRanksFromDB(uint32 GuildId);
+ bool LoadMembersFromDB(uint32 GuildId);
+
+ bool FillPlayerData(uint64 guid, MemberSlot* memslot);
+ void LoadPlayerStatsByGuid(uint64 guid);
+
+ void BroadcastToGuild(WorldSession *session, std::string msg, uint32 language = LANG_UNIVERSAL);
+ void BroadcastToOfficers(WorldSession *session, std::string msg, uint32 language = LANG_UNIVERSAL);
+ void BroadcastPacketToRank(WorldPacket *packet, uint32 rankId);
+ void BroadcastPacket(WorldPacket *packet);
+
+ void CreateRank(std::string name,uint32 rights);
+ void DelRank();
+ std::string GetRankName(uint32 rankId);
+ uint32 GetRankRights(uint32 rankId);
+ uint32 GetNrRanks() const { return m_ranks.size(); }
+
+ void SetRankName(uint32 rankId, std::string name);
+ void SetRankRights(uint32 rankId, uint32 rights);
+ bool HasRankRight(uint32 rankId, uint32 right)
+ {
+ return ((GetRankRights(rankId) & right) != GR_RIGHT_EMPTY) ? true : false;
+ }
+
+ void Roster(WorldSession *session);
+ void Query(WorldSession *session);
+
+ void UpdateLogoutTime(uint64 guid);
+ // Guild eventlog
+ void LoadGuildEventLogFromDB();
+ void UnloadGuildEventlog();
+ void DisplayGuildEventlog(WorldSession *session);
+ void LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid2, uint8 NewRank);
+ void RenumGuildEventlog();
+
+ // ** Guild bank **
+ // Content & item deposit/withdraw
+ void DisplayGuildBankContent(WorldSession *session, uint8 TabId);
+ void DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2 = -1);
+ void DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots);
+ void DisplayGuildBankMoneyUpdate();
+
+ Item* GetItem(uint8 TabId, uint8 SlotId);
+ uint8 CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32 count, Item *pItem, bool swap = false) const;
+ Item* StoreItem( uint8 tab, GuildItemPosCountVec const& pos, Item *pItem );
+ void RemoveItem(uint8 tab, uint8 slot );
+
+ // Tabs
+ void DisplayGuildBankTabsInfo(WorldSession *session);
+ void CreateNewBankTab();
+ void SetGuildBankTabText(uint8 TabId, std::string text);
+ void SendGuildBankTabText(WorldSession *session, uint8 TabId);
+ void SetGuildBankTabInfo(uint8 TabId, std::string name, std::string icon);
+ void CreateBankRightForTab(uint32 rankid, uint8 TabId);
+ const GuildBankTab *GetBankTab(uint8 index) { if(index >= m_TabListMap.size()) return NULL; return m_TabListMap[index]; }
+ const uint8 GetPurchasedTabs() const { return purchased_tabs; }
+ uint32 GetBankRights(uint32 rankId, uint8 TabId) const;
+ bool IsMemberHaveRights(uint32 LowGuid, uint8 TabId,uint32 rights) const;
+ bool CanMemberViewTab(uint32 LowGuid, uint8 TabId) const;
+ // Load/unload
+ void LoadGuildBankFromDB();
+ void UnloadGuildBank();
+ void IncOnlineMemberCount() { ++m_onlinemembers; }
+ // Money deposit/withdraw
+ void SendMoneyInfo(WorldSession *session, uint32 LowGuid);
+ bool MemberMoneyWithdraw(uint32 amount, uint32 LowGuid);
+ uint64 GetGuildBankMoney() { return guildbank_money; }
+ void SetBankMoney(int64 money);
+ // per days
+ bool MemberItemWithdraw(uint8 TabId, uint32 LowGuid);
+ uint32 GetMemberSlotWithdrawRem(uint32 LowGuid, uint8 TabId);
+ uint32 GetMemberMoneyWithdrawRem(uint32 LowGuid);
+ void SetBankMoneyPerDay(uint32 rankId, uint32 money);
+ void SetBankRightsAndSlots(uint32 rankId, uint8 TabId, uint32 right, uint32 SlotPerDay, bool db);
+ uint32 GetBankMoneyPerDay(uint32 rankId);
+ uint32 GetBankSlotPerDay(uint32 rankId, uint8 TabId);
+ // rights per day
+ void LoadBankRightsFromDB(uint32 GuildId);
+ // logs
+ void LoadGuildBankEventLogFromDB();
+ void UnloadGuildBankEventLog();
+ void DisplayGuildBankLogs(WorldSession *session, uint8 TabId);
+ void LogBankEvent(uint8 LogEntry, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount=0, uint8 DestTabId=0);
+ void RenumBankLogs();
+ bool AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot , uint32 GUIDLow, uint32 Entry );
+
+ protected:
+ void AddRank(std::string name,uint32 rights,uint32 money);
+
+ uint32 Id;
+ std::string name;
+ uint64 leaderGuid;
+ std::string MOTD;
+ std::string GINFO;
+ uint32 CreatedYear;
+ uint32 CreatedMonth;
+ uint32 CreatedDay;
+
+ uint32 EmblemStyle;
+ uint32 EmblemColor;
+ uint32 BorderStyle;
+ uint32 BorderColor;
+ uint32 BackgroundColor;
+
+ RankList m_ranks;
+
+ MemberList members;
+
+ typedef std::vector<GuildBankTab*> TabListMap;
+ TabListMap m_TabListMap;
+
+ /** These are actually ordered lists. The first element is the oldest entry.*/
+ typedef std::list<GuildEventlogEntry*> GuildEventlog;
+ typedef std::list<GuildBankEvent*> GuildBankEventLog;
+ GuildEventlog m_GuildEventlog;
+ GuildBankEventLog m_GuildBankEventLog_Money;
+ GuildBankEventLog m_GuildBankEventLog_Item[GUILD_BANK_MAX_TABS];
+
+ bool m_bankloaded;
+ bool m_eventlogloaded;
+ uint32 m_onlinemembers;
+ uint64 guildbank_money;
+ uint8 purchased_tabs;
+
+ uint32 LogMaxGuid;
+ uint32 GuildEventlogMaxGuid;
+ private:
+ // internal common parts for CanStore/StoreItem functions
+ void AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *tab, int32 slot );
+ uint8 _CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCountVec& dest, uint32& count, bool swap, Item *pSrcItem ) const;
+ uint8 _CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec& dest, uint32& count, bool merge, Item *pSrcItem, uint8 skip_slot ) const;
+ Item* _StoreItem( uint8 tab, uint8 slot, Item *pItem, uint32 count, bool clone );
+};
+#endif
diff --git a/src/game/HomeMovementGenerator.cpp b/src/game/HomeMovementGenerator.cpp
index 79f312d3b8b..399d437707a 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_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;
-}
+/*
+ * 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/ItemHandler.cpp b/src/game/ItemHandler.cpp
index 46a179fd78b..9cd6c2f6265 100644
--- a/src/game/ItemHandler.cpp
+++ b/src/game/ItemHandler.cpp
@@ -1,1211 +1,1211 @@
-/*
- * 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 "Opcodes.h"
-#include "Log.h"
-#include "ObjectMgr.h"
-#include "Player.h"
-#include "Item.h"
-#include "UpdateData.h"
-#include "ObjectAccessor.h"
-
-void WorldSession::HandleSplitItemOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,1+1+1+1+1);
-
- //sLog.outDebug("WORLD: CMSG_SPLIT_ITEM");
- uint8 srcbag, srcslot, dstbag, dstslot, count;
-
- recv_data >> srcbag >> srcslot >> dstbag >> dstslot >> count;
- //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u, count = %u", srcbag, srcslot, dstbag, dstslot, count);
-
- uint16 src = ( (srcbag << 8) | srcslot );
- uint16 dst = ( (dstbag << 8) | dstslot );
-
- if(src==dst)
- return;
-
- if (count==0)
- return; //check count - if zero it's fake packet
-
- _player->SplitItem( src, dst, count );
-}
-
-void WorldSession::HandleSwapInvItemOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,1+1);
-
- //sLog.outDebug("WORLD: CMSG_SWAP_INV_ITEM");
- uint8 srcslot, dstslot;
-
- recv_data >> srcslot >> dstslot;
- //sLog.outDebug("STORAGE: receive srcslot = %u, dstslot = %u", srcslot, dstslot);
-
- // prevent attempt swap same item to current position generated by client at special checting sequence
- if(srcslot==dstslot)
- return;
-
- uint16 src = ( (INVENTORY_SLOT_BAG_0 << 8) | srcslot );
- uint16 dst = ( (INVENTORY_SLOT_BAG_0 << 8) | dstslot );
-
- _player->SwapItem( src, dst );
-}
-
-void WorldSession::HandleAutoEquipItemSlotOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8+1);
- uint64 itemguid;
- uint8 dstslot;
- recv_data >> itemguid >> dstslot;
-
- // cheating attempt, client should never send opcode in that case
- if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot))
- return;
-
- Item* item = _player->GetItemByGuid(itemguid);
- uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8);
-
- if(!item || item->GetPos() == dstpos)
- return;
-
- _player->SwapItem(item->GetPos(), dstpos);
-}
-
-void WorldSession::HandleSwapItem( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,1+1+1+1);
-
- //sLog.outDebug("WORLD: CMSG_SWAP_ITEM");
- uint8 dstbag, dstslot, srcbag, srcslot;
-
- recv_data >> dstbag >> dstslot >> srcbag >> srcslot ;
- //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u", srcbag, srcslot, dstbag, dstslot);
-
- uint16 src = ( (srcbag << 8) | srcslot );
- uint16 dst = ( (dstbag << 8) | dstslot );
-
- // prevent attempt swap same item to current position generated by client at special checting sequence
- if(src==dst)
- return;
-
- _player->SwapItem( src, dst );
-}
-
-void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,1+1);
-
- //sLog.outDebug("WORLD: CMSG_AUTOEQUIP_ITEM");
- uint8 srcbag, srcslot;
-
- recv_data >> srcbag >> srcslot;
- //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
-
- Item *pSrcItem = _player->GetItemByPos( srcbag, srcslot );
- if( !pSrcItem )
- return; // only at cheat
-
- if(pSrcItem->m_lootGenerated) // prevent swap looting item
- {
- //best error message found for attempting to swap while looting
- _player->SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL );
- return;
- }
-
- uint16 dest;
- uint8 msg = _player->CanEquipItem( NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag() );
- if( msg != EQUIP_ERR_OK )
- {
- _player->SendEquipError( msg, pSrcItem, NULL );
- return;
- }
-
- uint16 src = pSrcItem->GetPos();
- if(dest==src) // prevent equip in same slot, only at cheat
- return;
-
- Item *pDstItem = _player->GetItemByPos( dest );
- if( !pDstItem ) // empty slot, simple case
- {
- _player->RemoveItem( srcbag, srcslot, true );
- _player->EquipItem( dest, pSrcItem, true );
- _player->AutoUnequipOffhandIfNeed();
- }
- else // have currently equipped item, not simple case
- {
- uint8 dstbag = pDstItem->GetBagSlot();
- uint8 dstslot = pDstItem->GetSlot();
-
- msg = _player->CanUnequipItem( dest, !pSrcItem->IsBag() );
- if( msg != EQUIP_ERR_OK )
- {
- _player->SendEquipError( msg, pDstItem, NULL );
- return;
- }
-
- // check dest->src move possibility
- ItemPosCountVec sSrc;
- uint16 eSrc;
- if( _player->IsInventoryPos( src ) )
- {
- msg = _player->CanStoreItem( srcbag, srcslot, sSrc, pDstItem, true );
- if( msg != EQUIP_ERR_OK )
- msg = _player->CanStoreItem( srcbag, NULL_SLOT, sSrc, pDstItem, true );
- if( msg != EQUIP_ERR_OK )
- msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true );
- }
- else if( _player->IsBankPos( src ) )
- {
- msg = _player->CanBankItem( srcbag, srcslot, sSrc, pDstItem, true );
- if( msg != EQUIP_ERR_OK )
- msg = _player->CanBankItem( srcbag, NULL_SLOT, sSrc, pDstItem, true );
- if( msg != EQUIP_ERR_OK )
- msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true );
- }
- else if( _player->IsEquipmentPos( src ) )
- {
- msg = _player->CanEquipItem( srcslot, eSrc, pDstItem, true);
- if( msg == EQUIP_ERR_OK )
- msg = _player->CanUnequipItem( eSrc, true);
- }
-
- if( msg != EQUIP_ERR_OK )
- {
- _player->SendEquipError( msg, pDstItem, pSrcItem );
- return;
- }
-
- // now do moves, remove...
- _player->RemoveItem(dstbag, dstslot, false);
- _player->RemoveItem(srcbag, srcslot, false);
-
- // add to dest
- _player->EquipItem(dest, pSrcItem, true);
-
- // add to src
- if( _player->IsInventoryPos( src ) )
- _player->StoreItem(sSrc, pDstItem, true);
- else if( _player->IsBankPos( src ) )
- _player->BankItem(sSrc, pDstItem, true);
- else if( _player->IsEquipmentPos( src ) )
- _player->EquipItem(eSrc, pDstItem, true);
-
- _player->AutoUnequipOffhandIfNeed();
- }
-}
-
-void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1);
-
- //sLog.outDebug("WORLD: CMSG_DESTROYITEM");
- uint8 bag, slot, count, data1, data2, data3;
-
- recv_data >> bag >> slot >> count >> data1 >> data2 >> data3;
- //sLog.outDebug("STORAGE: receive bag = %u, slot = %u, count = %u", bag, slot, count);
-
- uint16 pos = (bag << 8) | slot;
-
- // prevent drop unequipable items (in combat, for example) and non-empty bags
- if(_player->IsEquipmentPos(pos) || _player->IsBagPos(pos))
- {
- uint8 msg = _player->CanUnequipItem( pos, false );
- if( msg != EQUIP_ERR_OK )
- {
- _player->SendEquipError( msg, _player->GetItemByPos(pos), NULL );
- return;
- }
- }
-
- Item *pItem = _player->GetItemByPos( bag, slot );
- if(!pItem)
- {
- _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
- return;
- }
-
- if(count)
- {
- uint32 i_count = count;
- _player->DestroyItemCount( pItem, i_count, true );
- }
- else
- _player->DestroyItem( bag, slot, true );
-}
-
-// Only _static_ data send in this packet !!!
-void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data, 4);
-
- //sLog.outDebug("WORLD: CMSG_ITEM_QUERY_SINGLE");
- uint32 item;
- recv_data >> item;
-
- sLog.outDetail("STORAGE: Item Query = %u", item);
-
- ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
- if( pProto )
- {
- std::string Name = pProto->Name1;
- std::string Description = pProto->Description;
-
- int loc_idx = GetSessionDbLocaleIndex();
- if ( loc_idx >= 0 )
- {
- ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
- if (il)
- {
- if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
- Name = il->Name[loc_idx];
- if (il->Description.size() > loc_idx && !il->Description[loc_idx].empty())
- Description = il->Description[loc_idx];
- }
- }
- // guess size
- WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600);
- data << pProto->ItemId;
- data << pProto->Class;
- data << pProto->SubClass;
- data << uint32(-1); // new 2.0.3, not exist in wdb cache?
- data << Name;
- data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name...
- data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00);
- data << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00);
- data << pProto->DisplayInfoID;
- data << pProto->Quality;
- data << pProto->Flags;
- data << pProto->BuyPrice;
- data << pProto->SellPrice;
- data << pProto->InventoryType;
- data << pProto->AllowableClass;
- data << pProto->AllowableRace;
- data << pProto->ItemLevel;
- data << pProto->RequiredLevel;
- data << pProto->RequiredSkill;
- data << pProto->RequiredSkillRank;
- data << pProto->RequiredSpell;
- data << pProto->RequiredHonorRank;
- data << pProto->RequiredCityRank;
- data << pProto->RequiredReputationFaction;
- data << pProto->RequiredReputationRank;
- data << pProto->MaxCount;
- data << pProto->Stackable;
- data << pProto->ContainerSlots;
- for(int i = 0; i < 10; i++)
- {
- data << pProto->ItemStat[i].ItemStatType;
- data << pProto->ItemStat[i].ItemStatValue;
- }
- for(int i = 0; i < 5; i++)
- {
- data << pProto->Damage[i].DamageMin;
- data << pProto->Damage[i].DamageMax;
- data << pProto->Damage[i].DamageType;
- }
- data << pProto->Armor;
- data << pProto->HolyRes;
- data << pProto->FireRes;
- data << pProto->NatureRes;
- data << pProto->FrostRes;
- data << pProto->ShadowRes;
- data << pProto->ArcaneRes;
- data << pProto->Delay;
- data << pProto->Ammo_type;
-
- data << (float)pProto->RangedModRange;
- for(int s = 0; s < 5; s++)
- {
- // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown
- // use `item_template` or if not set then only use spell cooldowns
- SpellEntry const* spell = sSpellStore.LookupEntry(pProto->Spells[s].SpellId);
- if(spell)
- {
- bool db_data = pProto->Spells[s].SpellCooldown >= 0 || pProto->Spells[s].SpellCategoryCooldown >= 0;
-
- data << pProto->Spells[s].SpellId;
- data << pProto->Spells[s].SpellTrigger;
- data << uint32(-abs(pProto->Spells[s].SpellCharges));
-
- if(db_data)
- {
- data << uint32(pProto->Spells[s].SpellCooldown);
- data << uint32(pProto->Spells[s].SpellCategory);
- data << uint32(pProto->Spells[s].SpellCategoryCooldown);
- }
- else
- {
- data << uint32(spell->RecoveryTime);
- data << uint32(spell->Category);
- data << uint32(spell->CategoryRecoveryTime);
- }
- }
- else
- {
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(-1);
- data << uint32(0);
- data << uint32(-1);
- }
- }
- data << pProto->Bonding;
- data << Description;
- data << pProto->PageText;
- data << pProto->LanguageID;
- data << pProto->PageMaterial;
- data << pProto->StartQuest;
- data << pProto->LockID;
- data << pProto->Material;
- data << pProto->Sheath;
- data << pProto->RandomProperty;
- data << pProto->RandomSuffix;
- data << pProto->Block;
- data << pProto->ItemSet;
- data << pProto->MaxDurability;
- data << pProto->Area;
- data << pProto->Map; // Added in 1.12.x & 2.0.1 client branch
- data << pProto->BagFamily;
- data << pProto->TotemCategory;
- for(int s = 0; s < 3; s++)
- {
- data << pProto->Socket[s].Color;
- data << pProto->Socket[s].Content;
- }
- data << pProto->socketBonus;
- data << pProto->GemProperties;
- data << pProto->RequiredDisenchantSkill;
- data << pProto->ArmorDamageModifier;
- data << uint32(0); // added in 2.4.2.8209, duration (seconds)
- SendPacket( &data );
- }
- else
- {
- sLog.outDebug( "WORLD: CMSG_ITEM_QUERY_SINGLE - NO item INFO! (ENTRY: %u)", item );
- WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 4);
- data << uint32(item | 0x80000000);
- SendPacket( &data );
- }
-}
-
-void WorldSession::HandleReadItem( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,1+1);
-
- //sLog.outDebug( "WORLD: CMSG_READ_ITEM");
-
- uint8 bag, slot;
- recv_data >> bag >> slot;
-
- //sLog.outDetail("STORAGE: Read bag = %u, slot = %u", bag, slot);
- Item *pItem = _player->GetItemByPos( bag, slot );
-
- if( pItem && pItem->GetProto()->PageText )
- {
- WorldPacket data;
-
- uint8 msg = _player->CanUseItem( pItem );
- if( msg == EQUIP_ERR_OK )
- {
- data.Initialize (SMSG_READ_ITEM_OK, 8);
- sLog.outDetail("STORAGE: Item page sent");
- }
- else
- {
- data.Initialize( SMSG_READ_ITEM_FAILED, 8 );
- sLog.outDetail("STORAGE: Unable to read item");
- _player->SendEquipError( msg, pItem, NULL );
- }
- data << pItem->GetGUID();
- SendPacket(&data);
- }
- else
- _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
-}
-
-void WorldSession::HandlePageQuerySkippedOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,4+8);
-
- sLog.outDebug( "WORLD: Received CMSG_PAGE_TEXT_QUERY" );
-
- uint32 itemid;
- uint64 guid;
-
- recv_data >> itemid >> guid;
-
- sLog.outDetail( "Packet Info: itemid: %u guidlow: %u guidentry: %u guidhigh: %u",
- itemid, GUID_LOPART(guid), GUID_ENPART(guid), GUID_HIPART(guid));
-}
-
-void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8+8+1);
-
- sLog.outDebug( "WORLD: Received CMSG_SELL_ITEM" );
- uint64 vendorguid, itemguid;
- uint8 _count;
-
- recv_data >> vendorguid >> itemguid >> _count;
-
- // prevent possible overflow, as mangos uses uint32 for item count
- uint32 count = _count;
-
- if(!itemguid)
- return;
-
- Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
- if (!pCreature)
- {
- sLog.outDebug( "WORLD: HandleSellItemOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
- _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, itemguid, 0);
- return;
- }
-
- // remove fake death
- if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
- GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- Item *pItem = _player->GetItemByGuid( itemguid );
- if( pItem )
- {
- // prevent sell not owner item
- if(_player->GetGUID()!=pItem->GetOwnerGUID())
- {
- _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
- return;
- }
-
- // prevent sell non empty bag by drag-and-drop at vendor's item list
- if(pItem->IsBag() && !((Bag*)pItem)->IsEmpty())
- {
- _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
- return;
- }
-
- // prevent sell currently looted item
- if(_player->GetLootGUID()==pItem->GetGUID())
- {
- _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
- return;
- }
-
- // special case at auto sell (sell all)
- if(count==0)
- {
- count = pItem->GetCount();
- }
- else
- {
- // prevent sell more items that exist in stack (possable only not from client)
- if(count > pItem->GetCount())
- {
- _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
- return;
- }
- }
-
- ItemPrototype const *pProto = pItem->GetProto();
- if( pProto )
- {
- if( pProto->SellPrice > 0 )
- {
- if(count < pItem->GetCount()) // need split items
- {
- Item *pNewItem = pItem->CloneItem( count, _player );
- if (!pNewItem)
- {
- sLog.outError("WORLD: HandleSellItemOpcode - could not create clone of item %u; count = %u", pItem->GetEntry(), count );
- _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
- return;
- }
-
- pItem->SetCount( pItem->GetCount() - count );
- _player->ItemRemovedQuestCheck( pItem->GetEntry(), count );
- if( _player->IsInWorld() )
- pItem->SendUpdateToPlayer( _player );
- pItem->SetState(ITEM_CHANGED, _player);
-
- _player->AddItemToBuyBackSlot( pNewItem );
- if( _player->IsInWorld() )
- pNewItem->SendUpdateToPlayer( _player );
- }
- else
- {
- _player->ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount());
- _player->RemoveItem( pItem->GetBagSlot(), pItem->GetSlot(), true);
- pItem->RemoveFromUpdateQueueOf(_player);
- _player->AddItemToBuyBackSlot( pItem );
- }
-
- _player->ModifyMoney( pProto->SellPrice * count );
- }
- else
- _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
- return;
- }
- }
- _player->SendSellError( SELL_ERR_CANT_FIND_ITEM, pCreature, itemguid, 0);
- return;
-}
-
-void WorldSession::HandleBuybackItem(WorldPacket & recv_data)
-{
- CHECK_PACKET_SIZE(recv_data,8+4);
-
- sLog.outDebug( "WORLD: Received CMSG_BUYBACK_ITEM" );
- uint64 vendorguid;
- uint32 slot;
-
- recv_data >> vendorguid >> slot;
-
- Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
- if (!pCreature)
- {
- sLog.outDebug( "WORLD: HandleBuybackItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
- _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
- return;
- }
-
- // remove fake death
- if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
- GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- Item *pItem = _player->GetItemFromBuyBackSlot( slot );
- if( pItem )
- {
- uint32 price = _player->GetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START );
- if( _player->GetMoney() < price )
- {
- _player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, pItem->GetEntry(), 0);
- return;
- }
-
- ItemPosCountVec dest;
- uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
- if( msg == EQUIP_ERR_OK )
- {
- _player->ModifyMoney( -(int32)price );
- _player->RemoveItemFromBuyBackSlot( slot, false );
- _player->ItemAddedQuestCheck( pItem->GetEntry(), pItem->GetCount());
- _player->StoreItem( dest, pItem, true );
- }
- else
- _player->SendEquipError( msg, pItem, NULL );
- return;
- }
- else
- _player->SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, 0, 0);
-}
-
-void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8+4+8+1+1);
-
- sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM_IN_SLOT" );
- uint64 vendorguid, bagguid;
- uint32 item;
- uint8 slot, count;
-
- recv_data >> vendorguid >> item >> bagguid >> slot >> count;
-
- GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bagguid,slot);
-}
-
-void WorldSession::HandleBuyItemOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8+4+1+1);
-
- sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM" );
- uint64 vendorguid;
- uint32 item;
- uint8 count, unk1;
-
- recv_data >> vendorguid >> item >> count >> unk1;
-
- GetPlayer()->BuyItemFromVendor(vendorguid,item,count,NULL_BAG,NULL_SLOT);
-}
-
-void WorldSession::HandleListInventoryOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- uint64 guid;
-
- recv_data >> guid;
-
- if(!GetPlayer()->isAlive())
- return;
-
- sLog.outDebug( "WORLD: Recvd CMSG_LIST_INVENTORY" );
-
- SendListInventory( guid );
-}
-
-void WorldSession::SendListInventory( uint64 vendorguid )
-{
- sLog.outDebug( "WORLD: Sent SMSG_LIST_INVENTORY" );
-
- Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
- if (!pCreature)
- {
- sLog.outDebug( "WORLD: SendListInventory - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
- _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
- return;
- }
-
- // remove fake death
- if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
- GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- // Stop the npc if moving
- pCreature->StopMoving();
-
- VendorItemData const* vItems = pCreature->GetVendorItems();
- if(!vItems)
- {
- _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
- return;
- }
-
- uint8 numitems = vItems->GetItemCount();
- uint8 count = 0;
-
- WorldPacket data( SMSG_LIST_INVENTORY, (8+1+numitems*8*4) );
- data << uint64(vendorguid);
- data << uint8(numitems);
-
- float discountMod = _player->GetReputationPriceDiscount(pCreature);
-
- for(int i = 0; i < numitems; i++ )
- {
- if(VendorItem const* crItem = vItems->GetItem(i))
- {
- if(ItemPrototype const *pProto = objmgr.GetItemPrototype(crItem->item))
- {
- if((pProto->AllowableClass & _player->getClassMask()) == 0 && pProto->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster())
- continue;
-
- ++count;
-
- // reputation discount
- uint32 price = uint32(floor(pProto->BuyPrice * discountMod));
-
- data << uint32(count);
- data << uint32(crItem->item);
- data << uint32(pProto->DisplayInfoID);
- data << uint32(crItem->maxcount <= 0 ? 0xFFFFFFFF : pCreature->GetVendorItemCurrentCount(crItem));
- data << uint32(price);
- data << uint32(pProto->MaxDurability);
- data << uint32(pProto->BuyCount);
- data << uint32(crItem->ExtendedCost);
- }
- }
- }
-
- if ( count == 0 || data.size() != 8 + 1 + size_t(count) * 8 * 4 )
- return;
-
- data.put<uint8>(8, count);
- SendPacket( &data );
-}
-
-void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,1+1+1);
-
- //sLog.outDebug("WORLD: CMSG_AUTOSTORE_BAG_ITEM");
- uint8 srcbag, srcslot, dstbag;
-
- recv_data >> srcbag >> srcslot >> dstbag;
- //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u", srcbag, srcslot, dstbag);
-
- Item *pItem = _player->GetItemByPos( srcbag, srcslot );
- if( !pItem )
- return;
-
- uint16 src = pItem->GetPos();
-
- // check unequip potability for equipped items and bank bags
- if(_player->IsEquipmentPos ( src ) || _player->IsBagPos ( src ))
- {
- uint8 msg = _player->CanUnequipItem( src, !_player->IsBagPos ( src ));
- if(msg != EQUIP_ERR_OK)
- {
- _player->SendEquipError( msg, pItem, NULL );
- return;
- }
- }
-
- ItemPosCountVec dest;
- uint8 msg = _player->CanStoreItem( dstbag, NULL_SLOT, dest, pItem, false );
- if( msg != EQUIP_ERR_OK )
- {
- _player->SendEquipError( msg, pItem, NULL );
- return;
- }
-
- // no-op: placed in same slot
- if(dest.size()==1 && dest[0].pos==src)
- {
- // just remove grey item state
- _player->SendEquipError( EQUIP_ERR_NONE, pItem, NULL );
- return;
- }
-
- _player->RemoveItem(srcbag, srcslot, true );
- _player->StoreItem( dest, pItem, true );
-}
-
-void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& /*recvPacket*/)
-{
- sLog.outDebug("WORLD: CMSG_BUY_BANK_SLOT");
-
- uint32 slot = _player->GetByteValue(PLAYER_BYTES_2, 2);
-
- // next slot
- ++slot;
-
- sLog.outDetail("PLAYER: Buy bank bag slot, slot number = %u", slot);
-
- BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot);
-
- if(!slotEntry)
- return;
-
- uint32 price = slotEntry->price;
-
- if (_player->GetMoney() < price)
- return;
-
- _player->SetByteValue(PLAYER_BYTES_2, 2, slot);
- _player->ModifyMoney(-int32(price));
-}
-
-void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket)
-{
- CHECK_PACKET_SIZE(recvPacket,1+1);
-
- sLog.outDebug("WORLD: CMSG_AUTOBANK_ITEM");
- uint8 srcbag, srcslot;
-
- recvPacket >> srcbag >> srcslot;
- sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
-
- Item *pItem = _player->GetItemByPos( srcbag, srcslot );
- if( !pItem )
- return;
-
- ItemPosCountVec dest;
- uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
- if( msg != EQUIP_ERR_OK )
- {
- _player->SendEquipError( msg, pItem, NULL );
- return;
- }
-
- _player->RemoveItem(srcbag, srcslot, true);
- _player->BankItem( dest, pItem, true );
-}
-
-void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
-{
- CHECK_PACKET_SIZE(recvPacket,1+1);
-
- sLog.outDebug("WORLD: CMSG_AUTOSTORE_BANK_ITEM");
- uint8 srcbag, srcslot;
-
- recvPacket >> srcbag >> srcslot;
- sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
-
- Item *pItem = _player->GetItemByPos( srcbag, srcslot );
- if( !pItem )
- return;
-
- if(_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory
- {
- ItemPosCountVec dest;
- uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
- if( msg != EQUIP_ERR_OK )
- {
- _player->SendEquipError( msg, pItem, NULL );
- return;
- }
-
- _player->RemoveItem(srcbag, srcslot, true);
- _player->StoreItem( dest, pItem, true );
- }
- else // moving from inventory to bank
- {
- ItemPosCountVec dest;
- uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
- if( msg != EQUIP_ERR_OK )
- {
- _player->SendEquipError( msg, pItem, NULL );
- return;
- }
-
- _player->RemoveItem(srcbag, srcslot, true);
- _player->BankItem( dest, pItem, true );
- }
-}
-
-void WorldSession::HandleSetAmmoOpcode(WorldPacket & recv_data)
-{
- CHECK_PACKET_SIZE(recv_data,4);
-
- if(!GetPlayer()->isAlive())
- {
- GetPlayer()->SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, NULL, NULL );
- return;
- }
-
- sLog.outDebug("WORLD: CMSG_SET_AMMO");
- uint32 item;
-
- recv_data >> item;
-
- if(!item)
- GetPlayer()->RemoveAmmo();
- else
- GetPlayer()->SetAmmo(item);
-}
-
-void WorldSession::SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID,uint32 SpellID)
-{
- WorldPacket data(SMSG_ENCHANTMENTLOG, (8+8+4+4+1)); // last check 2.0.10
- data << Target;
- data << Caster;
- data << ItemID;
- data << SpellID;
- data << uint8(0);
- SendPacket(&data);
-}
-
-void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration)
-{
- // last check 2.0.10
- WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8+4+4+8));
- data << uint64(Itemguid);
- data << uint32(slot);
- data << uint32(Duration);
- data << uint64(Playerguid);
- SendPacket(&data);
-}
-
-void WorldSession::HandleItemNameQueryOpcode(WorldPacket & recv_data)
-{
- CHECK_PACKET_SIZE(recv_data,4);
-
- uint32 itemid;
- recv_data >> itemid;
- sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY %u", itemid);
- ItemPrototype const *pProto = objmgr.GetItemPrototype( itemid );
- if( pProto )
- {
- std::string Name;
- Name = pProto->Name1;
-
- int loc_idx = GetSessionDbLocaleIndex();
- if (loc_idx >= 0)
- {
- ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
- if (il)
- {
- if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
- Name = il->Name[loc_idx];
- }
- }
- // guess size
- WorldPacket data(SMSG_ITEM_NAME_QUERY_RESPONSE, (4+10));
- data << uint32(pProto->ItemId);
- data << Name;
- data << uint32(pProto->InventoryType);
- SendPacket(&data);
- return;
- }
- else
- sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY for item %u failed (unknown item)", itemid);
-}
-
-void WorldSession::HandleWrapItemOpcode(WorldPacket& recv_data)
-{
- CHECK_PACKET_SIZE(recv_data,1+1+1+1);
-
- sLog.outDebug("Received opcode CMSG_WRAP_ITEM");
-
- uint8 gift_bag, gift_slot, item_bag, item_slot;
- //recv_data.hexlike();
-
- recv_data >> gift_bag >> gift_slot; // paper
- recv_data >> item_bag >> item_slot; // item
-
- sLog.outDebug("WRAP: receive gift_bag = %u, gift_slot = %u, item_bag = %u, item_slot = %u", gift_bag, gift_slot, item_bag, item_slot);
-
- Item *gift = _player->GetItemByPos( gift_bag, gift_slot );
- if(!gift)
- {
- _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL );
- return;
- }
-
- if(!gift->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPER))// cheating: non-wrapper wrapper
- {
- _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL );
- return;
- }
-
- Item *item = _player->GetItemByPos( item_bag, item_slot );
-
- if( !item )
- {
- _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, item, NULL );
- return;
- }
-
- if(item==gift) // not possable with pacjket from real client
- {
- _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL );
- return;
- }
-
- if(item->IsEquipped())
- {
- _player->SendEquipError( EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED, item, NULL );
- return;
- }
-
- if(item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR)) // HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED);
- {
- _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL );
- return;
- }
-
- if(item->IsBag())
- {
- _player->SendEquipError( EQUIP_ERR_BAGS_CANT_BE_WRAPPED, item, NULL );
- return;
- }
-
- if(item->IsSoulBound())
- {
- _player->SendEquipError( EQUIP_ERR_BOUND_CANT_BE_WRAPPED, item, NULL );
- return;
- }
-
- if(item->GetMaxStackCount() != 1)
- {
- _player->SendEquipError( EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED, item, NULL );
- return;
- }
-
- // maybe not correct check (it is better than nothing)
- if(item->GetProto()->MaxCount>0)
- {
- _player->SendEquipError( EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED, item, NULL );
- return;
- }
-
- CharacterDatabase.BeginTransaction();
- CharacterDatabase.PExecute("INSERT INTO character_gifts VALUES ('%u', '%u', '%u', '%u')", GUID_LOPART(item->GetOwnerGUID()), item->GetGUIDLow(), item->GetEntry(), item->GetUInt32Value(ITEM_FIELD_FLAGS));
- item->SetUInt32Value(OBJECT_FIELD_ENTRY, gift->GetUInt32Value(OBJECT_FIELD_ENTRY));
-
- switch (item->GetEntry())
- {
- case 5042: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5043); break;
- case 5048: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5044); break;
- case 17303: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17302); break;
- case 17304: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17305); break;
- case 17307: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17308); break;
- case 21830: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 21831); break;
- }
- item->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID());
- item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED);
- item->SetState(ITEM_CHANGED, _player);
-
- if(item->GetState()==ITEM_NEW) // save new item, to have alway for `character_gifts` record in `item_instance`
- {
- // after save it will be impossible to remove the item from the queue
- item->RemoveFromUpdateQueueOf(_player);
- item->SaveToDB(); // item gave inventory record unchanged and can be save standalone
- }
- CharacterDatabase.CommitTransaction();
-
- uint32 count = 1;
- _player->DestroyItemCount(gift, count, true);
-}
-
-void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
-{
- sLog.outDebug("WORLD: CMSG_SOCKET_GEMS");
-
- CHECK_PACKET_SIZE(recv_data,8*4);
-
- uint64 guids[4];
- uint32 GemEnchants[3], OldEnchants[3];
- Item *Gems[3];
- bool SocketBonusActivated, SocketBonusToBeActivated;
-
- for(int i = 0; i < 4; i++)
- recv_data >> guids[i];
-
- if(!guids[0])
- return;
-
- //cheat -> tried to socket same gem multiple times
- if((guids[1] && (guids[1] == guids[2] || guids[1] == guids[3])) || (guids[2] && (guids[2] == guids[3])))
- return;
-
- Item *itemTarget = _player->GetItemByGuid(guids[0]);
- if(!itemTarget) //missing item to socket
- return;
-
- //this slot is excepted when applying / removing meta gem bonus
- uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : NULL_SLOT;
-
- for(int i = 0; i < 3; i++)
- Gems[i] = guids[i + 1] ? _player->GetItemByGuid(guids[i + 1]) : NULL;
-
- GemPropertiesEntry const *GemProps[3];
- for(int i = 0; i < 3; ++i) //get geminfo from dbc storage
- {
- GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetProto()->GemProperties) : NULL;
- }
-
- for(int i = 0; i < 3; ++i) //check for hack maybe
- {
- // tried to put gem in socket where no socket exists / tried to put normal gem in meta socket
- // tried to put meta gem in normal socket
- if( GemProps[i] && ( !itemTarget->GetProto()->Socket[i].Color ||
- itemTarget->GetProto()->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META ||
- itemTarget->GetProto()->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META ) )
- return;
- }
-
- for(int i = 0; i < 3; ++i) //get new and old enchantments
- {
- GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0;
- OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i));
- }
-
- // check unique-equipped conditions
- for(int i = 0; i < 3; ++i)
- {
- if (Gems[i] && (Gems[i]->GetProto()->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED))
- {
- // for equipped item check all equipment for duplicate equipped gems
- if(itemTarget->IsEquipped())
- {
- if(GetPlayer()->GetItemOrItemWithGemEquipped(Gems[i]->GetEntry()))
- {
- _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE, itemTarget, NULL );
- return;
- }
- }
-
- // continue check for case when attempt add 2 similar unique equipped gems in one item.
- for (int j = 0; j < 3; ++j)
- {
- if ((i != j) && (Gems[j]) && (Gems[i]->GetProto()->ItemId == Gems[j]->GetProto()->ItemId))
- {
- _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
- return;
- }
- }
- for (int j = 0; j < 3; ++j)
- {
- if (OldEnchants[j])
- {
- SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]);
- if(!enchantEntry)
- continue;
-
- if ((enchantEntry->GemID == Gems[i]->GetProto()->ItemId) && (i != j))
- {
- _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
- return;
- }
- }
- }
- }
- }
-
- SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus
- _player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item)
-
- //if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met
-
- //remove ALL enchants
- for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
- _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),false);
-
- for(int i = 0; i < 3; ++i)
- {
- if(GemEnchants[i])
- {
- itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i],0,0);
- if(Item* guidItem = _player->GetItemByGuid(guids[i + 1]))
- _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true );
- }
- }
-
- for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
- _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),true);
-
- SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state
- if(SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change...
- {
- _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,false);
- itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetProto()->socketBonus : 0), 0, 0);
- _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,true);
- //it is not displayed, client has an inbuilt system to determine if the bonus is activated
- }
-
- _player->ToggleMetaGemsActive(slot, true); //turn on all metagems (except for target item)
-}
-
-void WorldSession::HandleCancelTempItemEnchantmentOpcode(WorldPacket& recv_data)
-{
- sLog.outDebug("WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT");
-
- CHECK_PACKET_SIZE(recv_data,4);
-
- uint32 eslot;
-
- recv_data >> eslot;
-
- // apply only to equipped item
- if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0,eslot))
- return;
-
- Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot);
-
- if(!item)
- return;
-
- if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
- return;
-
- GetPlayer()->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false);
- item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
-}
+/*
+ * 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 "Opcodes.h"
+#include "Log.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Item.h"
+#include "UpdateData.h"
+#include "ObjectAccessor.h"
+
+void WorldSession::HandleSplitItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_SPLIT_ITEM");
+ uint8 srcbag, srcslot, dstbag, dstslot, count;
+
+ recv_data >> srcbag >> srcslot >> dstbag >> dstslot >> count;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u, count = %u", srcbag, srcslot, dstbag, dstslot, count);
+
+ uint16 src = ( (srcbag << 8) | srcslot );
+ uint16 dst = ( (dstbag << 8) | dstslot );
+
+ if(src==dst)
+ return;
+
+ if (count==0)
+ return; //check count - if zero it's fake packet
+
+ _player->SplitItem( src, dst, count );
+}
+
+void WorldSession::HandleSwapInvItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ //sLog.outDebug("WORLD: CMSG_SWAP_INV_ITEM");
+ uint8 srcslot, dstslot;
+
+ recv_data >> srcslot >> dstslot;
+ //sLog.outDebug("STORAGE: receive srcslot = %u, dstslot = %u", srcslot, dstslot);
+
+ // prevent attempt swap same item to current position generated by client at special checting sequence
+ if(srcslot==dstslot)
+ return;
+
+ uint16 src = ( (INVENTORY_SLOT_BAG_0 << 8) | srcslot );
+ uint16 dst = ( (INVENTORY_SLOT_BAG_0 << 8) | dstslot );
+
+ _player->SwapItem( src, dst );
+}
+
+void WorldSession::HandleAutoEquipItemSlotOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+1);
+ uint64 itemguid;
+ uint8 dstslot;
+ recv_data >> itemguid >> dstslot;
+
+ // cheating attempt, client should never send opcode in that case
+ if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot))
+ return;
+
+ Item* item = _player->GetItemByGuid(itemguid);
+ uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8);
+
+ if(!item || item->GetPos() == dstpos)
+ return;
+
+ _player->SwapItem(item->GetPos(), dstpos);
+}
+
+void WorldSession::HandleSwapItem( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_SWAP_ITEM");
+ uint8 dstbag, dstslot, srcbag, srcslot;
+
+ recv_data >> dstbag >> dstslot >> srcbag >> srcslot ;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u", srcbag, srcslot, dstbag, dstslot);
+
+ uint16 src = ( (srcbag << 8) | srcslot );
+ uint16 dst = ( (dstbag << 8) | dstslot );
+
+ // prevent attempt swap same item to current position generated by client at special checting sequence
+ if(src==dst)
+ return;
+
+ _player->SwapItem( src, dst );
+}
+
+void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ //sLog.outDebug("WORLD: CMSG_AUTOEQUIP_ITEM");
+ uint8 srcbag, srcslot;
+
+ recv_data >> srcbag >> srcslot;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
+
+ Item *pSrcItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pSrcItem )
+ return; // only at cheat
+
+ if(pSrcItem->m_lootGenerated) // prevent swap looting item
+ {
+ //best error message found for attempting to swap while looting
+ _player->SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL );
+ return;
+ }
+
+ uint16 dest;
+ uint8 msg = _player->CanEquipItem( NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag() );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ uint16 src = pSrcItem->GetPos();
+ if(dest==src) // prevent equip in same slot, only at cheat
+ return;
+
+ Item *pDstItem = _player->GetItemByPos( dest );
+ if( !pDstItem ) // empty slot, simple case
+ {
+ _player->RemoveItem( srcbag, srcslot, true );
+ _player->EquipItem( dest, pSrcItem, true );
+ _player->AutoUnequipOffhandIfNeed();
+ }
+ else // have currently equipped item, not simple case
+ {
+ uint8 dstbag = pDstItem->GetBagSlot();
+ uint8 dstslot = pDstItem->GetSlot();
+
+ msg = _player->CanUnequipItem( dest, !pSrcItem->IsBag() );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pDstItem, NULL );
+ return;
+ }
+
+ // check dest->src move possibility
+ ItemPosCountVec sSrc;
+ uint16 eSrc;
+ if( _player->IsInventoryPos( src ) )
+ {
+ msg = _player->CanStoreItem( srcbag, srcslot, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanStoreItem( srcbag, NULL_SLOT, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true );
+ }
+ else if( _player->IsBankPos( src ) )
+ {
+ msg = _player->CanBankItem( srcbag, srcslot, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanBankItem( srcbag, NULL_SLOT, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true );
+ }
+ else if( _player->IsEquipmentPos( src ) )
+ {
+ msg = _player->CanEquipItem( srcslot, eSrc, pDstItem, true);
+ if( msg == EQUIP_ERR_OK )
+ msg = _player->CanUnequipItem( eSrc, true);
+ }
+
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pDstItem, pSrcItem );
+ return;
+ }
+
+ // now do moves, remove...
+ _player->RemoveItem(dstbag, dstslot, false);
+ _player->RemoveItem(srcbag, srcslot, false);
+
+ // add to dest
+ _player->EquipItem(dest, pSrcItem, true);
+
+ // add to src
+ if( _player->IsInventoryPos( src ) )
+ _player->StoreItem(sSrc, pDstItem, true);
+ else if( _player->IsBankPos( src ) )
+ _player->BankItem(sSrc, pDstItem, true);
+ else if( _player->IsEquipmentPos( src ) )
+ _player->EquipItem(eSrc, pDstItem, true);
+
+ _player->AutoUnequipOffhandIfNeed();
+ }
+}
+
+void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_DESTROYITEM");
+ uint8 bag, slot, count, data1, data2, data3;
+
+ recv_data >> bag >> slot >> count >> data1 >> data2 >> data3;
+ //sLog.outDebug("STORAGE: receive bag = %u, slot = %u, count = %u", bag, slot, count);
+
+ uint16 pos = (bag << 8) | slot;
+
+ // prevent drop unequipable items (in combat, for example) and non-empty bags
+ if(_player->IsEquipmentPos(pos) || _player->IsBagPos(pos))
+ {
+ uint8 msg = _player->CanUnequipItem( pos, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, _player->GetItemByPos(pos), NULL );
+ return;
+ }
+ }
+
+ Item *pItem = _player->GetItemByPos( bag, slot );
+ if(!pItem)
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return;
+ }
+
+ if(count)
+ {
+ uint32 i_count = count;
+ _player->DestroyItemCount( pItem, i_count, true );
+ }
+ else
+ _player->DestroyItem( bag, slot, true );
+}
+
+// Only _static_ data send in this packet !!!
+void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ //sLog.outDebug("WORLD: CMSG_ITEM_QUERY_SINGLE");
+ uint32 item;
+ recv_data >> item;
+
+ sLog.outDetail("STORAGE: Item Query = %u", item);
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
+ if( pProto )
+ {
+ std::string Name = pProto->Name1;
+ std::string Description = pProto->Description;
+
+ int loc_idx = GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
+ if (il)
+ {
+ if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
+ Name = il->Name[loc_idx];
+ if (il->Description.size() > loc_idx && !il->Description[loc_idx].empty())
+ Description = il->Description[loc_idx];
+ }
+ }
+ // guess size
+ WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600);
+ data << pProto->ItemId;
+ data << pProto->Class;
+ data << pProto->SubClass;
+ data << uint32(-1); // new 2.0.3, not exist in wdb cache?
+ data << Name;
+ data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name...
+ data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00);
+ data << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00);
+ data << pProto->DisplayInfoID;
+ data << pProto->Quality;
+ data << pProto->Flags;
+ data << pProto->BuyPrice;
+ data << pProto->SellPrice;
+ data << pProto->InventoryType;
+ data << pProto->AllowableClass;
+ data << pProto->AllowableRace;
+ data << pProto->ItemLevel;
+ data << pProto->RequiredLevel;
+ data << pProto->RequiredSkill;
+ data << pProto->RequiredSkillRank;
+ data << pProto->RequiredSpell;
+ data << pProto->RequiredHonorRank;
+ data << pProto->RequiredCityRank;
+ data << pProto->RequiredReputationFaction;
+ data << pProto->RequiredReputationRank;
+ data << pProto->MaxCount;
+ data << pProto->Stackable;
+ data << pProto->ContainerSlots;
+ for(int i = 0; i < 10; i++)
+ {
+ data << pProto->ItemStat[i].ItemStatType;
+ data << pProto->ItemStat[i].ItemStatValue;
+ }
+ for(int i = 0; i < 5; i++)
+ {
+ data << pProto->Damage[i].DamageMin;
+ data << pProto->Damage[i].DamageMax;
+ data << pProto->Damage[i].DamageType;
+ }
+ data << pProto->Armor;
+ data << pProto->HolyRes;
+ data << pProto->FireRes;
+ data << pProto->NatureRes;
+ data << pProto->FrostRes;
+ data << pProto->ShadowRes;
+ data << pProto->ArcaneRes;
+ data << pProto->Delay;
+ data << pProto->Ammo_type;
+
+ data << (float)pProto->RangedModRange;
+ for(int s = 0; s < 5; s++)
+ {
+ // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown
+ // use `item_template` or if not set then only use spell cooldowns
+ SpellEntry const* spell = sSpellStore.LookupEntry(pProto->Spells[s].SpellId);
+ if(spell)
+ {
+ bool db_data = pProto->Spells[s].SpellCooldown >= 0 || pProto->Spells[s].SpellCategoryCooldown >= 0;
+
+ data << pProto->Spells[s].SpellId;
+ data << pProto->Spells[s].SpellTrigger;
+ data << uint32(-abs(pProto->Spells[s].SpellCharges));
+
+ if(db_data)
+ {
+ data << uint32(pProto->Spells[s].SpellCooldown);
+ data << uint32(pProto->Spells[s].SpellCategory);
+ data << uint32(pProto->Spells[s].SpellCategoryCooldown);
+ }
+ else
+ {
+ data << uint32(spell->RecoveryTime);
+ data << uint32(spell->Category);
+ data << uint32(spell->CategoryRecoveryTime);
+ }
+ }
+ else
+ {
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(-1);
+ data << uint32(0);
+ data << uint32(-1);
+ }
+ }
+ data << pProto->Bonding;
+ data << Description;
+ data << pProto->PageText;
+ data << pProto->LanguageID;
+ data << pProto->PageMaterial;
+ data << pProto->StartQuest;
+ data << pProto->LockID;
+ data << pProto->Material;
+ data << pProto->Sheath;
+ data << pProto->RandomProperty;
+ data << pProto->RandomSuffix;
+ data << pProto->Block;
+ data << pProto->ItemSet;
+ data << pProto->MaxDurability;
+ data << pProto->Area;
+ data << pProto->Map; // Added in 1.12.x & 2.0.1 client branch
+ data << pProto->BagFamily;
+ data << pProto->TotemCategory;
+ for(int s = 0; s < 3; s++)
+ {
+ data << pProto->Socket[s].Color;
+ data << pProto->Socket[s].Content;
+ }
+ data << pProto->socketBonus;
+ data << pProto->GemProperties;
+ data << pProto->RequiredDisenchantSkill;
+ data << pProto->ArmorDamageModifier;
+ data << uint32(0); // added in 2.4.2.8209, duration (seconds)
+ SendPacket( &data );
+ }
+ else
+ {
+ sLog.outDebug( "WORLD: CMSG_ITEM_QUERY_SINGLE - NO item INFO! (ENTRY: %u)", item );
+ WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 4);
+ data << uint32(item | 0x80000000);
+ SendPacket( &data );
+ }
+}
+
+void WorldSession::HandleReadItem( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ //sLog.outDebug( "WORLD: CMSG_READ_ITEM");
+
+ uint8 bag, slot;
+ recv_data >> bag >> slot;
+
+ //sLog.outDetail("STORAGE: Read bag = %u, slot = %u", bag, slot);
+ Item *pItem = _player->GetItemByPos( bag, slot );
+
+ if( pItem && pItem->GetProto()->PageText )
+ {
+ WorldPacket data;
+
+ uint8 msg = _player->CanUseItem( pItem );
+ if( msg == EQUIP_ERR_OK )
+ {
+ data.Initialize (SMSG_READ_ITEM_OK, 8);
+ sLog.outDetail("STORAGE: Item page sent");
+ }
+ else
+ {
+ data.Initialize( SMSG_READ_ITEM_FAILED, 8 );
+ sLog.outDetail("STORAGE: Unable to read item");
+ _player->SendEquipError( msg, pItem, NULL );
+ }
+ data << pItem->GetGUID();
+ SendPacket(&data);
+ }
+ else
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+}
+
+void WorldSession::HandlePageQuerySkippedOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+8);
+
+ sLog.outDebug( "WORLD: Received CMSG_PAGE_TEXT_QUERY" );
+
+ uint32 itemid;
+ uint64 guid;
+
+ recv_data >> itemid >> guid;
+
+ sLog.outDetail( "Packet Info: itemid: %u guidlow: %u guidentry: %u guidhigh: %u",
+ itemid, GUID_LOPART(guid), GUID_ENPART(guid), GUID_HIPART(guid));
+}
+
+void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+8+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_SELL_ITEM" );
+ uint64 vendorguid, itemguid;
+ uint8 _count;
+
+ recv_data >> vendorguid >> itemguid >> _count;
+
+ // prevent possible overflow, as mangos uses uint32 for item count
+ uint32 count = _count;
+
+ if(!itemguid)
+ return;
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleSellItemOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, itemguid, 0);
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ Item *pItem = _player->GetItemByGuid( itemguid );
+ if( pItem )
+ {
+ // prevent sell not owner item
+ if(_player->GetGUID()!=pItem->GetOwnerGUID())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ // prevent sell non empty bag by drag-and-drop at vendor's item list
+ if(pItem->IsBag() && !((Bag*)pItem)->IsEmpty())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ // prevent sell currently looted item
+ if(_player->GetLootGUID()==pItem->GetGUID())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ // special case at auto sell (sell all)
+ if(count==0)
+ {
+ count = pItem->GetCount();
+ }
+ else
+ {
+ // prevent sell more items that exist in stack (possable only not from client)
+ if(count > pItem->GetCount())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+ }
+
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( pProto )
+ {
+ if( pProto->SellPrice > 0 )
+ {
+ if(count < pItem->GetCount()) // need split items
+ {
+ Item *pNewItem = pItem->CloneItem( count, _player );
+ if (!pNewItem)
+ {
+ sLog.outError("WORLD: HandleSellItemOpcode - could not create clone of item %u; count = %u", pItem->GetEntry(), count );
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ pItem->SetCount( pItem->GetCount() - count );
+ _player->ItemRemovedQuestCheck( pItem->GetEntry(), count );
+ if( _player->IsInWorld() )
+ pItem->SendUpdateToPlayer( _player );
+ pItem->SetState(ITEM_CHANGED, _player);
+
+ _player->AddItemToBuyBackSlot( pNewItem );
+ if( _player->IsInWorld() )
+ pNewItem->SendUpdateToPlayer( _player );
+ }
+ else
+ {
+ _player->ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount());
+ _player->RemoveItem( pItem->GetBagSlot(), pItem->GetSlot(), true);
+ pItem->RemoveFromUpdateQueueOf(_player);
+ _player->AddItemToBuyBackSlot( pItem );
+ }
+
+ _player->ModifyMoney( pProto->SellPrice * count );
+ }
+ else
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+ }
+ _player->SendSellError( SELL_ERR_CANT_FIND_ITEM, pCreature, itemguid, 0);
+ return;
+}
+
+void WorldSession::HandleBuybackItem(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ sLog.outDebug( "WORLD: Received CMSG_BUYBACK_ITEM" );
+ uint64 vendorguid;
+ uint32 slot;
+
+ recv_data >> vendorguid >> slot;
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleBuybackItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ Item *pItem = _player->GetItemFromBuyBackSlot( slot );
+ if( pItem )
+ {
+ uint32 price = _player->GetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START );
+ if( _player->GetMoney() < price )
+ {
+ _player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, pItem->GetEntry(), 0);
+ return;
+ }
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ _player->ModifyMoney( -(int32)price );
+ _player->RemoveItemFromBuyBackSlot( slot, false );
+ _player->ItemAddedQuestCheck( pItem->GetEntry(), pItem->GetCount());
+ _player->StoreItem( dest, pItem, true );
+ }
+ else
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+ else
+ _player->SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, 0, 0);
+}
+
+void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+8+1+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM_IN_SLOT" );
+ uint64 vendorguid, bagguid;
+ uint32 item;
+ uint8 slot, count;
+
+ recv_data >> vendorguid >> item >> bagguid >> slot >> count;
+
+ GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bagguid,slot);
+}
+
+void WorldSession::HandleBuyItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+1+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM" );
+ uint64 vendorguid;
+ uint32 item;
+ uint8 count, unk1;
+
+ recv_data >> vendorguid >> item >> count >> unk1;
+
+ GetPlayer()->BuyItemFromVendor(vendorguid,item,count,NULL_BAG,NULL_SLOT);
+}
+
+void WorldSession::HandleListInventoryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+
+ recv_data >> guid;
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ sLog.outDebug( "WORLD: Recvd CMSG_LIST_INVENTORY" );
+
+ SendListInventory( guid );
+}
+
+void WorldSession::SendListInventory( uint64 vendorguid )
+{
+ sLog.outDebug( "WORLD: Sent SMSG_LIST_INVENTORY" );
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: SendListInventory - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ // Stop the npc if moving
+ pCreature->StopMoving();
+
+ VendorItemData const* vItems = pCreature->GetVendorItems();
+ if(!vItems)
+ {
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
+ return;
+ }
+
+ uint8 numitems = vItems->GetItemCount();
+ uint8 count = 0;
+
+ WorldPacket data( SMSG_LIST_INVENTORY, (8+1+numitems*8*4) );
+ data << uint64(vendorguid);
+ data << uint8(numitems);
+
+ float discountMod = _player->GetReputationPriceDiscount(pCreature);
+
+ for(int i = 0; i < numitems; i++ )
+ {
+ if(VendorItem const* crItem = vItems->GetItem(i))
+ {
+ if(ItemPrototype const *pProto = objmgr.GetItemPrototype(crItem->item))
+ {
+ if((pProto->AllowableClass & _player->getClassMask()) == 0 && pProto->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster())
+ continue;
+
+ ++count;
+
+ // reputation discount
+ uint32 price = uint32(floor(pProto->BuyPrice * discountMod));
+
+ data << uint32(count);
+ data << uint32(crItem->item);
+ data << uint32(pProto->DisplayInfoID);
+ data << uint32(crItem->maxcount <= 0 ? 0xFFFFFFFF : pCreature->GetVendorItemCurrentCount(crItem));
+ data << uint32(price);
+ data << uint32(pProto->MaxDurability);
+ data << uint32(pProto->BuyCount);
+ data << uint32(crItem->ExtendedCost);
+ }
+ }
+ }
+
+ if ( count == 0 || data.size() != 8 + 1 + size_t(count) * 8 * 4 )
+ return;
+
+ data.put<uint8>(8, count);
+ SendPacket( &data );
+}
+
+void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_AUTOSTORE_BAG_ITEM");
+ uint8 srcbag, srcslot, dstbag;
+
+ recv_data >> srcbag >> srcslot >> dstbag;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u", srcbag, srcslot, dstbag);
+
+ Item *pItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pItem )
+ return;
+
+ uint16 src = pItem->GetPos();
+
+ // check unequip potability for equipped items and bank bags
+ if(_player->IsEquipmentPos ( src ) || _player->IsBagPos ( src ))
+ {
+ uint8 msg = _player->CanUnequipItem( src, !_player->IsBagPos ( src ));
+ if(msg != EQUIP_ERR_OK)
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+ }
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( dstbag, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ // no-op: placed in same slot
+ if(dest.size()==1 && dest[0].pos==src)
+ {
+ // just remove grey item state
+ _player->SendEquipError( EQUIP_ERR_NONE, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true );
+ _player->StoreItem( dest, pItem, true );
+}
+
+void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& /*recvPacket*/)
+{
+ sLog.outDebug("WORLD: CMSG_BUY_BANK_SLOT");
+
+ uint32 slot = _player->GetByteValue(PLAYER_BYTES_2, 2);
+
+ // next slot
+ ++slot;
+
+ sLog.outDetail("PLAYER: Buy bank bag slot, slot number = %u", slot);
+
+ BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot);
+
+ if(!slotEntry)
+ return;
+
+ uint32 price = slotEntry->price;
+
+ if (_player->GetMoney() < price)
+ return;
+
+ _player->SetByteValue(PLAYER_BYTES_2, 2, slot);
+ _player->ModifyMoney(-int32(price));
+}
+
+void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1+1);
+
+ sLog.outDebug("WORLD: CMSG_AUTOBANK_ITEM");
+ uint8 srcbag, srcslot;
+
+ recvPacket >> srcbag >> srcslot;
+ sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
+
+ Item *pItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pItem )
+ return;
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true);
+ _player->BankItem( dest, pItem, true );
+}
+
+void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1+1);
+
+ sLog.outDebug("WORLD: CMSG_AUTOSTORE_BANK_ITEM");
+ uint8 srcbag, srcslot;
+
+ recvPacket >> srcbag >> srcslot;
+ sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
+
+ Item *pItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pItem )
+ return;
+
+ if(_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory
+ {
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true);
+ _player->StoreItem( dest, pItem, true );
+ }
+ else // moving from inventory to bank
+ {
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true);
+ _player->BankItem( dest, pItem, true );
+ }
+}
+
+void WorldSession::HandleSetAmmoOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ if(!GetPlayer()->isAlive())
+ {
+ GetPlayer()->SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, NULL, NULL );
+ return;
+ }
+
+ sLog.outDebug("WORLD: CMSG_SET_AMMO");
+ uint32 item;
+
+ recv_data >> item;
+
+ if(!item)
+ GetPlayer()->RemoveAmmo();
+ else
+ GetPlayer()->SetAmmo(item);
+}
+
+void WorldSession::SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID,uint32 SpellID)
+{
+ WorldPacket data(SMSG_ENCHANTMENTLOG, (8+8+4+4+1)); // last check 2.0.10
+ data << Target;
+ data << Caster;
+ data << ItemID;
+ data << SpellID;
+ data << uint8(0);
+ SendPacket(&data);
+}
+
+void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration)
+{
+ // last check 2.0.10
+ WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8+4+4+8));
+ data << uint64(Itemguid);
+ data << uint32(slot);
+ data << uint32(Duration);
+ data << uint64(Playerguid);
+ SendPacket(&data);
+}
+
+void WorldSession::HandleItemNameQueryOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 itemid;
+ recv_data >> itemid;
+ sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY %u", itemid);
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( itemid );
+ if( pProto )
+ {
+ std::string Name;
+ Name = pProto->Name1;
+
+ int loc_idx = GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
+ if (il)
+ {
+ if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
+ Name = il->Name[loc_idx];
+ }
+ }
+ // guess size
+ WorldPacket data(SMSG_ITEM_NAME_QUERY_RESPONSE, (4+10));
+ data << uint32(pProto->ItemId);
+ data << Name;
+ data << uint32(pProto->InventoryType);
+ SendPacket(&data);
+ return;
+ }
+ else
+ sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY for item %u failed (unknown item)", itemid);
+}
+
+void WorldSession::HandleWrapItemOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1);
+
+ sLog.outDebug("Received opcode CMSG_WRAP_ITEM");
+
+ uint8 gift_bag, gift_slot, item_bag, item_slot;
+ //recv_data.hexlike();
+
+ recv_data >> gift_bag >> gift_slot; // paper
+ recv_data >> item_bag >> item_slot; // item
+
+ sLog.outDebug("WRAP: receive gift_bag = %u, gift_slot = %u, item_bag = %u, item_slot = %u", gift_bag, gift_slot, item_bag, item_slot);
+
+ Item *gift = _player->GetItemByPos( gift_bag, gift_slot );
+ if(!gift)
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL );
+ return;
+ }
+
+ if(!gift->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPER))// cheating: non-wrapper wrapper
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL );
+ return;
+ }
+
+ Item *item = _player->GetItemByPos( item_bag, item_slot );
+
+ if( !item )
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, item, NULL );
+ return;
+ }
+
+ if(item==gift) // not possable with pacjket from real client
+ {
+ _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->IsEquipped())
+ {
+ _player->SendEquipError( EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR)) // HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED);
+ {
+ _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->IsBag())
+ {
+ _player->SendEquipError( EQUIP_ERR_BAGS_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->IsSoulBound())
+ {
+ _player->SendEquipError( EQUIP_ERR_BOUND_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->GetMaxStackCount() != 1)
+ {
+ _player->SendEquipError( EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ // maybe not correct check (it is better than nothing)
+ if(item->GetProto()->MaxCount>0)
+ {
+ _player->SendEquipError( EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("INSERT INTO character_gifts VALUES ('%u', '%u', '%u', '%u')", GUID_LOPART(item->GetOwnerGUID()), item->GetGUIDLow(), item->GetEntry(), item->GetUInt32Value(ITEM_FIELD_FLAGS));
+ item->SetUInt32Value(OBJECT_FIELD_ENTRY, gift->GetUInt32Value(OBJECT_FIELD_ENTRY));
+
+ switch (item->GetEntry())
+ {
+ case 5042: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5043); break;
+ case 5048: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5044); break;
+ case 17303: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17302); break;
+ case 17304: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17305); break;
+ case 17307: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17308); break;
+ case 21830: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 21831); break;
+ }
+ item->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID());
+ item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED);
+ item->SetState(ITEM_CHANGED, _player);
+
+ if(item->GetState()==ITEM_NEW) // save new item, to have alway for `character_gifts` record in `item_instance`
+ {
+ // after save it will be impossible to remove the item from the queue
+ item->RemoveFromUpdateQueueOf(_player);
+ item->SaveToDB(); // item gave inventory record unchanged and can be save standalone
+ }
+ CharacterDatabase.CommitTransaction();
+
+ uint32 count = 1;
+ _player->DestroyItemCount(gift, count, true);
+}
+
+void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
+{
+ sLog.outDebug("WORLD: CMSG_SOCKET_GEMS");
+
+ CHECK_PACKET_SIZE(recv_data,8*4);
+
+ uint64 guids[4];
+ uint32 GemEnchants[3], OldEnchants[3];
+ Item *Gems[3];
+ bool SocketBonusActivated, SocketBonusToBeActivated;
+
+ for(int i = 0; i < 4; i++)
+ recv_data >> guids[i];
+
+ if(!guids[0])
+ return;
+
+ //cheat -> tried to socket same gem multiple times
+ if((guids[1] && (guids[1] == guids[2] || guids[1] == guids[3])) || (guids[2] && (guids[2] == guids[3])))
+ return;
+
+ Item *itemTarget = _player->GetItemByGuid(guids[0]);
+ if(!itemTarget) //missing item to socket
+ return;
+
+ //this slot is excepted when applying / removing meta gem bonus
+ uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : NULL_SLOT;
+
+ for(int i = 0; i < 3; i++)
+ Gems[i] = guids[i + 1] ? _player->GetItemByGuid(guids[i + 1]) : NULL;
+
+ GemPropertiesEntry const *GemProps[3];
+ for(int i = 0; i < 3; ++i) //get geminfo from dbc storage
+ {
+ GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetProto()->GemProperties) : NULL;
+ }
+
+ for(int i = 0; i < 3; ++i) //check for hack maybe
+ {
+ // tried to put gem in socket where no socket exists / tried to put normal gem in meta socket
+ // tried to put meta gem in normal socket
+ if( GemProps[i] && ( !itemTarget->GetProto()->Socket[i].Color ||
+ itemTarget->GetProto()->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META ||
+ itemTarget->GetProto()->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META ) )
+ return;
+ }
+
+ for(int i = 0; i < 3; ++i) //get new and old enchantments
+ {
+ GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0;
+ OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i));
+ }
+
+ // check unique-equipped conditions
+ for(int i = 0; i < 3; ++i)
+ {
+ if (Gems[i] && (Gems[i]->GetProto()->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED))
+ {
+ // for equipped item check all equipment for duplicate equipped gems
+ if(itemTarget->IsEquipped())
+ {
+ if(GetPlayer()->GetItemOrItemWithGemEquipped(Gems[i]->GetEntry()))
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE, itemTarget, NULL );
+ return;
+ }
+ }
+
+ // continue check for case when attempt add 2 similar unique equipped gems in one item.
+ for (int j = 0; j < 3; ++j)
+ {
+ if ((i != j) && (Gems[j]) && (Gems[i]->GetProto()->ItemId == Gems[j]->GetProto()->ItemId))
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
+ return;
+ }
+ }
+ for (int j = 0; j < 3; ++j)
+ {
+ if (OldEnchants[j])
+ {
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]);
+ if(!enchantEntry)
+ continue;
+
+ if ((enchantEntry->GemID == Gems[i]->GetProto()->ItemId) && (i != j))
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus
+ _player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item)
+
+ //if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met
+
+ //remove ALL enchants
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),false);
+
+ for(int i = 0; i < 3; ++i)
+ {
+ if(GemEnchants[i])
+ {
+ itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i],0,0);
+ if(Item* guidItem = _player->GetItemByGuid(guids[i + 1]))
+ _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true );
+ }
+ }
+
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),true);
+
+ SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state
+ if(SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change...
+ {
+ _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,false);
+ itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetProto()->socketBonus : 0), 0, 0);
+ _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,true);
+ //it is not displayed, client has an inbuilt system to determine if the bonus is activated
+ }
+
+ _player->ToggleMetaGemsActive(slot, true); //turn on all metagems (except for target item)
+}
+
+void WorldSession::HandleCancelTempItemEnchantmentOpcode(WorldPacket& recv_data)
+{
+ sLog.outDebug("WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT");
+
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 eslot;
+
+ recv_data >> eslot;
+
+ // apply only to equipped item
+ if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0,eslot))
+ return;
+
+ Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot);
+
+ if(!item)
+ return;
+
+ if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
+ return;
+
+ GetPlayer()->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false);
+ item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
+}
diff --git a/src/game/Language.h b/src/game/Language.h
index fc4f4039e51..f805103047e 100644
--- a/src/game/Language.h
+++ b/src/game/Language.h
@@ -1,663 +1,663 @@
-/*
- * 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
- */
-
-#ifndef __MANGOS_LANGUAGE_H
-#define __MANGOS_LANGUAGE_H
-
-enum MangosStrings
-{
- // for chat commands
- LANG_SELECT_CHAR_OR_CREATURE = 1,
- LANG_SELECT_CREATURE = 2,
-
- // level 0 chat
- LANG_SYSTEMMESSAGE = 3,
- LANG_EVENTMESSAGE = 4,
- LANG_NO_HELP_CMD = 5,
- LANG_NO_CMD = 6,
- LANG_NO_SUBCMD = 7,
- LANG_SUBCMDS_LIST = 8,
- LANG_AVIABLE_CMD = 9,
- LANG_CMD_SYNTAX = 10,
- LANG_ACCOUNT_LEVEL = 11,
- LANG_CONNECTED_USERS = 12,
- LANG_UPTIME = 13,
- LANG_PLAYER_SAVED = 14,
- LANG_PLAYERS_SAVED = 15,
- LANG_GMS_ON_SRV = 16,
- LANG_GMS_NOT_LOGGED = 17,
- LANG_YOU_IN_FLIGHT = 18,
- //LANG_YOU_IN_BATTLEGROUND = 19, not used
- //LANG_TARGET_IN_FLIGHT = 20, not used
- LANG_CHAR_IN_FLIGHT = 21,
- LANG_CHAR_NON_MOUNTED = 22,
- LANG_YOU_IN_COMBAT = 23,
- LANG_YOU_USED_IT_RECENTLY = 24,
- LANG_COMMAND_NOTCHANGEPASSWORD = 25,
- LANG_COMMAND_PASSWORD = 26,
- LANG_COMMAND_WRONGOLDPASSWORD = 27,
- LANG_COMMAND_ACCLOCKLOCKED = 28,
- LANG_COMMAND_ACCLOCKUNLOCKED = 29,
- LANG_SPELL_RANK = 30,
- LANG_KNOWN = 31,
- LANG_LEARN = 32,
- LANG_PASSIVE = 33,
- LANG_TALENT = 34,
- LANG_ACTIVE = 35,
- LANG_COMPLETE = 36,
- LANG_OFFLINE = 37,
- LANG_ON = 38,
- LANG_OFF = 39,
- LANG_YOU_ARE = 40,
- LANG_VISIBLE = 41,
- LANG_INVISIBLE = 42,
- LANG_DONE = 43,
- LANG_YOU = 44,
- LANG_UNKNOWN = 45,
- LANG_ERROR = 46,
- LANG_NON_EXIST_CHARACTER = 47,
- LANG_FRIEND_IGNORE_UNKNOWN = 48,
- LANG_LEVEL_MINREQUIRED = 49,
- LANG_LEVEL_MINREQUIRED_AND_ITEM = 50,
- LANG_NPC_TAINER_HELLO = 51,
- 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,
- LANG_MAP_POSITION = 101,
- LANG_IS_TELEPORTED = 102,
- LANG_CANNOT_SUMMON_TO_INST = 103,
- LANG_CANNOT_GO_TO_INST_PARTY = 104,
- LANG_CANNOT_GO_TO_INST_GM = 105,
- LANG_CANNOT_GO_INST_INST = 106,
- LANG_CANNOT_SUMMON_INST_INST = 107,
-
- LANG_SUMMONING = 108,
- LANG_SUMMONED_BY = 109,
- LANG_TELEPORTING_TO = 110,
- LANG_TELEPORTED_TO_BY = 111,
- LANG_NO_PLAYER = 112,
- LANG_APPEARING_AT = 113,
- LANG_APPEARING_TO = 114,
-
- LANG_BAD_VALUE = 115,
- LANG_NO_CHAR_SELECTED = 116,
- LANG_NOT_IN_GROUP = 117,
-
- LANG_YOU_CHANGE_HP = 118,
- LANG_YOURS_HP_CHANGED = 119,
- LANG_YOU_CHANGE_MANA = 120,
- LANG_YOURS_MANA_CHANGED = 121,
- LANG_YOU_CHANGE_ENERGY = 122,
- LANG_YOURS_ENERGY_CHANGED = 123,
-
- LANG_CURRENT_ENERGY = 124, //log
- LANG_YOU_CHANGE_RAGE = 125,
- LANG_YOURS_RAGE_CHANGED = 126,
- LANG_YOU_CHANGE_LVL = 127,
- LANG_CURRENT_FACTION = 128,
- LANG_WRONG_FACTION = 129,
- LANG_YOU_CHANGE_FACTION = 130,
- LANG_YOU_CHANGE_SPELLFLATID = 131,
- LANG_YOURS_SPELLFLATID_CHANGED = 132,
- LANG_YOU_GIVE_TAXIS = 133,
- LANG_YOU_REMOVE_TAXIS = 134,
- LANG_YOURS_TAXIS_ADDED = 135,
- LANG_YOURS_TAXIS_REMOVED = 136,
-
- LANG_YOU_CHANGE_ASPEED = 137,
- LANG_YOURS_ASPEED_CHANGED = 138,
- LANG_YOU_CHANGE_SPEED = 139,
- LANG_YOURS_SPEED_CHANGED = 140,
- LANG_YOU_CHANGE_SWIM_SPEED = 141,
- LANG_YOURS_SWIM_SPEED_CHANGED = 142,
- LANG_YOU_CHANGE_BACK_SPEED = 143,
- LANG_YOURS_BACK_SPEED_CHANGED = 144,
- LANG_YOU_CHANGE_FLY_SPEED = 145,
- LANG_YOURS_FLY_SPEED_CHANGED = 146,
-
- LANG_YOU_CHANGE_SIZE = 147,
- LANG_YOURS_SIZE_CHANGED = 148,
- LANG_NO_MOUNT = 149,
- LANG_YOU_GIVE_MOUNT = 150,
- LANG_MOUNT_GIVED = 151,
-
- LANG_CURRENT_MONEY = 152,
- LANG_YOU_TAKE_ALL_MONEY = 153,
- LANG_YOURS_ALL_MONEY_GONE = 154,
- LANG_YOU_TAKE_MONEY = 155,
- LANG_YOURS_MONEY_TAKEN = 156,
- LANG_YOU_GIVE_MONEY = 157,
- LANG_YOURS_MONEY_GIVEN = 158,
- LANG_YOU_HEAR_SOUND = 159,
-
- LANG_NEW_MONEY = 160, // Log
-
- LANG_REMOVE_BIT = 161,
- LANG_SET_BIT = 162,
- LANG_COMMAND_TELE_TABLEEMPTY = 163,
- LANG_COMMAND_TELE_NOTFOUND = 164,
- LANG_COMMAND_TELE_PARAMETER = 165,
- LANG_COMMAND_TELE_NOLOCATION = 166,
- // 167 // not used
- LANG_COMMAND_TELE_LOCATION = 168,
-
- LANG_MAIL_SENT = 169,
- LANG_SOUND_NOT_EXIST = 170,
- // Room for more level 1 171-199 not used
-
- // level 2 chat
- LANG_NO_SELECTION = 200,
- LANG_OBJECT_GUID = 201,
- LANG_TOO_LONG_NAME = 202,
- LANG_CHARS_ONLY = 203,
- LANG_TOO_LONG_SUBNAME = 204,
- LANG_NOT_IMPLEMENTED = 205,
-
- LANG_ITEM_ADDED_TO_LIST = 206,
- LANG_ITEM_NOT_FOUND = 207,
- LANG_ITEM_DELETED_FROM_LIST = 208,
- LANG_ITEM_NOT_IN_LIST = 209,
- LANG_ITEM_ALREADY_IN_LIST = 210,
-
- LANG_RESET_SPELLS_ONLINE = 211,
- LANG_RESET_SPELLS_OFFLINE = 212,
- LANG_RESET_TALENTS_ONLINE = 213,
- LANG_RESET_TALENTS_OFFLINE = 214,
- LANG_RESET_SPELLS = 215,
- LANG_RESET_TALENTS = 216,
-
- LANG_RESETALL_UNKNOWN_CASE = 217,
- LANG_RESETALL_SPELLS = 218,
- LANG_RESETALL_TALENTS = 219,
-
- LANG_WAYPOINT_NOTFOUND = 220,
- LANG_WAYPOINT_NOTFOUNDLAST = 221,
- LANG_WAYPOINT_NOTFOUNDSEARCH = 222,
- LANG_WAYPOINT_NOTFOUNDDBPROBLEM = 223,
- LANG_WAYPOINT_CREATSELECTED = 224,
- LANG_WAYPOINT_CREATNOTFOUND = 225,
- LANG_WAYPOINT_VP_SELECT = 226,
- LANG_WAYPOINT_VP_NOTFOUND = 227,
- LANG_WAYPOINT_VP_NOTCREATED = 228,
- LANG_WAYPOINT_VP_ALLREMOVED = 229,
- LANG_WAYPOINT_NOTCREATED = 230,
- LANG_WAYPOINT_NOGUID = 231,
- LANG_WAYPOINT_NOWAYPOINTGIVEN = 232,
- LANG_WAYPOINT_ARGUMENTREQ = 233,
- LANG_WAYPOINT_ADDED = 234,
- LANG_WAYPOINT_ADDED_NO = 235,
- LANG_WAYPOINT_CHANGED = 236,
- LANG_WAYPOINT_CHANGED_NO = 237,
- LANG_WAYPOINT_EXPORTED = 238,
- LANG_WAYPOINT_NOTHINGTOEXPORT = 239,
- LANG_WAYPOINT_IMPORTED = 240,
- LANG_WAYPOINT_REMOVED = 241,
- LANG_WAYPOINT_NOTREMOVED = 242,
- LANG_WAYPOINT_TOOFAR1 = 243,
- LANG_WAYPOINT_TOOFAR2 = 244,
- LANG_WAYPOINT_TOOFAR3 = 245,
- LANG_WAYPOINT_INFO_TITLE = 246,
- LANG_WAYPOINT_INFO_WAITTIME = 247,
- LANG_WAYPOINT_INFO_MODEL = 248,
- LANG_WAYPOINT_INFO_EMOTE = 249,
- LANG_WAYPOINT_INFO_SPELL = 250,
- LANG_WAYPOINT_INFO_TEXT = 251,
- LANG_WAYPOINT_INFO_AISCRIPT = 252,
-
- LANG_RENAME_PLAYER = 253,
- LANG_RENAME_PLAYER_GUID = 254,
-
- LANG_WAYPOINT_WPCREATNOTFOUND = 255,
- LANG_WAYPOINT_NPCNOTFOUND = 256,
-
- LANG_MOVE_TYPE_SET = 257,
- LANG_MOVE_TYPE_SET_NODEL = 258,
- LANG_USE_BOL = 259,
- LANG_VALUE_SAVED = 260,
- LANG_VALUE_SAVED_REJOIN = 261,
-
- LANG_COMMAND_GOAREATRNOTFOUND = 262,
- LANG_INVALID_TARGET_COORD = 263,
- LANG_INVALID_ZONE_COORD = 264,
- LANG_INVALID_ZONE_MAP = 265,
- LANG_COMMAND_TARGETOBJNOTFOUND = 266,
- LANG_COMMAND_GOOBJNOTFOUND = 267,
- LANG_COMMAND_GOCREATNOTFOUND = 268,
- LANG_COMMAND_GOCREATMULTIPLE = 269,
- LANG_COMMAND_DELCREATMESSAGE = 270,
- LANG_COMMAND_CREATUREMOVED = 271,
- LANG_COMMAND_CREATUREATSAMEMAP = 272,
- LANG_COMMAND_OBJNOTFOUND = 273,
- LANG_COMMAND_DELOBJREFERCREATURE = 274,
- LANG_COMMAND_DELOBJMESSAGE = 275,
- LANG_COMMAND_TURNOBJMESSAGE = 276,
- LANG_COMMAND_MOVEOBJMESSAGE = 277,
- LANG_COMMAND_VENDORSELECTION = 278,
- LANG_COMMAND_NEEDITEMSEND = 279,
- LANG_COMMAND_ADDVENDORITEMITEMS = 280,
- LANG_COMMAND_KICKSELF = 281,
- LANG_COMMAND_KICKMESSAGE = 282,
- LANG_COMMAND_KICKNOTFOUNDPLAYER = 283,
- LANG_COMMAND_WHISPERACCEPTING = 284,
- LANG_COMMAND_WHISPERON = 285,
- LANG_COMMAND_WHISPEROFF = 286,
- LANG_COMMAND_CREATGUIDNOTFOUND = 287,
- LANG_COMMAND_TICKETCOUNT = 288,
- LANG_COMMAND_TICKETNEW = 289,
- LANG_COMMAND_TICKETVIEW = 290,
- LANG_COMMAND_TICKETON = 291,
- LANG_COMMAND_TICKETOFF = 292,
- LANG_COMMAND_TICKENOTEXIST = 293,
- LANG_COMMAND_ALLTICKETDELETED = 294,
- LANG_COMMAND_TICKETPLAYERDEL = 295,
- LANG_COMMAND_TICKETDEL = 296,
- LANG_COMMAND_SPAWNDIST = 297,
- LANG_COMMAND_SPAWNTIME = 298,
- LANG_COMMAND_MODIFY_HONOR = 299,
-
- LANG_YOUR_CHAT_DISABLED = 300,
- LANG_YOU_DISABLE_CHAT = 301,
- LANG_CHAT_ALREADY_ENABLED = 302,
- LANG_YOUR_CHAT_ENABLED = 303,
- LANG_YOU_ENABLE_CHAT = 304,
-
- LANG_COMMAND_MODIFY_REP = 305,
- LANG_COMMAND_MODIFY_ARENA = 306,
- LANG_COMMAND_FACTION_NOTFOUND = 307,
- LANG_COMMAND_FACTION_UNKNOWN = 308,
- LANG_COMMAND_FACTION_INVPARAM = 309,
- LANG_COMMAND_FACTION_DELTA = 310,
- LANG_FACTION_LIST = 311,
- LANG_FACTION_VISIBLE = 312,
- LANG_FACTION_ATWAR = 313,
- LANG_FACTION_PEACE_FORCED = 314,
- LANG_FACTION_HIDDEN = 315,
- LANG_FACTION_INVISIBLE_FORCED = 316,
- LANG_FACTION_INACTIVE = 317,
- LANG_REP_HATED = 318,
- LANG_REP_HOSTILE = 319,
- LANG_REP_UNFRIENDLY = 320,
- LANG_REP_NEUTRAL = 321,
- LANG_REP_FRIENDLY = 322,
- LANG_REP_HONORED = 323,
- LANG_REP_REVERED = 324,
- LANG_REP_EXALTED = 325,
- LANG_COMMAND_FACTION_NOREP_ERROR = 326,
- LANG_FACTION_NOREPUTATION = 327,
- LANG_LOOKUP_PLAYER_ACCOUNT = 328,
- LANG_LOOKUP_PLAYER_CHARACTER = 329,
- LANG_NO_PLAYERS_FOUND = 330,
- LANG_EXTENDED_COST_NOT_EXIST = 331,
- 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,
- LANG_YOU_CHANGE_SECURITY = 401,
- LANG_YOURS_SECURITY_CHANGED = 402,
- LANG_YOURS_SECURITY_IS_LOW = 403,
- LANG_CREATURE_MOVE_DISABLED = 404,
- LANG_CREATURE_MOVE_ENABLED = 405,
- LANG_NO_WEATHER = 406,
- LANG_WEATHER_DISABLED = 407,
-
- LANG_BAN_YOUBANNED = 408,
- LANG_BAN_YOUPERMBANNED = 409,
- LANG_BAN_NOTFOUND = 410,
-
- LANG_UNBAN_UNBANNED = 411,
- LANG_UNBAN_ERROR = 412,
-
- LANG_BANINFO_NOACCOUNT = 413,
- LANG_BANINFO_NOCHARACTER = 414,
- LANG_BANINFO_NOIP = 415,
- LANG_BANINFO_NOACCOUNTBAN = 416,
- LANG_BANINFO_BANHISTORY = 417,
- LANG_BANINFO_HISTORYENTRY = 418,
- LANG_BANINFO_INFINITE = 419,
- LANG_BANINFO_NEVER = 420,
- LANG_BANINFO_YES = 421,
- LANG_BANINFO_NO = 422,
- LANG_BANINFO_IPENTRY = 423,
-
- LANG_BANLIST_NOIP = 424,
- LANG_BANLIST_NOACCOUNT = 425,
- LANG_BANLIST_NOCHARACTER = 426,
- LANG_BANLIST_MATCHINGIP = 427,
- LANG_BANLIST_MATCHINGACCOUNT = 428,
-
- LANG_COMMAND_LEARN_MANY_SPELLS = 429,
- LANG_COMMAND_LEARN_CLASS_SPELLS = 430,
- LANG_COMMAND_LEARN_CLASS_TALENTS = 431,
- LANG_COMMAND_LEARN_ALL_LANG = 432,
- LANG_COMMAND_LEARN_ALL_CRAFT = 433,
- LANG_COMMAND_COULDNOTFIND = 434,
- LANG_COMMAND_ITEMIDINVALID = 435,
- LANG_COMMAND_NOITEMFOUND = 436,
- LANG_COMMAND_LISTOBJINVALIDID = 437,
- LANG_COMMAND_LISTITEMMESSAGE = 438,
- LANG_COMMAND_LISTOBJMESSAGE = 439,
- LANG_COMMAND_INVALIDCREATUREID = 440,
- LANG_COMMAND_LISTCREATUREMESSAGE = 441,
- LANG_COMMAND_NOAREAFOUND = 442,
- LANG_COMMAND_NOITEMSETFOUND = 443,
- LANG_COMMAND_NOSKILLFOUND = 444,
- LANG_COMMAND_NOSPELLFOUND = 445,
- LANG_COMMAND_NOQUESTFOUND = 446,
- LANG_COMMAND_NOCREATUREFOUND = 447,
- LANG_COMMAND_NOGAMEOBJECTFOUND = 448,
- LANG_COMMAND_GRAVEYARDNOEXIST = 449,
- LANG_COMMAND_GRAVEYARDALRLINKED = 450,
- LANG_COMMAND_GRAVEYARDLINKED = 451,
- LANG_COMMAND_GRAVEYARDWRONGZONE = 452,
- LANG_COMMAND_GRAVEYARDWRONGTEAM = 453,
- LANG_COMMAND_GRAVEYARDERROR = 454,
- LANG_COMMAND_GRAVEYARD_NOTEAM = 455,
- LANG_COMMAND_GRAVEYARD_ANY = 456,
- LANG_COMMAND_GRAVEYARD_ALLIANCE = 457,
- LANG_COMMAND_GRAVEYARD_HORDE = 458,
- LANG_COMMAND_GRAVEYARDNEAREST = 459,
- LANG_COMMAND_ZONENOGRAVEYARDS = 460,
- LANG_COMMAND_ZONENOGRAFACTION = 461,
- LANG_COMMAND_TP_ALREADYEXIST = 462,
- LANG_COMMAND_TP_ADDED = 463,
- LANG_COMMAND_TP_ADDEDERR = 464,
- LANG_COMMAND_TP_DELETED = 465,
- // 466, // not used
-
- LANG_COMMAND_TARGET_LISTAURAS = 467,
- LANG_COMMAND_TARGET_AURADETAIL = 468,
- LANG_COMMAND_TARGET_LISTAURATYPE = 469,
- LANG_COMMAND_TARGET_AURASIMPLE = 470,
-
- LANG_COMMAND_QUEST_NOTFOUND = 471,
- LANG_COMMAND_QUEST_STARTFROMITEM = 472,
- LANG_COMMAND_QUEST_REMOVED = 473,
- LANG_COMMAND_QUEST_REWARDED = 474,
- LANG_COMMAND_QUEST_COMPLETE = 475,
- LANG_COMMAND_QUEST_ACTIVE = 476,
-
- LANG_COMMAND_FLYMODE_STATUS = 477,
-
- LANG_COMMAND_OPCODESENT = 478,
-
- LANG_COMMAND_IMPORT_SUCCESS = 479,
- LANG_COMMAND_IMPORT_FAILED = 480,
- LANG_COMMAND_EXPORT_SUCCESS = 481,
- LANG_COMMAND_EXPORT_FAILED = 482,
-
- LANG_COMMAND_SPELL_BROKEN = 483,
-
- LANG_SET_SKILL = 484,
- LANG_SET_SKILL_ERROR = 485,
-
- LANG_INVALID_SKILL_ID = 486,
- LANG_LEARNING_GM_SKILLS = 487,
- LANG_YOU_KNOWN_SPELL = 488,
- LANG_TARGET_KNOWN_SPELL = 489,
- LANG_UNKNOWN_SPELL = 490,
- LANG_FORGET_SPELL = 491,
- LANG_REMOVEALL_COOLDOWN = 492,
- LANG_REMOVE_COOLDOWN = 493,
-
- LANG_ADDITEM = 494, //log
- LANG_ADDITEMSET = 495, //log
- LANG_REMOVEITEM = 496,
- LANG_ITEM_CANNOT_CREATE = 497,
- LANG_INSERT_GUILD_NAME = 498,
- LANG_PLAYER_NOT_FOUND = 499,
- LANG_PLAYER_IN_GUILD = 500,
- LANG_GUILD_NOT_CREATED = 501,
- LANG_NO_ITEMS_FROM_ITEMSET_FOUND = 502,
-
- LANG_DISTANCE = 503,
-
- LANG_ITEM_SLOT = 504,
- LANG_ITEM_SLOT_NOT_EXIST = 505,
- LANG_ITEM_ADDED_TO_SLOT = 506,
- LANG_ITEM_SAVE_FAILED = 507,
- LANG_ITEMLIST_SLOT = 508,
- LANG_ITEMLIST_MAIL = 509,
- LANG_ITEMLIST_AUCTION = 510,
-
- LANG_WRONG_LINK_TYPE = 511,
- LANG_ITEM_LIST = 512,
- LANG_QUEST_LIST = 513,
- LANG_CREATURE_ENTRY_LIST = 514,
- LANG_CREATURE_LIST = 515,
- LANG_GO_ENTRY_LIST = 516,
- LANG_GO_LIST = 517,
- LANG_ITEMSET_LIST = 518,
- LANG_TELE_LIST = 519,
- LANG_SPELL_LIST = 520,
- LANG_SKILL_LIST = 521,
-
- LANG_GAMEOBJECT_NOT_EXIST = 522,
-
- LANG_GAMEOBJECT_CURRENT = 523, //log
- LANG_GAMEOBJECT_DETAIL = 524,
- LANG_GAMEOBJECT_ADD = 525,
-
- LANG_MOVEGENS_LIST = 526,
- LANG_MOVEGENS_IDLE = 527,
- LANG_MOVEGENS_RANDOM = 528,
- LANG_MOVEGENS_WAYPOINT = 529,
- LANG_MOVEGENS_ANIMAL_RANDOM = 530,
- LANG_MOVEGENS_CONFUSED = 531,
- LANG_MOVEGENS_TARGETED_PLAYER = 532,
- LANG_MOVEGENS_TARGETED_CREATURE = 533,
- LANG_MOVEGENS_TARGETED_NULL = 534,
- LANG_MOVEGENS_HOME_CREATURE = 535,
- LANG_MOVEGENS_HOME_PLAYER = 536,
- LANG_MOVEGENS_FLIGHT = 537,
- LANG_MOVEGENS_UNKNOWN = 538,
-
- LANG_NPCINFO_CHAR = 539,
- LANG_NPCINFO_LEVEL = 540,
- LANG_NPCINFO_HEALTH = 541,
- LANG_NPCINFO_FLAGS = 542,
- LANG_NPCINFO_LOOT = 543,
- LANG_NPCINFO_POSITION = 544,
- LANG_NPCINFO_VENDOR = 545,
- LANG_NPCINFO_TRAINER = 546,
- LANG_NPCINFO_DUNGEON_ID = 547,
-
- LANG_PINFO_ACCOUNT = 548,
- LANG_PINFO_LEVEL = 549,
- LANG_PINFO_NO_REP = 550,
-
- LANG_YOU_SET_EXPLORE_ALL = 551,
- LANG_YOU_SET_EXPLORE_NOTHING = 552,
- LANG_YOURS_EXPLORE_SET_ALL = 553,
- LANG_YOURS_EXPLORE_SET_NOTHING = 554,
-
- LANG_HOVER_ENABLED = 555,
- LANG_HOVER_DISABLED = 556,
- LANG_YOURS_LEVEL_UP = 557,
- LANG_YOURS_LEVEL_DOWN = 558,
- LANG_YOURS_LEVEL_PROGRESS_RESET = 559,
- LANG_EXPLORE_AREA = 560,
- LANG_UNEXPLORE_AREA = 561,
-
- LANG_UPDATE = 562,
- LANG_UPDATE_CHANGE = 563,
- LANG_TOO_BIG_INDEX = 564,
- LANG_SET_UINT = 565, //log
- LANG_SET_UINT_FIELD = 566,
- LANG_SET_FLOAT = 567, //log
- LANG_SET_FLOAT_FIELD = 568,
- LANG_GET_UINT = 569, //log
- LANG_GET_UINT_FIELD = 570,
- LANG_GET_FLOAT = 571, //log
- LANG_GET_FLOAT_FIELD = 572,
- LANG_SET_32BIT = 573, //log
- LANG_SET_32BIT_FIELD = 574,
- LANG_CHANGE_32BIT = 575, //log
- LANG_CHANGE_32BIT_FIELD = 576,
-
- LANG_INVISIBLE_INVISIBLE = 577,
- LANG_INVISIBLE_VISIBLE = 578,
- LANG_SELECTED_TARGET_NOT_HAVE_VICTIM = 579,
-
- LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST = 580,
- LANG_COMMAND_NEAROBJMESSAGE = 581,
- LANG_COMMAND_RAWPAWNTIMES = 582,
-
- LANG_EVENT_ENTRY_LIST = 583,
- LANG_NOEVENTFOUND = 584,
- LANG_EVENT_NOT_EXIST = 585,
- LANG_EVENT_INFO = 586,
- LANG_EVENT_ALREADY_ACTIVE = 587,
- LANG_EVENT_NOT_ACTIVE = 588,
-
- LANG_MOVEGENS_POINT = 589,
- LANG_MOVEGENS_FEAR = 590,
- LANG_MOVEGENS_DISTRACT = 591,
-
- LANG_COMMAND_LEARN_ALL_RECIPES = 592,
-
- // Battleground
- LANG_BG_A_WINS = 600,
- LANG_BG_H_WINS = 601,
- LANG_BG_WS_ONE_MINUTE = 602,
- LANG_BG_WS_HALF_MINUTE = 603,
- LANG_BG_WS_BEGIN = 604,
-
- LANG_BG_WS_CAPTURED_HF = 605,
- LANG_BG_WS_CAPTURED_AF = 606,
- LANG_BG_WS_DROPPED_HF = 607,
- LANG_BG_WS_DROPPED_AF = 608,
- LANG_BG_WS_RETURNED_AF = 609,
- LANG_BG_WS_RETURNED_HF = 610,
- LANG_BG_WS_PICKEDUP_HF = 611,
- LANG_BG_WS_PICKEDUP_AF = 612,
- LANG_BG_WS_F_PLACED = 613,
- LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED = 614,
- LANG_BG_WS_HORDE_FLAG_RESPAWNED = 615,
-
- LANG_BG_EY_ONE_MINUTE = 636,
- LANG_BG_EY_HALF_MINUTE = 637,
- LANG_BG_EY_BEGIN = 638,
-
- LANG_BG_AB_ALLY = 650,
- LANG_BG_AB_HORDE = 651,
- LANG_BG_AB_NODE_STABLES = 652,
- LANG_BG_AB_NODE_BLACKSMITH = 653,
- LANG_BG_AB_NODE_FARM = 654,
- LANG_BG_AB_NODE_LUMBER_MILL = 655,
- LANG_BG_AB_NODE_GOLD_MINE = 656,
- LANG_BG_AB_NODE_TAKEN = 657,
- LANG_BG_AB_NODE_DEFENDED = 658,
- LANG_BG_AB_NODE_ASSAULTED = 659,
- LANG_BG_AB_NODE_CLAIMED = 660,
- LANG_BG_AB_ONEMINTOSTART = 661,
- LANG_BG_AB_HALFMINTOSTART = 662,
- LANG_BG_AB_STARTED = 663,
- LANG_BG_AB_A_NEAR_VICTORY = 664,
- LANG_BG_AB_H_NEAR_VICTORY = 665,
- LANG_BG_MARK_BY_MAIL = 666,
-
- LANG_BG_EY_HAS_TAKEN_A_M_TOWER = 667,
- LANG_BG_EY_HAS_TAKEN_H_M_TOWER = 668,
- LANG_BG_EY_HAS_TAKEN_A_D_RUINS = 669,
- LANG_BG_EY_HAS_TAKEN_H_D_RUINS = 670,
- LANG_BG_EY_HAS_TAKEN_A_B_TOWER = 671,
- LANG_BG_EY_HAS_TAKEN_H_B_TOWER = 672,
- LANG_BG_EY_HAS_TAKEN_A_F_RUINS = 673,
- LANG_BG_EY_HAS_TAKEN_H_F_RUINS = 674,
- LANG_BG_EY_HAS_LOST_A_M_TOWER = 675,
- LANG_BG_EY_HAS_LOST_H_M_TOWER = 676,
- LANG_BG_EY_HAS_LOST_A_D_RUINS = 677,
- LANG_BG_EY_HAS_LOST_H_D_RUINS = 678,
- LANG_BG_EY_HAS_LOST_A_B_TOWER = 679,
- LANG_BG_EY_HAS_LOST_H_B_TOWER = 680,
- LANG_BG_EY_HAS_LOST_A_F_RUINS = 681,
- LANG_BG_EY_HAS_LOST_H_F_RUINS = 682,
- LANG_BG_EY_HAS_TAKEN_FLAG = 683,
- LANG_BG_EY_CAPTURED_FLAG_A = 684,
- LANG_BG_EY_CAPTURED_FLAG_H = 685,
- LANG_BG_EY_DROPPED_FLAG = 686,
- LANG_BG_EY_RESETED_FLAG = 687,
-
- LANG_ARENA_ONE_TOOLOW = 700,
- LANG_ARENA_ONE_MINUTE = 701,
- LANG_ARENA_THIRTY_SECONDS = 702,
- LANG_ARENA_FIFTEEN_SECONDS = 703,
- LANG_ARENA_BEGUN = 704,
-
- LANG_WAIT_BEFORE_SPEAKING = 705,
- LANG_NOT_EQUIPPED_ITEM = 706,
- LANG_PLAYER_DND = 707,
- LANG_PLAYER_AFK = 708,
- LANG_PLAYER_DND_DEFAULT = 709,
- LANG_PLAYER_AFK_DEFAULT = 710,
-
- LANG_BG_GROUP_TOO_LARGE = 711,
- LANG_ARENA_GROUP_TOO_LARGE = 712,
- 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
+/*
+ * 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
+ */
+
+#ifndef __MANGOS_LANGUAGE_H
+#define __MANGOS_LANGUAGE_H
+
+enum MangosStrings
+{
+ // for chat commands
+ LANG_SELECT_CHAR_OR_CREATURE = 1,
+ LANG_SELECT_CREATURE = 2,
+
+ // level 0 chat
+ LANG_SYSTEMMESSAGE = 3,
+ LANG_EVENTMESSAGE = 4,
+ LANG_NO_HELP_CMD = 5,
+ LANG_NO_CMD = 6,
+ LANG_NO_SUBCMD = 7,
+ LANG_SUBCMDS_LIST = 8,
+ LANG_AVIABLE_CMD = 9,
+ LANG_CMD_SYNTAX = 10,
+ LANG_ACCOUNT_LEVEL = 11,
+ LANG_CONNECTED_USERS = 12,
+ LANG_UPTIME = 13,
+ LANG_PLAYER_SAVED = 14,
+ LANG_PLAYERS_SAVED = 15,
+ LANG_GMS_ON_SRV = 16,
+ LANG_GMS_NOT_LOGGED = 17,
+ LANG_YOU_IN_FLIGHT = 18,
+ //LANG_YOU_IN_BATTLEGROUND = 19, not used
+ //LANG_TARGET_IN_FLIGHT = 20, not used
+ LANG_CHAR_IN_FLIGHT = 21,
+ LANG_CHAR_NON_MOUNTED = 22,
+ LANG_YOU_IN_COMBAT = 23,
+ LANG_YOU_USED_IT_RECENTLY = 24,
+ LANG_COMMAND_NOTCHANGEPASSWORD = 25,
+ LANG_COMMAND_PASSWORD = 26,
+ LANG_COMMAND_WRONGOLDPASSWORD = 27,
+ LANG_COMMAND_ACCLOCKLOCKED = 28,
+ LANG_COMMAND_ACCLOCKUNLOCKED = 29,
+ LANG_SPELL_RANK = 30,
+ LANG_KNOWN = 31,
+ LANG_LEARN = 32,
+ LANG_PASSIVE = 33,
+ LANG_TALENT = 34,
+ LANG_ACTIVE = 35,
+ LANG_COMPLETE = 36,
+ LANG_OFFLINE = 37,
+ LANG_ON = 38,
+ LANG_OFF = 39,
+ LANG_YOU_ARE = 40,
+ LANG_VISIBLE = 41,
+ LANG_INVISIBLE = 42,
+ LANG_DONE = 43,
+ LANG_YOU = 44,
+ LANG_UNKNOWN = 45,
+ LANG_ERROR = 46,
+ LANG_NON_EXIST_CHARACTER = 47,
+ LANG_FRIEND_IGNORE_UNKNOWN = 48,
+ LANG_LEVEL_MINREQUIRED = 49,
+ LANG_LEVEL_MINREQUIRED_AND_ITEM = 50,
+ LANG_NPC_TAINER_HELLO = 51,
+ 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,
+ LANG_MAP_POSITION = 101,
+ LANG_IS_TELEPORTED = 102,
+ LANG_CANNOT_SUMMON_TO_INST = 103,
+ LANG_CANNOT_GO_TO_INST_PARTY = 104,
+ LANG_CANNOT_GO_TO_INST_GM = 105,
+ LANG_CANNOT_GO_INST_INST = 106,
+ LANG_CANNOT_SUMMON_INST_INST = 107,
+
+ LANG_SUMMONING = 108,
+ LANG_SUMMONED_BY = 109,
+ LANG_TELEPORTING_TO = 110,
+ LANG_TELEPORTED_TO_BY = 111,
+ LANG_NO_PLAYER = 112,
+ LANG_APPEARING_AT = 113,
+ LANG_APPEARING_TO = 114,
+
+ LANG_BAD_VALUE = 115,
+ LANG_NO_CHAR_SELECTED = 116,
+ LANG_NOT_IN_GROUP = 117,
+
+ LANG_YOU_CHANGE_HP = 118,
+ LANG_YOURS_HP_CHANGED = 119,
+ LANG_YOU_CHANGE_MANA = 120,
+ LANG_YOURS_MANA_CHANGED = 121,
+ LANG_YOU_CHANGE_ENERGY = 122,
+ LANG_YOURS_ENERGY_CHANGED = 123,
+
+ LANG_CURRENT_ENERGY = 124, //log
+ LANG_YOU_CHANGE_RAGE = 125,
+ LANG_YOURS_RAGE_CHANGED = 126,
+ LANG_YOU_CHANGE_LVL = 127,
+ LANG_CURRENT_FACTION = 128,
+ LANG_WRONG_FACTION = 129,
+ LANG_YOU_CHANGE_FACTION = 130,
+ LANG_YOU_CHANGE_SPELLFLATID = 131,
+ LANG_YOURS_SPELLFLATID_CHANGED = 132,
+ LANG_YOU_GIVE_TAXIS = 133,
+ LANG_YOU_REMOVE_TAXIS = 134,
+ LANG_YOURS_TAXIS_ADDED = 135,
+ LANG_YOURS_TAXIS_REMOVED = 136,
+
+ LANG_YOU_CHANGE_ASPEED = 137,
+ LANG_YOURS_ASPEED_CHANGED = 138,
+ LANG_YOU_CHANGE_SPEED = 139,
+ LANG_YOURS_SPEED_CHANGED = 140,
+ LANG_YOU_CHANGE_SWIM_SPEED = 141,
+ LANG_YOURS_SWIM_SPEED_CHANGED = 142,
+ LANG_YOU_CHANGE_BACK_SPEED = 143,
+ LANG_YOURS_BACK_SPEED_CHANGED = 144,
+ LANG_YOU_CHANGE_FLY_SPEED = 145,
+ LANG_YOURS_FLY_SPEED_CHANGED = 146,
+
+ LANG_YOU_CHANGE_SIZE = 147,
+ LANG_YOURS_SIZE_CHANGED = 148,
+ LANG_NO_MOUNT = 149,
+ LANG_YOU_GIVE_MOUNT = 150,
+ LANG_MOUNT_GIVED = 151,
+
+ LANG_CURRENT_MONEY = 152,
+ LANG_YOU_TAKE_ALL_MONEY = 153,
+ LANG_YOURS_ALL_MONEY_GONE = 154,
+ LANG_YOU_TAKE_MONEY = 155,
+ LANG_YOURS_MONEY_TAKEN = 156,
+ LANG_YOU_GIVE_MONEY = 157,
+ LANG_YOURS_MONEY_GIVEN = 158,
+ LANG_YOU_HEAR_SOUND = 159,
+
+ LANG_NEW_MONEY = 160, // Log
+
+ LANG_REMOVE_BIT = 161,
+ LANG_SET_BIT = 162,
+ LANG_COMMAND_TELE_TABLEEMPTY = 163,
+ LANG_COMMAND_TELE_NOTFOUND = 164,
+ LANG_COMMAND_TELE_PARAMETER = 165,
+ LANG_COMMAND_TELE_NOLOCATION = 166,
+ // 167 // not used
+ LANG_COMMAND_TELE_LOCATION = 168,
+
+ LANG_MAIL_SENT = 169,
+ LANG_SOUND_NOT_EXIST = 170,
+ // Room for more level 1 171-199 not used
+
+ // level 2 chat
+ LANG_NO_SELECTION = 200,
+ LANG_OBJECT_GUID = 201,
+ LANG_TOO_LONG_NAME = 202,
+ LANG_CHARS_ONLY = 203,
+ LANG_TOO_LONG_SUBNAME = 204,
+ LANG_NOT_IMPLEMENTED = 205,
+
+ LANG_ITEM_ADDED_TO_LIST = 206,
+ LANG_ITEM_NOT_FOUND = 207,
+ LANG_ITEM_DELETED_FROM_LIST = 208,
+ LANG_ITEM_NOT_IN_LIST = 209,
+ LANG_ITEM_ALREADY_IN_LIST = 210,
+
+ LANG_RESET_SPELLS_ONLINE = 211,
+ LANG_RESET_SPELLS_OFFLINE = 212,
+ LANG_RESET_TALENTS_ONLINE = 213,
+ LANG_RESET_TALENTS_OFFLINE = 214,
+ LANG_RESET_SPELLS = 215,
+ LANG_RESET_TALENTS = 216,
+
+ LANG_RESETALL_UNKNOWN_CASE = 217,
+ LANG_RESETALL_SPELLS = 218,
+ LANG_RESETALL_TALENTS = 219,
+
+ LANG_WAYPOINT_NOTFOUND = 220,
+ LANG_WAYPOINT_NOTFOUNDLAST = 221,
+ LANG_WAYPOINT_NOTFOUNDSEARCH = 222,
+ LANG_WAYPOINT_NOTFOUNDDBPROBLEM = 223,
+ LANG_WAYPOINT_CREATSELECTED = 224,
+ LANG_WAYPOINT_CREATNOTFOUND = 225,
+ LANG_WAYPOINT_VP_SELECT = 226,
+ LANG_WAYPOINT_VP_NOTFOUND = 227,
+ LANG_WAYPOINT_VP_NOTCREATED = 228,
+ LANG_WAYPOINT_VP_ALLREMOVED = 229,
+ LANG_WAYPOINT_NOTCREATED = 230,
+ LANG_WAYPOINT_NOGUID = 231,
+ LANG_WAYPOINT_NOWAYPOINTGIVEN = 232,
+ LANG_WAYPOINT_ARGUMENTREQ = 233,
+ LANG_WAYPOINT_ADDED = 234,
+ LANG_WAYPOINT_ADDED_NO = 235,
+ LANG_WAYPOINT_CHANGED = 236,
+ LANG_WAYPOINT_CHANGED_NO = 237,
+ LANG_WAYPOINT_EXPORTED = 238,
+ LANG_WAYPOINT_NOTHINGTOEXPORT = 239,
+ LANG_WAYPOINT_IMPORTED = 240,
+ LANG_WAYPOINT_REMOVED = 241,
+ LANG_WAYPOINT_NOTREMOVED = 242,
+ LANG_WAYPOINT_TOOFAR1 = 243,
+ LANG_WAYPOINT_TOOFAR2 = 244,
+ LANG_WAYPOINT_TOOFAR3 = 245,
+ LANG_WAYPOINT_INFO_TITLE = 246,
+ LANG_WAYPOINT_INFO_WAITTIME = 247,
+ LANG_WAYPOINT_INFO_MODEL = 248,
+ LANG_WAYPOINT_INFO_EMOTE = 249,
+ LANG_WAYPOINT_INFO_SPELL = 250,
+ LANG_WAYPOINT_INFO_TEXT = 251,
+ LANG_WAYPOINT_INFO_AISCRIPT = 252,
+
+ LANG_RENAME_PLAYER = 253,
+ LANG_RENAME_PLAYER_GUID = 254,
+
+ LANG_WAYPOINT_WPCREATNOTFOUND = 255,
+ LANG_WAYPOINT_NPCNOTFOUND = 256,
+
+ LANG_MOVE_TYPE_SET = 257,
+ LANG_MOVE_TYPE_SET_NODEL = 258,
+ LANG_USE_BOL = 259,
+ LANG_VALUE_SAVED = 260,
+ LANG_VALUE_SAVED_REJOIN = 261,
+
+ LANG_COMMAND_GOAREATRNOTFOUND = 262,
+ LANG_INVALID_TARGET_COORD = 263,
+ LANG_INVALID_ZONE_COORD = 264,
+ LANG_INVALID_ZONE_MAP = 265,
+ LANG_COMMAND_TARGETOBJNOTFOUND = 266,
+ LANG_COMMAND_GOOBJNOTFOUND = 267,
+ LANG_COMMAND_GOCREATNOTFOUND = 268,
+ LANG_COMMAND_GOCREATMULTIPLE = 269,
+ LANG_COMMAND_DELCREATMESSAGE = 270,
+ LANG_COMMAND_CREATUREMOVED = 271,
+ LANG_COMMAND_CREATUREATSAMEMAP = 272,
+ LANG_COMMAND_OBJNOTFOUND = 273,
+ LANG_COMMAND_DELOBJREFERCREATURE = 274,
+ LANG_COMMAND_DELOBJMESSAGE = 275,
+ LANG_COMMAND_TURNOBJMESSAGE = 276,
+ LANG_COMMAND_MOVEOBJMESSAGE = 277,
+ LANG_COMMAND_VENDORSELECTION = 278,
+ LANG_COMMAND_NEEDITEMSEND = 279,
+ LANG_COMMAND_ADDVENDORITEMITEMS = 280,
+ LANG_COMMAND_KICKSELF = 281,
+ LANG_COMMAND_KICKMESSAGE = 282,
+ LANG_COMMAND_KICKNOTFOUNDPLAYER = 283,
+ LANG_COMMAND_WHISPERACCEPTING = 284,
+ LANG_COMMAND_WHISPERON = 285,
+ LANG_COMMAND_WHISPEROFF = 286,
+ LANG_COMMAND_CREATGUIDNOTFOUND = 287,
+ LANG_COMMAND_TICKETCOUNT = 288,
+ LANG_COMMAND_TICKETNEW = 289,
+ LANG_COMMAND_TICKETVIEW = 290,
+ LANG_COMMAND_TICKETON = 291,
+ LANG_COMMAND_TICKETOFF = 292,
+ LANG_COMMAND_TICKENOTEXIST = 293,
+ LANG_COMMAND_ALLTICKETDELETED = 294,
+ LANG_COMMAND_TICKETPLAYERDEL = 295,
+ LANG_COMMAND_TICKETDEL = 296,
+ LANG_COMMAND_SPAWNDIST = 297,
+ LANG_COMMAND_SPAWNTIME = 298,
+ LANG_COMMAND_MODIFY_HONOR = 299,
+
+ LANG_YOUR_CHAT_DISABLED = 300,
+ LANG_YOU_DISABLE_CHAT = 301,
+ LANG_CHAT_ALREADY_ENABLED = 302,
+ LANG_YOUR_CHAT_ENABLED = 303,
+ LANG_YOU_ENABLE_CHAT = 304,
+
+ LANG_COMMAND_MODIFY_REP = 305,
+ LANG_COMMAND_MODIFY_ARENA = 306,
+ LANG_COMMAND_FACTION_NOTFOUND = 307,
+ LANG_COMMAND_FACTION_UNKNOWN = 308,
+ LANG_COMMAND_FACTION_INVPARAM = 309,
+ LANG_COMMAND_FACTION_DELTA = 310,
+ LANG_FACTION_LIST = 311,
+ LANG_FACTION_VISIBLE = 312,
+ LANG_FACTION_ATWAR = 313,
+ LANG_FACTION_PEACE_FORCED = 314,
+ LANG_FACTION_HIDDEN = 315,
+ LANG_FACTION_INVISIBLE_FORCED = 316,
+ LANG_FACTION_INACTIVE = 317,
+ LANG_REP_HATED = 318,
+ LANG_REP_HOSTILE = 319,
+ LANG_REP_UNFRIENDLY = 320,
+ LANG_REP_NEUTRAL = 321,
+ LANG_REP_FRIENDLY = 322,
+ LANG_REP_HONORED = 323,
+ LANG_REP_REVERED = 324,
+ LANG_REP_EXALTED = 325,
+ LANG_COMMAND_FACTION_NOREP_ERROR = 326,
+ LANG_FACTION_NOREPUTATION = 327,
+ LANG_LOOKUP_PLAYER_ACCOUNT = 328,
+ LANG_LOOKUP_PLAYER_CHARACTER = 329,
+ LANG_NO_PLAYERS_FOUND = 330,
+ LANG_EXTENDED_COST_NOT_EXIST = 331,
+ 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,
+ LANG_YOU_CHANGE_SECURITY = 401,
+ LANG_YOURS_SECURITY_CHANGED = 402,
+ LANG_YOURS_SECURITY_IS_LOW = 403,
+ LANG_CREATURE_MOVE_DISABLED = 404,
+ LANG_CREATURE_MOVE_ENABLED = 405,
+ LANG_NO_WEATHER = 406,
+ LANG_WEATHER_DISABLED = 407,
+
+ LANG_BAN_YOUBANNED = 408,
+ LANG_BAN_YOUPERMBANNED = 409,
+ LANG_BAN_NOTFOUND = 410,
+
+ LANG_UNBAN_UNBANNED = 411,
+ LANG_UNBAN_ERROR = 412,
+
+ LANG_BANINFO_NOACCOUNT = 413,
+ LANG_BANINFO_NOCHARACTER = 414,
+ LANG_BANINFO_NOIP = 415,
+ LANG_BANINFO_NOACCOUNTBAN = 416,
+ LANG_BANINFO_BANHISTORY = 417,
+ LANG_BANINFO_HISTORYENTRY = 418,
+ LANG_BANINFO_INFINITE = 419,
+ LANG_BANINFO_NEVER = 420,
+ LANG_BANINFO_YES = 421,
+ LANG_BANINFO_NO = 422,
+ LANG_BANINFO_IPENTRY = 423,
+
+ LANG_BANLIST_NOIP = 424,
+ LANG_BANLIST_NOACCOUNT = 425,
+ LANG_BANLIST_NOCHARACTER = 426,
+ LANG_BANLIST_MATCHINGIP = 427,
+ LANG_BANLIST_MATCHINGACCOUNT = 428,
+
+ LANG_COMMAND_LEARN_MANY_SPELLS = 429,
+ LANG_COMMAND_LEARN_CLASS_SPELLS = 430,
+ LANG_COMMAND_LEARN_CLASS_TALENTS = 431,
+ LANG_COMMAND_LEARN_ALL_LANG = 432,
+ LANG_COMMAND_LEARN_ALL_CRAFT = 433,
+ LANG_COMMAND_COULDNOTFIND = 434,
+ LANG_COMMAND_ITEMIDINVALID = 435,
+ LANG_COMMAND_NOITEMFOUND = 436,
+ LANG_COMMAND_LISTOBJINVALIDID = 437,
+ LANG_COMMAND_LISTITEMMESSAGE = 438,
+ LANG_COMMAND_LISTOBJMESSAGE = 439,
+ LANG_COMMAND_INVALIDCREATUREID = 440,
+ LANG_COMMAND_LISTCREATUREMESSAGE = 441,
+ LANG_COMMAND_NOAREAFOUND = 442,
+ LANG_COMMAND_NOITEMSETFOUND = 443,
+ LANG_COMMAND_NOSKILLFOUND = 444,
+ LANG_COMMAND_NOSPELLFOUND = 445,
+ LANG_COMMAND_NOQUESTFOUND = 446,
+ LANG_COMMAND_NOCREATUREFOUND = 447,
+ LANG_COMMAND_NOGAMEOBJECTFOUND = 448,
+ LANG_COMMAND_GRAVEYARDNOEXIST = 449,
+ LANG_COMMAND_GRAVEYARDALRLINKED = 450,
+ LANG_COMMAND_GRAVEYARDLINKED = 451,
+ LANG_COMMAND_GRAVEYARDWRONGZONE = 452,
+ LANG_COMMAND_GRAVEYARDWRONGTEAM = 453,
+ LANG_COMMAND_GRAVEYARDERROR = 454,
+ LANG_COMMAND_GRAVEYARD_NOTEAM = 455,
+ LANG_COMMAND_GRAVEYARD_ANY = 456,
+ LANG_COMMAND_GRAVEYARD_ALLIANCE = 457,
+ LANG_COMMAND_GRAVEYARD_HORDE = 458,
+ LANG_COMMAND_GRAVEYARDNEAREST = 459,
+ LANG_COMMAND_ZONENOGRAVEYARDS = 460,
+ LANG_COMMAND_ZONENOGRAFACTION = 461,
+ LANG_COMMAND_TP_ALREADYEXIST = 462,
+ LANG_COMMAND_TP_ADDED = 463,
+ LANG_COMMAND_TP_ADDEDERR = 464,
+ LANG_COMMAND_TP_DELETED = 465,
+ // 466, // not used
+
+ LANG_COMMAND_TARGET_LISTAURAS = 467,
+ LANG_COMMAND_TARGET_AURADETAIL = 468,
+ LANG_COMMAND_TARGET_LISTAURATYPE = 469,
+ LANG_COMMAND_TARGET_AURASIMPLE = 470,
+
+ LANG_COMMAND_QUEST_NOTFOUND = 471,
+ LANG_COMMAND_QUEST_STARTFROMITEM = 472,
+ LANG_COMMAND_QUEST_REMOVED = 473,
+ LANG_COMMAND_QUEST_REWARDED = 474,
+ LANG_COMMAND_QUEST_COMPLETE = 475,
+ LANG_COMMAND_QUEST_ACTIVE = 476,
+
+ LANG_COMMAND_FLYMODE_STATUS = 477,
+
+ LANG_COMMAND_OPCODESENT = 478,
+
+ LANG_COMMAND_IMPORT_SUCCESS = 479,
+ LANG_COMMAND_IMPORT_FAILED = 480,
+ LANG_COMMAND_EXPORT_SUCCESS = 481,
+ LANG_COMMAND_EXPORT_FAILED = 482,
+
+ LANG_COMMAND_SPELL_BROKEN = 483,
+
+ LANG_SET_SKILL = 484,
+ LANG_SET_SKILL_ERROR = 485,
+
+ LANG_INVALID_SKILL_ID = 486,
+ LANG_LEARNING_GM_SKILLS = 487,
+ LANG_YOU_KNOWN_SPELL = 488,
+ LANG_TARGET_KNOWN_SPELL = 489,
+ LANG_UNKNOWN_SPELL = 490,
+ LANG_FORGET_SPELL = 491,
+ LANG_REMOVEALL_COOLDOWN = 492,
+ LANG_REMOVE_COOLDOWN = 493,
+
+ LANG_ADDITEM = 494, //log
+ LANG_ADDITEMSET = 495, //log
+ LANG_REMOVEITEM = 496,
+ LANG_ITEM_CANNOT_CREATE = 497,
+ LANG_INSERT_GUILD_NAME = 498,
+ LANG_PLAYER_NOT_FOUND = 499,
+ LANG_PLAYER_IN_GUILD = 500,
+ LANG_GUILD_NOT_CREATED = 501,
+ LANG_NO_ITEMS_FROM_ITEMSET_FOUND = 502,
+
+ LANG_DISTANCE = 503,
+
+ LANG_ITEM_SLOT = 504,
+ LANG_ITEM_SLOT_NOT_EXIST = 505,
+ LANG_ITEM_ADDED_TO_SLOT = 506,
+ LANG_ITEM_SAVE_FAILED = 507,
+ LANG_ITEMLIST_SLOT = 508,
+ LANG_ITEMLIST_MAIL = 509,
+ LANG_ITEMLIST_AUCTION = 510,
+
+ LANG_WRONG_LINK_TYPE = 511,
+ LANG_ITEM_LIST = 512,
+ LANG_QUEST_LIST = 513,
+ LANG_CREATURE_ENTRY_LIST = 514,
+ LANG_CREATURE_LIST = 515,
+ LANG_GO_ENTRY_LIST = 516,
+ LANG_GO_LIST = 517,
+ LANG_ITEMSET_LIST = 518,
+ LANG_TELE_LIST = 519,
+ LANG_SPELL_LIST = 520,
+ LANG_SKILL_LIST = 521,
+
+ LANG_GAMEOBJECT_NOT_EXIST = 522,
+
+ LANG_GAMEOBJECT_CURRENT = 523, //log
+ LANG_GAMEOBJECT_DETAIL = 524,
+ LANG_GAMEOBJECT_ADD = 525,
+
+ LANG_MOVEGENS_LIST = 526,
+ LANG_MOVEGENS_IDLE = 527,
+ LANG_MOVEGENS_RANDOM = 528,
+ LANG_MOVEGENS_WAYPOINT = 529,
+ LANG_MOVEGENS_ANIMAL_RANDOM = 530,
+ LANG_MOVEGENS_CONFUSED = 531,
+ LANG_MOVEGENS_TARGETED_PLAYER = 532,
+ LANG_MOVEGENS_TARGETED_CREATURE = 533,
+ LANG_MOVEGENS_TARGETED_NULL = 534,
+ LANG_MOVEGENS_HOME_CREATURE = 535,
+ LANG_MOVEGENS_HOME_PLAYER = 536,
+ LANG_MOVEGENS_FLIGHT = 537,
+ LANG_MOVEGENS_UNKNOWN = 538,
+
+ LANG_NPCINFO_CHAR = 539,
+ LANG_NPCINFO_LEVEL = 540,
+ LANG_NPCINFO_HEALTH = 541,
+ LANG_NPCINFO_FLAGS = 542,
+ LANG_NPCINFO_LOOT = 543,
+ LANG_NPCINFO_POSITION = 544,
+ LANG_NPCINFO_VENDOR = 545,
+ LANG_NPCINFO_TRAINER = 546,
+ LANG_NPCINFO_DUNGEON_ID = 547,
+
+ LANG_PINFO_ACCOUNT = 548,
+ LANG_PINFO_LEVEL = 549,
+ LANG_PINFO_NO_REP = 550,
+
+ LANG_YOU_SET_EXPLORE_ALL = 551,
+ LANG_YOU_SET_EXPLORE_NOTHING = 552,
+ LANG_YOURS_EXPLORE_SET_ALL = 553,
+ LANG_YOURS_EXPLORE_SET_NOTHING = 554,
+
+ LANG_HOVER_ENABLED = 555,
+ LANG_HOVER_DISABLED = 556,
+ LANG_YOURS_LEVEL_UP = 557,
+ LANG_YOURS_LEVEL_DOWN = 558,
+ LANG_YOURS_LEVEL_PROGRESS_RESET = 559,
+ LANG_EXPLORE_AREA = 560,
+ LANG_UNEXPLORE_AREA = 561,
+
+ LANG_UPDATE = 562,
+ LANG_UPDATE_CHANGE = 563,
+ LANG_TOO_BIG_INDEX = 564,
+ LANG_SET_UINT = 565, //log
+ LANG_SET_UINT_FIELD = 566,
+ LANG_SET_FLOAT = 567, //log
+ LANG_SET_FLOAT_FIELD = 568,
+ LANG_GET_UINT = 569, //log
+ LANG_GET_UINT_FIELD = 570,
+ LANG_GET_FLOAT = 571, //log
+ LANG_GET_FLOAT_FIELD = 572,
+ LANG_SET_32BIT = 573, //log
+ LANG_SET_32BIT_FIELD = 574,
+ LANG_CHANGE_32BIT = 575, //log
+ LANG_CHANGE_32BIT_FIELD = 576,
+
+ LANG_INVISIBLE_INVISIBLE = 577,
+ LANG_INVISIBLE_VISIBLE = 578,
+ LANG_SELECTED_TARGET_NOT_HAVE_VICTIM = 579,
+
+ LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST = 580,
+ LANG_COMMAND_NEAROBJMESSAGE = 581,
+ LANG_COMMAND_RAWPAWNTIMES = 582,
+
+ LANG_EVENT_ENTRY_LIST = 583,
+ LANG_NOEVENTFOUND = 584,
+ LANG_EVENT_NOT_EXIST = 585,
+ LANG_EVENT_INFO = 586,
+ LANG_EVENT_ALREADY_ACTIVE = 587,
+ LANG_EVENT_NOT_ACTIVE = 588,
+
+ LANG_MOVEGENS_POINT = 589,
+ LANG_MOVEGENS_FEAR = 590,
+ LANG_MOVEGENS_DISTRACT = 591,
+
+ LANG_COMMAND_LEARN_ALL_RECIPES = 592,
+
+ // Battleground
+ LANG_BG_A_WINS = 600,
+ LANG_BG_H_WINS = 601,
+ LANG_BG_WS_ONE_MINUTE = 602,
+ LANG_BG_WS_HALF_MINUTE = 603,
+ LANG_BG_WS_BEGIN = 604,
+
+ LANG_BG_WS_CAPTURED_HF = 605,
+ LANG_BG_WS_CAPTURED_AF = 606,
+ LANG_BG_WS_DROPPED_HF = 607,
+ LANG_BG_WS_DROPPED_AF = 608,
+ LANG_BG_WS_RETURNED_AF = 609,
+ LANG_BG_WS_RETURNED_HF = 610,
+ LANG_BG_WS_PICKEDUP_HF = 611,
+ LANG_BG_WS_PICKEDUP_AF = 612,
+ LANG_BG_WS_F_PLACED = 613,
+ LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED = 614,
+ LANG_BG_WS_HORDE_FLAG_RESPAWNED = 615,
+
+ LANG_BG_EY_ONE_MINUTE = 636,
+ LANG_BG_EY_HALF_MINUTE = 637,
+ LANG_BG_EY_BEGIN = 638,
+
+ LANG_BG_AB_ALLY = 650,
+ LANG_BG_AB_HORDE = 651,
+ LANG_BG_AB_NODE_STABLES = 652,
+ LANG_BG_AB_NODE_BLACKSMITH = 653,
+ LANG_BG_AB_NODE_FARM = 654,
+ LANG_BG_AB_NODE_LUMBER_MILL = 655,
+ LANG_BG_AB_NODE_GOLD_MINE = 656,
+ LANG_BG_AB_NODE_TAKEN = 657,
+ LANG_BG_AB_NODE_DEFENDED = 658,
+ LANG_BG_AB_NODE_ASSAULTED = 659,
+ LANG_BG_AB_NODE_CLAIMED = 660,
+ LANG_BG_AB_ONEMINTOSTART = 661,
+ LANG_BG_AB_HALFMINTOSTART = 662,
+ LANG_BG_AB_STARTED = 663,
+ LANG_BG_AB_A_NEAR_VICTORY = 664,
+ LANG_BG_AB_H_NEAR_VICTORY = 665,
+ LANG_BG_MARK_BY_MAIL = 666,
+
+ LANG_BG_EY_HAS_TAKEN_A_M_TOWER = 667,
+ LANG_BG_EY_HAS_TAKEN_H_M_TOWER = 668,
+ LANG_BG_EY_HAS_TAKEN_A_D_RUINS = 669,
+ LANG_BG_EY_HAS_TAKEN_H_D_RUINS = 670,
+ LANG_BG_EY_HAS_TAKEN_A_B_TOWER = 671,
+ LANG_BG_EY_HAS_TAKEN_H_B_TOWER = 672,
+ LANG_BG_EY_HAS_TAKEN_A_F_RUINS = 673,
+ LANG_BG_EY_HAS_TAKEN_H_F_RUINS = 674,
+ LANG_BG_EY_HAS_LOST_A_M_TOWER = 675,
+ LANG_BG_EY_HAS_LOST_H_M_TOWER = 676,
+ LANG_BG_EY_HAS_LOST_A_D_RUINS = 677,
+ LANG_BG_EY_HAS_LOST_H_D_RUINS = 678,
+ LANG_BG_EY_HAS_LOST_A_B_TOWER = 679,
+ LANG_BG_EY_HAS_LOST_H_B_TOWER = 680,
+ LANG_BG_EY_HAS_LOST_A_F_RUINS = 681,
+ LANG_BG_EY_HAS_LOST_H_F_RUINS = 682,
+ LANG_BG_EY_HAS_TAKEN_FLAG = 683,
+ LANG_BG_EY_CAPTURED_FLAG_A = 684,
+ LANG_BG_EY_CAPTURED_FLAG_H = 685,
+ LANG_BG_EY_DROPPED_FLAG = 686,
+ LANG_BG_EY_RESETED_FLAG = 687,
+
+ LANG_ARENA_ONE_TOOLOW = 700,
+ LANG_ARENA_ONE_MINUTE = 701,
+ LANG_ARENA_THIRTY_SECONDS = 702,
+ LANG_ARENA_FIFTEEN_SECONDS = 703,
+ LANG_ARENA_BEGUN = 704,
+
+ LANG_WAIT_BEFORE_SPEAKING = 705,
+ LANG_NOT_EQUIPPED_ITEM = 706,
+ LANG_PLAYER_DND = 707,
+ LANG_PLAYER_AFK = 708,
+ LANG_PLAYER_DND_DEFAULT = 709,
+ LANG_PLAYER_AFK_DEFAULT = 710,
+
+ LANG_BG_GROUP_TOO_LARGE = 711,
+ LANG_ARENA_GROUP_TOO_LARGE = 712,
+ 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
diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp
index 19dec5500bc..9bcbb80b811 100644
--- a/src/game/Level1.cpp
+++ b/src/game/Level1.cpp
@@ -1,2479 +1,2479 @@
-/*
- * 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 "World.h"
-#include "ObjectMgr.h"
-#include "Player.h"
-#include "Opcodes.h"
-#include "Chat.h"
-#include "Log.h"
-#include "MapManager.h"
-#include "ObjectAccessor.h"
-#include "Language.h"
-#include "CellImpl.h"
-#include "InstanceSaveMgr.h"
-#include "Util.h"
-#ifdef _DEBUG_VMAPS
-#include "VMapFactory.h"
-#endif
-
-bool ChatHandler::HandleSayCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Creature* pCreature = getSelectedCreature();
- if(!pCreature)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- pCreature->Say(args, LANG_UNIVERSAL, 0);
-
- return true;
-}
-
-bool ChatHandler::HandleYellCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Creature* pCreature = getSelectedCreature();
- if(!pCreature)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- pCreature->Yell(args, LANG_UNIVERSAL, 0);
-
- return true;
-}
-
-//show text emote by creature in chat
-bool ChatHandler::HandleTextEmoteCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Creature* pCreature = getSelectedCreature();
-
- if(!pCreature)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- pCreature->TextEmote(args, 0);
-
- return true;
-}
-
-// make npc whisper to player
-bool ChatHandler::HandleNpcWhisperCommand(const char* args)
-{
- if(!*args)
- return false;
-
- char* receiver_str = strtok((char*)args, " ");
- char* text = strtok(NULL, "");
-
- uint64 guid = m_session->GetPlayer()->GetSelection();
- Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
-
- if(!pCreature || !receiver_str || !text)
- {
- return false;
- }
-
- uint64 receiver_guid= atol(receiver_str);
-
- pCreature->Whisper(text,receiver_guid);
-
- return true;
-}
-
-// global announce
-bool ChatHandler::HandleAnnounceCommand(const char* args)
-{
- if(!*args)
- return false;
-
- sWorld.SendWorldText(LANG_SYSTEMMESSAGE,args);
- return true;
-}
-
-//notification player at the screen
-bool ChatHandler::HandleNotifyCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string str = GetMangosString(LANG_GLOBAL_NOTIFY);
- str += args;
-
- WorldPacket data(SMSG_NOTIFICATION, (str.size()+1));
- data << str;
- sWorld.SendGlobalMessage(&data);
-
- return true;
-}
-
-//Enable\Dissable GM Mode
-bool ChatHandler::HandleGMmodeCommand(const char* args)
-{
- if(!*args)
- {
- 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;
-
- if (argstr == "on")
- {
- m_session->GetPlayer()->SetGameMaster(true);
- m_session->SendNotification(LANG_GM_ON);
- #ifdef _DEBUG_VMAPS
- VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
- vMapManager->processCommand("stoplog");
- #endif
- return true;
- }
-
- if (argstr == "off")
- {
- m_session->GetPlayer()->SetGameMaster(false);
- m_session->SendNotification(LANG_GM_OFF);
- #ifdef _DEBUG_VMAPS
- VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
- vMapManager->processCommand("startlog");
- #endif
- return true;
- }
-
- SendSysMessage(LANG_USE_BOL);
- SetSentErrorMessage(true);
- 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)
-{
- if (!*args)
- {
- PSendSysMessage(LANG_YOU_ARE, m_session->GetPlayer()->isGMVisible() ? GetMangosString(LANG_VISIBLE) : GetMangosString(LANG_INVISIBLE));
- return true;
- }
-
- std::string argstr = (char*)args;
-
- if (argstr == "on")
- {
- m_session->GetPlayer()->SetGMVisible(true);
- m_session->SendNotification(LANG_INVISIBLE_VISIBLE);
- return true;
- }
-
- if (argstr == "off")
- {
- m_session->SendNotification(LANG_INVISIBLE_INVISIBLE);
- m_session->GetPlayer()->SetGMVisible(false);
- return true;
- }
-
- SendSysMessage(LANG_USE_BOL);
- SetSentErrorMessage(true);
- return false;
-}
-
-bool ChatHandler::HandleGPSCommand(const char* args)
-{
- WorldObject *obj = NULL;
- if (*args)
- {
- std::string name = args;
- if(normalizePlayerName(name))
- obj = objmgr.GetPlayer(name.c_str());
-
- if(!obj)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
- }
- else
- {
- obj = getSelectedUnit();
-
- if(!obj)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
- }
- CellPair cell_val = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
- Cell cell(cell_val);
-
- uint32 zone_id = obj->GetZoneId();
- uint32 area_id = obj->GetAreaId();
-
- MapEntry const* mapEntry = sMapStore.LookupEntry(obj->GetMapId());
- AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zone_id);
- AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(area_id);
-
- float zone_x = obj->GetPositionX();
- float zone_y = obj->GetPositionY();
-
- Map2ZoneCoordinates(zone_x,zone_y,zone_id);
-
- Map const *map = MapManager::Instance().GetMap(obj->GetMapId(), obj);
- float ground_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), MAX_HEIGHT);
- float floor_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ());
-
- GridPair p = MaNGOS::ComputeGridPair(obj->GetPositionX(), obj->GetPositionY());
-
- int gx=63-p.x_coord;
- int gy=63-p.y_coord;
-
- uint32 have_map = Map::ExistMap(obj->GetMapId(),gx,gy) ? 1 : 0;
- uint32 have_vmap = Map::ExistVMap(obj->GetMapId(),gx,gy) ? 1 : 0;
-
- PSendSysMessage(LANG_MAP_POSITION,
- obj->GetMapId(), (mapEntry ? mapEntry->name[m_session->GetSessionDbcLocale()] : "<unknown>" ),
- zone_id, (zoneEntry ? zoneEntry->area_name[m_session->GetSessionDbcLocale()] : "<unknown>" ),
- area_id, (areaEntry ? areaEntry->area_name[m_session->GetSessionDbcLocale()] : "<unknown>" ),
- obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(),
- cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(),
- zone_x, zone_y, ground_z, floor_z, have_map, have_vmap );
-
- sLog.outDebug("Player %s GPS call for %s '%s' (%s: %u):",
- m_session->GetPlayer()->GetName(),
- (obj->GetTypeId() == TYPEID_PLAYER ? "player" : "creature"), obj->GetName(),
- (obj->GetTypeId() == TYPEID_PLAYER ? "GUID" : "Entry"), (obj->GetTypeId() == TYPEID_PLAYER ? obj->GetGUIDLow(): obj->GetEntry()) );
- sLog.outDebug(GetMangosString(LANG_MAP_POSITION),
- obj->GetMapId(), (mapEntry ? mapEntry->name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ),
- zone_id, (zoneEntry ? zoneEntry->area_name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ),
- area_id, (areaEntry ? areaEntry->area_name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ),
- obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(),
- cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(),
- zone_x, zone_y, ground_z, floor_z, have_map, have_vmap );
-
- return true;
-}
-
-//Summon Player
-bool ChatHandler::HandleNamegoCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string name = args;
-
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = objmgr.GetPlayer(name.c_str());
- if (chr)
- {
- if(chr->IsBeingTeleported()==true)
- {
- PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- Map* pMap = MapManager::Instance().GetMap(m_session->GetPlayer()->GetMapId(),m_session->GetPlayer());
-
- if(pMap->IsBattleGroundOrArena())
- {
- // cannot summon to bg
- SendSysMessage(LANG_CANNOT_SUMMON_TO_BG);
- SetSentErrorMessage(true);
- return false;
- }
- else if(pMap->IsDungeon())
- {
- Map* cMap = MapManager::Instance().GetMap(chr->GetMapId(),chr);
- if( cMap->Instanceable() && cMap->GetInstanceId() != pMap->GetInstanceId() )
- {
- // cannot summon from instance to instance
- PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- // we are in instance, and can summon only player in our group with us as lead
- if ( !m_session->GetPlayer()->GetGroup() || !chr->GetGroup() ||
- (chr->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ||
- (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) )
- // the last check is a bit excessive, but let it be, just in case
- {
- PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
- }
-
- PSendSysMessage(LANG_SUMMONING, chr->GetName(),"");
-
- if (m_session->GetPlayer()->IsVisibleGloballyFor(chr))
- ChatHandler(chr).PSendSysMessage(LANG_SUMMONED_BY, m_session->GetPlayer()->GetName());
-
- // stop flight if need
- if(chr->isInFlight())
- {
- chr->GetMotionMaster()->MovementExpired();
- chr->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- chr->SaveRecallPosition();
-
- // before GM
- float x,y,z;
- m_session->GetPlayer()->GetClosePoint(x,y,z,chr->GetObjectSize());
- chr->TeleportTo(m_session->GetPlayer()->GetMapId(),x,y,z,chr->GetOrientation());
- }
- else if (uint64 guid = objmgr.GetPlayerGUIDByName(name))
- {
- PSendSysMessage(LANG_SUMMONING, name.c_str(),GetMangosString(LANG_OFFLINE));
-
- // in point where GM stay
- Player::SavePositionInDB(m_session->GetPlayer()->GetMapId(),
- m_session->GetPlayer()->GetPositionX(),
- m_session->GetPlayer()->GetPositionY(),
- m_session->GetPlayer()->GetPositionZ(),
- m_session->GetPlayer()->GetOrientation(),
- m_session->GetPlayer()->GetZoneId(),
- guid);
- }
- else
- {
- PSendSysMessage(LANG_NO_PLAYER, args);
- SetSentErrorMessage(true);
- }
-
- return true;
-}
-
-//Teleport to Player
-bool ChatHandler::HandleGonameCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Player* _player = m_session->GetPlayer();
-
- std::string name = args;
-
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = objmgr.GetPlayer(name.c_str());
- if (chr)
- {
- Map* cMap = MapManager::Instance().GetMap(chr->GetMapId(),chr);
- if(cMap->IsBattleGroundOrArena())
- {
- // only allow if gm mode is on
- if (!_player->isGameMaster())
- {
- SendSysMessage(LANG_CANNOT_GO_TO_BG_GM);
- SetSentErrorMessage(true);
- return false;
- }
- // if already in a bg, don't let port to other
- else if (_player->GetBattleGroundId())
- {
- SendSysMessage(LANG_CANNOT_GO_TO_BG_FROM_BG);
- SetSentErrorMessage(true);
- return false;
- }
- // all's well, set bg id
- // when porting out from the bg, it will be reset to 0
- _player->SetBattleGroundId(chr->GetBattleGroundId());
- }
- else if(cMap->IsDungeon())
- {
- Map* pMap = MapManager::Instance().GetMap(_player->GetMapId(),_player);
-
- // we have to go to instance, and can go to player only if:
- // 1) we are in his group (either as leader or as member)
- // 2) we are not bound to any group and have GM mode on
- if (_player->GetGroup())
- {
- // we are in group, we can go only if we are in the player group
- if (_player->GetGroup() != chr->GetGroup())
- {
- PSendSysMessage(LANG_CANNOT_GO_TO_INST_PARTY,chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
- }
- else
- {
- // we are not in group, let's verify our GM mode
- if (!_player->isGameMaster())
- {
- PSendSysMessage(LANG_CANNOT_GO_TO_INST_GM,chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
- }
-
- // if the player or the player's group is bound to another instance
- // the player will not be bound to another one
- InstancePlayerBind *pBind = _player->GetBoundInstance(chr->GetMapId(), chr->GetDifficulty());
- if(!pBind)
- {
- Group *group = _player->GetGroup();
- InstanceGroupBind *gBind = group ? group->GetBoundInstance(chr->GetMapId(), chr->GetDifficulty()) : NULL;
- if(!gBind)
- {
- // if no bind exists, create a solo bind
- InstanceSave *save = sInstanceSaveManager.GetInstanceSave(chr->GetInstanceId());
- if(save) _player->BindToInstance(save, !save->CanReset());
- }
- }
-
- _player->SetDifficulty(chr->GetDifficulty());
- }
-
- PSendSysMessage(LANG_APPEARING_AT, chr->GetName());
-
- if (_player->IsVisibleGloballyFor(chr))
- ChatHandler(chr).PSendSysMessage(LANG_APPEARING_TO, _player->GetName());
-
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- // to point to see at target with same orientation
- float x,y,z;
- chr->GetContactPoint(m_session->GetPlayer(),x,y,z);
-
- _player->TeleportTo(chr->GetMapId(), x, y, z, _player->GetAngle( chr ), TELE_TO_GM_MODE);
-
- return true;
- }
-
- if (uint64 guid = objmgr.GetPlayerGUIDByName(name))
- {
- PSendSysMessage(LANG_APPEARING_AT, name.c_str());
-
- // to point where player stay (if loaded)
- float x,y,z,o;
- uint32 map;
- bool in_flight;
- if(Player::LoadPositionFromDB(map,x,y,z,o,in_flight,guid))
- {
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- _player->TeleportTo(map, x, y, z,_player->GetOrientation());
- return true;
- }
- }
-
- PSendSysMessage(LANG_NO_PLAYER, args);
-
- SetSentErrorMessage(true);
- return false;
-}
-
-// Teleport player to last position
-bool ChatHandler::HandleRecallCommand(const char* args)
-{
- Player* chr = NULL;
-
- if(!*args)
- {
- chr = getSelectedPlayer();
- if(!chr)
- chr = m_session->GetPlayer();
- }
- else
- {
- std::string name = args;
-
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- chr = objmgr.GetPlayer(name.c_str());
-
- if(!chr)
- {
- PSendSysMessage(LANG_NO_PLAYER, args);
- SetSentErrorMessage(true);
- return false;
- }
- }
-
- if(chr->IsBeingTeleported())
- {
- PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- // stop flight if need
- if(chr->isInFlight())
- {
- chr->GetMotionMaster()->MovementExpired();
- chr->m_taxi.ClearTaxiDestinations();
- }
-
- chr->TeleportTo(chr->m_recallMap, chr->m_recallX, chr->m_recallY, chr->m_recallZ, chr->m_recallO);
- return true;
-}
-
-//Edit Player KnownTitles
-bool ChatHandler::HandleModifyKnownTitlesCommand(const char* args)
-{
- if(!*args)
- return false;
-
- uint64 titles = 0;
-
- sscanf((char*)args, I64FMTD, &titles);
-
- Player *chr = getSelectedPlayer();
- if (!chr)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 titles2 = titles;
-
- for(int i=1; i < sCharTitlesStore.GetNumRows(); ++i)
- if(CharTitlesEntry const* tEntry = sCharTitlesStore.LookupEntry(i))
- titles2 &= ~(uint64(1) << tEntry->bit_index);
-
- titles &= ~titles2; // remove not existed titles
-
- chr->SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES, titles);
- SendSysMessage(LANG_DONE);
-
- return true;
-}
-
-//Edit Player HP
-bool ChatHandler::HandleModifyHPCommand(const char* args)
-{
- if(!*args)
- return false;
-
- // char* pHp = strtok((char*)args, " ");
- // if (!pHp)
- // return false;
-
- // char* pHpMax = strtok(NULL, " ");
- // if (!pHpMax)
- // return false;
-
- // int32 hpm = atoi(pHpMax);
- // int32 hp = atoi(pHp);
-
- int32 hp = atoi((char*)args);
- int32 hpm = atoi((char*)args);
-
- if (hp <= 0 || hpm <= 0 || hpm < hp)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_HP, chr->GetName(), hp, hpm);
- ChatHandler(chr).PSendSysMessage(LANG_YOURS_HP_CHANGED, m_session->GetPlayer()->GetName(), hp, hpm);
-
- chr->SetMaxHealth( hpm );
- chr->SetHealth( hp );
-
- return true;
-}
-
-//Edit Player Mana
-bool ChatHandler::HandleModifyManaCommand(const char* args)
-{
- if(!*args)
- return false;
-
- // char* pmana = strtok((char*)args, " ");
- // if (!pmana)
- // return false;
-
- // char* pmanaMax = strtok(NULL, " ");
- // if (!pmanaMax)
- // return false;
-
- // int32 manam = atoi(pmanaMax);
- // int32 mana = atoi(pmana);
- int32 mana = atoi((char*)args);
- int32 manam = atoi((char*)args);
-
- if (mana <= 0 || manam <= 0 || manam < mana)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_MANA, chr->GetName(), mana, manam);
- ChatHandler(chr).PSendSysMessage(LANG_YOURS_MANA_CHANGED, m_session->GetPlayer()->GetName(), mana, manam);
-
- chr->SetMaxPower(POWER_MANA,manam );
- chr->SetPower(POWER_MANA, mana );
-
- return true;
-}
-
-//Edit Player Energy
-bool ChatHandler::HandleModifyEnergyCommand(const char* args)
-{
- if(!*args)
- return false;
-
- // char* pmana = strtok((char*)args, " ");
- // if (!pmana)
- // return false;
-
- // char* pmanaMax = strtok(NULL, " ");
- // if (!pmanaMax)
- // return false;
-
- // int32 manam = atoi(pmanaMax);
- // int32 mana = atoi(pmana);
-
- int32 energy = atoi((char*)args)*10;
- int32 energym = atoi((char*)args)*10;
-
- if (energy <= 0 || energym <= 0 || energym < energy)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (!chr)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_ENERGY, chr->GetName(), energy/10, energym/10);
- ChatHandler(chr).PSendSysMessage(LANG_YOURS_ENERGY_CHANGED, m_session->GetPlayer()->GetName(), energy/10, energym/10);
-
- chr->SetMaxPower(POWER_ENERGY,energym );
- chr->SetPower(POWER_ENERGY, energy );
-
- sLog.outDetail(GetMangosString(LANG_CURRENT_ENERGY),chr->GetMaxPower(POWER_ENERGY));
-
- return true;
-}
-
-//Edit Player Rage
-bool ChatHandler::HandleModifyRageCommand(const char* args)
-{
- if(!*args)
- return false;
-
- // char* pmana = strtok((char*)args, " ");
- // if (!pmana)
- // return false;
-
- // char* pmanaMax = strtok(NULL, " ");
- // if (!pmanaMax)
- // return false;
-
- // int32 manam = atoi(pmanaMax);
- // int32 mana = atoi(pmana);
-
- int32 rage = atoi((char*)args)*10;
- int32 ragem = atoi((char*)args)*10;
-
- if (rage <= 0 || ragem <= 0 || ragem < rage)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_RAGE, chr->GetName(), rage/10, ragem/10);
- // Special case: I use GetMangosString here to get local of destination char ;)
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_RAGE_CHANGED), m_session->GetPlayer()->GetName(), rage/10, ragem/10);
-
- chr->SetMaxPower(POWER_RAGE,ragem );
- chr->SetPower(POWER_RAGE, rage );
-
- return true;
-}
-
-//Edit Player Faction
-bool ChatHandler::HandleModifyFactionCommand(const char* args)
-{
- if(!*args)
- return false;
-
- char* pfactionid = extractKeyFromLink((char*)args,"Hfaction");
-
- Creature* chr = getSelectedCreature();
- if(!chr)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!pfactionid)
- {
- if(chr)
- {
- uint32 factionid = chr->getFaction();
- uint32 flag = chr->GetUInt32Value(UNIT_FIELD_FLAGS);
- uint32 npcflag = chr->GetUInt32Value(UNIT_NPC_FLAGS);
- uint32 dyflag = chr->GetUInt32Value(UNIT_DYNAMIC_FLAGS);
- PSendSysMessage(LANG_CURRENT_FACTION,chr->GetGUIDLow(),factionid,flag,npcflag,dyflag);
- }
- return true;
- }
-
- if( !chr )
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint32 factionid = atoi(pfactionid);
- uint32 flag;
-
- char *pflag = strtok(NULL, " ");
- if (!pflag)
- flag = chr->GetUInt32Value(UNIT_FIELD_FLAGS);
- else
- flag = atoi(pflag);
-
- char* pnpcflag = strtok(NULL, " ");
-
- uint32 npcflag;
- if(!pnpcflag)
- npcflag = chr->GetUInt32Value(UNIT_NPC_FLAGS);
- else
- npcflag = atoi(pnpcflag);
-
- char* pdyflag = strtok(NULL, " ");
-
- uint32 dyflag;
- if(!pdyflag)
- dyflag = chr->GetUInt32Value(UNIT_DYNAMIC_FLAGS);
- else
- dyflag = atoi(pdyflag);
-
- if(!sFactionTemplateStore.LookupEntry(factionid))
- {
- PSendSysMessage(LANG_WRONG_FACTION, factionid);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_FACTION, chr->GetGUIDLow(),factionid,flag,npcflag,dyflag);
-
- //sprintf((char*)buf,"%s changed your Faction to %i.", m_session->GetPlayer()->GetName(), factionid);
- //FillSystemMessageData(&data, m_session, buf);
-
- //chr->GetSession()->SendPacket(&data);
-
- chr->setFaction(factionid);
- chr->SetUInt32Value(UNIT_FIELD_FLAGS,flag);
- chr->SetUInt32Value(UNIT_NPC_FLAGS,npcflag);
- chr->SetUInt32Value(UNIT_DYNAMIC_FLAGS,dyflag);
-
- return true;
-}
-
-//Edit Player Spell
-bool ChatHandler::HandleModifySpellCommand(const char* args)
-{
- if(!*args) return false;
- char* pspellflatid = strtok((char*)args, " ");
- if (!pspellflatid)
- return false;
-
- char* pop = strtok(NULL, " ");
- if (!pop)
- return false;
-
- char* pval = strtok(NULL, " ");
- if (!pval)
- return false;
-
- uint16 mark;
-
- char* pmark = strtok(NULL, " ");
-
- uint8 spellflatid = atoi(pspellflatid);
- uint8 op = atoi(pop);
- uint16 val = atoi(pval);
- if(!pmark)
- mark = 65535;
- else
- mark = atoi(pmark);
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_SPELLFLATID, spellflatid, val, mark, chr->GetName());
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPELLFLATID_CHANGED, m_session->GetPlayer()->GetName(), spellflatid, val, mark);
-
- WorldPacket data(SMSG_SET_FLAT_SPELL_MODIFIER, (1+1+2+2));
- data << uint8(spellflatid);
- data << uint8(op);
- data << uint16(val);
- data << uint16(mark);
- chr->GetSession()->SendPacket(&data);
-
- return true;
-}
-
-//Edit Player TP
-bool ChatHandler::HandleModifyTalentCommand (const char* args)
-{
- if (!*args)
- return false;
-
- int tp = atoi((char*)args);
- if (tp>0)
- {
- Player* player = getSelectedPlayer();
- if(!player)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
- player->SetFreeTalentPoints(tp);
- return true;
- }
- return false;
-}
-
-//Enable On\OFF all taxi paths
-bool ChatHandler::HandleTaxiCheatCommand(const char* args)
-{
- if (!*args)
- {
- SendSysMessage(LANG_USE_BOL);
- SetSentErrorMessage(true);
- return false;
- }
-
- std::string argstr = (char*)args;
-
- Player *chr = getSelectedPlayer();
- if (!chr)
- {
- chr=m_session->GetPlayer();
- }
-
- if (argstr == "on")
- {
- chr->SetTaxiCheater(true);
- PSendSysMessage(LANG_YOU_GIVE_TAXIS, chr->GetName());
-
- if(chr != m_session->GetPlayer())
- // to send localized data to target
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_TAXIS_ADDED), m_session->GetPlayer()->GetName());
- return true;
- }
-
- if (argstr == "off")
- {
- chr->SetTaxiCheater(false);
- PSendSysMessage(LANG_YOU_REMOVE_TAXIS, chr->GetName());
-
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_TAXIS_REMOVED), m_session->GetPlayer()->GetName());
-
- return true;
- }
-
- SendSysMessage(LANG_USE_BOL);
- SetSentErrorMessage(true);
- return false;
-}
-
-//Edit Player Aspeed
-bool ChatHandler::HandleModifyASpeedCommand(const char* args)
-{
- if (!*args)
- return false;
-
- float ASpeed = (float)atof((char*)args);
-
- if (ASpeed > 10 || ASpeed < 0.1)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(chr->isInFlight())
- {
- PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_ASPEED, ASpeed, chr->GetName());
-
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_ASPEED_CHANGED), m_session->GetPlayer()->GetName(), ASpeed);
-
- chr->SetSpeed(MOVE_WALK, ASpeed,true);
- chr->SetSpeed(MOVE_RUN, ASpeed,true);
- chr->SetSpeed(MOVE_SWIM, ASpeed,true);
- //chr->SetSpeed(MOVE_TURN, ASpeed,true);
- chr->SetSpeed(MOVE_FLY, ASpeed,true);
- return true;
-}
-
-//Edit Player Speed
-bool ChatHandler::HandleModifySpeedCommand(const char* args)
-{
- if (!*args)
- return false;
-
- float Speed = (float)atof((char*)args);
-
- if (Speed > 10 || Speed < 0.1)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(chr->isInFlight())
- {
- PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_SPEED, Speed, chr->GetName());
-
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_SPEED_CHANGED), m_session->GetPlayer()->GetName(), Speed);
-
- chr->SetSpeed(MOVE_RUN,Speed,true);
-
- return true;
-}
-
-//Edit Player Swim Speed
-bool ChatHandler::HandleModifySwimCommand(const char* args)
-{
- if (!*args)
- return false;
-
- float Swim = (float)atof((char*)args);
-
- if (Swim > 10.0f || Swim < 0.01f)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(chr->isInFlight())
- {
- PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, Swim, chr->GetName());
-
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_SWIM_SPEED_CHANGED), m_session->GetPlayer()->GetName(), Swim);
-
- chr->SetSpeed(MOVE_SWIM,Swim,true);
-
- return true;
-}
-
-//Edit Player Walk Speed
-bool ChatHandler::HandleModifyBWalkCommand(const char* args)
-{
- if (!*args)
- return false;
-
- float BSpeed = (float)atof((char*)args);
-
- if (BSpeed > 10.0f || BSpeed < 0.1f)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(chr->isInFlight())
- {
- PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, BSpeed, chr->GetName());
-
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_BACK_SPEED_CHANGED), m_session->GetPlayer()->GetName(), BSpeed);
-
- chr->SetSpeed(MOVE_WALKBACK,BSpeed,true);
-
- return true;
-}
-
-//Edit Player Fly
-bool ChatHandler::HandleModifyFlyCommand(const char* args)
-{
- if (!*args)
- return false;
-
- float FSpeed = (float)atof((char*)args);
-
- if (FSpeed > 10.0f || FSpeed < 0.1f)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, FSpeed, chr->GetName());
-
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_FLY_SPEED_CHANGED), m_session->GetPlayer()->GetName(), FSpeed);
-
- chr->SetSpeed(MOVE_FLY,FSpeed,true);
-
- return true;
-}
-
-//Edit Player Scale
-bool ChatHandler::HandleModifyScaleCommand(const char* args)
-{
- if (!*args)
- return false;
-
- float Scale = (float)atof((char*)args);
- if (Scale > 3.0f || Scale <= 0.0f)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_SIZE, Scale, chr->GetName());
-
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_SIZE_CHANGED), m_session->GetPlayer()->GetName(), Scale);
-
- chr->SetFloatValue(OBJECT_FIELD_SCALE_X, Scale);
-
- return true;
-}
-
-//Enable Player mount
-bool ChatHandler::HandleModifyMountCommand(const char* args)
-{
- if(!*args)
- return false;
-
- uint16 mId = 1147;
- float speed = (float)15;
- uint32 num = 0;
-
- num = atoi((char*)args);
- switch(num)
- {
- case 1:
- mId=14340;
- break;
- case 2:
- mId=4806;
- break;
- case 3:
- mId=6471;
- break;
- case 4:
- mId=12345;
- break;
- case 5:
- mId=6472;
- break;
- case 6:
- mId=6473;
- break;
- case 7:
- mId=10670;
- break;
- case 8:
- mId=10719;
- break;
- case 9:
- mId=10671;
- break;
- case 10:
- mId=10672;
- break;
- case 11:
- mId=10720;
- break;
- case 12:
- mId=14349;
- break;
- case 13:
- mId=11641;
- break;
- case 14:
- mId=12244;
- break;
- case 15:
- mId=12242;
- break;
- case 16:
- mId=14578;
- break;
- case 17:
- mId=14579;
- break;
- case 18:
- mId=14349;
- break;
- case 19:
- mId=12245;
- break;
- case 20:
- mId=14335;
- break;
- case 21:
- mId=207;
- break;
- case 22:
- mId=2328;
- break;
- case 23:
- mId=2327;
- break;
- case 24:
- mId=2326;
- break;
- case 25:
- mId=14573;
- break;
- case 26:
- mId=14574;
- break;
- case 27:
- mId=14575;
- break;
- case 28:
- mId=604;
- break;
- case 29:
- mId=1166;
- break;
- case 30:
- mId=2402;
- break;
- case 31:
- mId=2410;
- break;
- case 32:
- mId=2409;
- break;
- case 33:
- mId=2408;
- break;
- case 34:
- mId=2405;
- break;
- case 35:
- mId=14337;
- break;
- case 36:
- mId=6569;
- break;
- case 37:
- mId=10661;
- break;
- case 38:
- mId=10666;
- break;
- case 39:
- mId=9473;
- break;
- case 40:
- mId=9476;
- break;
- case 41:
- mId=9474;
- break;
- case 42:
- mId=14374;
- break;
- case 43:
- mId=14376;
- break;
- case 44:
- mId=14377;
- break;
- case 45:
- mId=2404;
- break;
- case 46:
- mId=2784;
- break;
- case 47:
- mId=2787;
- break;
- case 48:
- mId=2785;
- break;
- case 49:
- mId=2736;
- break;
- case 50:
- mId=2786;
- break;
- case 51:
- mId=14347;
- break;
- case 52:
- mId=14346;
- break;
- case 53:
- mId=14576;
- break;
- case 54:
- mId=9695;
- break;
- case 55:
- mId=9991;
- break;
- case 56:
- mId=6448;
- break;
- case 57:
- mId=6444;
- break;
- case 58:
- mId=6080;
- break;
- case 59:
- mId=6447;
- break;
- case 60:
- mId=4805;
- break;
- case 61:
- mId=9714;
- break;
- case 62:
- mId=6448;
- break;
- case 63:
- mId=6442;
- break;
- case 64:
- mId=14632;
- break;
- case 65:
- mId=14332;
- break;
- case 66:
- mId=14331;
- break;
- case 67:
- mId=8469;
- break;
- case 68:
- mId=2830;
- break;
- case 69:
- mId=2346;
- break;
- default:
- SendSysMessage(LANG_NO_MOUNT);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_YOU_GIVE_MOUNT, chr->GetName());
-
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_MOUNT_GIVED), m_session->GetPlayer()->GetName());
-
- chr->SetUInt32Value( UNIT_FIELD_FLAGS , 0x001000 );
- chr->Mount(mId);
-
- WorldPacket data( SMSG_FORCE_RUN_SPEED_CHANGE, (8+4+1+4) );
- data.append(chr->GetPackGUID());
- data << (uint32)0;
- data << (uint8)0; //new 2.1.0
- data << float(speed);
- chr->SendMessageToSet( &data, true );
-
- data.Initialize( SMSG_FORCE_SWIM_SPEED_CHANGE, (8+4+4) );
- data.append(chr->GetPackGUID());
- data << (uint32)0;
- data << float(speed);
- chr->SendMessageToSet( &data, true );
-
- return true;
-}
-
-//Edit Player money
-bool ChatHandler::HandleModifyMoneyCommand(const char* args)
-{
- if (!*args)
- return false;
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- int32 addmoney = atoi((char*)args);
-
- uint32 moneyuser = chr->GetMoney();
-
- if(addmoney < 0)
- {
- int32 newmoney = moneyuser + addmoney;
-
- sLog.outDetail(GetMangosString(LANG_CURRENT_MONEY), moneyuser, addmoney, newmoney);
- if(newmoney <= 0 )
- {
- PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, chr->GetName());
-
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_ALL_MONEY_GONE), m_session->GetPlayer()->GetName());
-
- chr->SetMoney(0);
- }
- else
- {
- PSendSysMessage(LANG_YOU_TAKE_MONEY, abs(addmoney), chr->GetName());
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_MONEY_TAKEN), m_session->GetPlayer()->GetName(), abs(addmoney));
- chr->SetMoney( newmoney );
- }
- }
- else
- {
- PSendSysMessage(LANG_YOU_GIVE_MONEY, addmoney, chr->GetName());
- if(chr != m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_MONEY_GIVEN), m_session->GetPlayer()->GetName(), addmoney);
- chr->ModifyMoney( addmoney );
- }
-
- sLog.outDetail(GetMangosString(LANG_NEW_MONEY), moneyuser, addmoney, chr->GetMoney() );
-
- return true;
-}
-
-//Edit Player field
-bool ChatHandler::HandleModifyBitCommand(const char* args)
-{
- if( !*args )
- return false;
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* pField = strtok((char*)args, " ");
- if (!pField)
- return false;
-
- char* pBit = strtok(NULL, " ");
- if (!pBit)
- return false;
-
- uint16 field = atoi(pField);
- uint32 bit = atoi(pBit);
-
- if (field < 1 || field >= PLAYER_END)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- if (bit < 1 || bit > 32)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- if ( chr->HasFlag( field, (1<<(bit-1)) ) )
- {
- chr->RemoveFlag( field, (1<<(bit-1)) );
- PSendSysMessage(LANG_REMOVE_BIT, bit, field);
- }
- else
- {
- chr->SetFlag( field, (1<<(bit-1)) );
- PSendSysMessage(LANG_SET_BIT, bit, field);
- }
-
- return true;
-}
-
-bool ChatHandler::HandleModifyHonorCommand (const char* args)
-{
- if (!*args)
- return false;
-
- Player *target = getSelectedPlayer();
- if(!target)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- int32 amount = (uint32)atoi(args);
-
- target->ModifyHonorPoints(amount);
-
- PSendSysMessage(LANG_COMMAND_MODIFY_HONOR, target->GetName(), target->GetHonorPoints());
-
- return true;
-}
-
-bool ChatHandler::HandleTeleCommand(const char * args)
-{
- if(!*args)
- return false;
-
- Player* _player = m_session->GetPlayer();
-
- // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
- GameTele const* tele = extractGameTeleFromLink((char*)args);
- if (!tele)
- {
- SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- MapEntry const * me = sMapStore.LookupEntry(tele->mapId);
- if(!me || me->IsBattleGroundOrArena())
- {
- SendSysMessage(LANG_CANNOT_TELE_TO_BG);
- SetSentErrorMessage(true);
- return false;
- }
-
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- _player->TeleportTo(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation);
- return true;
-}
-
-bool ChatHandler::HandleLookupAreaCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string namepart = args;
- std::wstring wnamepart;
-
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- uint32 counter = 0; // Counter for figure out that we found smth.
-
- // converting string that we try to find to lower case
- wstrToLower( wnamepart );
-
- // Search in AreaTable.dbc
- for (uint32 areaflag = 0; areaflag < sAreaStore.GetNumRows(); ++areaflag)
- {
- AreaTableEntry const *areaEntry = sAreaStore.LookupEntry(areaflag);
- if(areaEntry)
- {
- int loc = m_session->GetSessionDbcLocale();
- std::string name = areaEntry->area_name[loc];
- if(name.empty())
- continue;
-
- if(!Utf8FitTo(name, wnamepart))
- {
- loc = 0;
- for(; loc < MAX_LOCALE; ++loc)
- {
- if(loc==m_session->GetSessionDbcLocale())
- continue;
-
- name = areaEntry->area_name[loc];
- if(name.empty())
- continue;
-
- if (Utf8FitTo(name, wnamepart))
- break;
- }
- }
-
- if(loc < MAX_LOCALE)
- {
- // send area in "id - [name]" format
- std::ostringstream ss;
- ss << areaEntry->ID << " - |cffffffff|Harea:" << areaEntry->ID << "|h[" << name << " " << localeNames[loc]<< "]|h|r";
-
- SendSysMessage(ss.str().c_str());
-
- ++counter;
- }
- }
- }
- if (counter == 0) // if counter == 0 then we found nth
- SendSysMessage(LANG_COMMAND_NOAREAFOUND);
- return true;
-}
-
-//Find tele in game_tele order by name
-bool ChatHandler::HandleLookupTeleCommand(const char * args)
-{
- if(!*args)
- {
- SendSysMessage(LANG_COMMAND_TELE_PARAMETER);
- SetSentErrorMessage(true);
- return false;
- }
- char const* str = strtok((char*)args, " ");
- if(!str)
- return false;
-
- std::string namepart = str;
- std::wstring wnamepart;
-
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- // converting string that we try to find to lower case
- wstrToLower( wnamepart );
-
- GameTeleMap const & teleMap = objmgr.GetGameTeleMap();
-
- std::ostringstream reply;
- for(GameTeleMap::const_iterator itr = teleMap.begin(); itr != teleMap.end(); ++itr)
- {
- GameTele const* tele = &itr->second;
-
- if(tele->wnameLow.find(wnamepart) == std::wstring::npos)
- continue;
-
- reply << " |cffffffff|Htele:";
- reply << itr->first;
- reply << "|h[";
- reply << tele->name;
- reply << "]|h|r\n";
- }
-
- if(reply.str().empty())
- SendSysMessage(LANG_COMMAND_TELE_NOLOCATION);
- else
- PSendSysMessage(LANG_COMMAND_TELE_LOCATION,reply.str().c_str());
-
- return true;
-}
-
-//Enable\Dissable accept whispers (for GM)
-bool ChatHandler::HandleWhispersCommand(const char* args)
-{
- if(!*args)
- {
- PSendSysMessage(LANG_COMMAND_WHISPERACCEPTING, m_session->GetPlayer()->isAcceptWhispers() ? GetMangosString(LANG_ON) : GetMangosString(LANG_OFF));
- return true;
- }
-
- std::string argstr = (char*)args;
- // whisper on
- if (argstr == "on")
- {
- m_session->GetPlayer()->SetAcceptWhispers(true);
- SendSysMessage(LANG_COMMAND_WHISPERON);
- return true;
- }
-
- // whisper off
- if (argstr == "off")
- {
- m_session->GetPlayer()->SetAcceptWhispers(false);
- SendSysMessage(LANG_COMMAND_WHISPEROFF);
- return true;
- }
-
- SendSysMessage(LANG_USE_BOL);
- SetSentErrorMessage(true);
- return false;
-}
-
-//Play sound
-bool ChatHandler::HandlePlaySoundCommand(const char* args)
-{
- // USAGE: .debug playsound #soundid
- // #soundid - ID decimal number from SoundEntries.dbc (1st column)
- // this file have about 5000 sounds.
- // In this realization only caller can hear this sound.
- if( *args )
- {
- uint32 dwSoundId = atoi((char*)args);
-
- if( !sSoundEntriesStore.LookupEntry(dwSoundId) )
- {
- PSendSysMessage(LANG_SOUND_NOT_EXIST, dwSoundId);
- SetSentErrorMessage(true);
- return false;
- }
-
- WorldPacket data(SMSG_PLAY_OBJECT_SOUND,4+8);
- data << uint32(dwSoundId) << m_session->GetPlayer()->GetGUID();
- m_session->SendPacket(&data);
-
- PSendSysMessage(LANG_YOU_HEAR_SOUND, dwSoundId);
- return true;
- }
-
- return false;
-}
-
-//Save all players in the world
-bool ChatHandler::HandleSaveAllCommand(const char* /*args*/)
-{
- ObjectAccessor::Instance().SaveAllPlayers();
- SendSysMessage(LANG_PLAYERS_SAVED);
- return true;
-}
-
-//Send mail by command
-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, " ");
- 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);
- SetSentErrorMessage(true);
- return false;
- }
-
- 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();
- uint32 messagetype = MAIL_NORMAL;
- uint32 stationery = MAIL_STATIONERY_GM;
- uint32 itemTextId = 0;
- if (!text.empty())
- {
- itemTextId = objmgr.CreateItemText( text );
- }
-
- Player *receiver = objmgr.GetPlayer(receiver_guid);
-
- // 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;
-}
-
-// teleport player to given game_tele.entry
-bool ChatHandler::HandleNameTeleCommand(const char * args)
-{
- if(!*args)
- return false;
-
- char* pName = strtok((char*)args, " ");
-
- if(!pName)
- return false;
-
- std::string name = pName;
-
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* tail = strtok(NULL, "");
- if(!tail)
- return false;
-
- // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
- GameTele const* tele = extractGameTeleFromLink(tail);
- if(!tele)
- {
- SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- MapEntry const * me = sMapStore.LookupEntry(tele->mapId);
- if(!me || me->IsBattleGroundOrArena())
- {
- SendSysMessage(LANG_CANNOT_TELE_TO_BG);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = objmgr.GetPlayer(name.c_str());
- if (chr)
- {
-
- if(chr->IsBeingTeleported()==true)
- {
- PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_TELEPORTING_TO, chr->GetName(),"", tele->name.c_str());
-
- if (m_session->GetPlayer()->IsVisibleGloballyFor(chr))
- ChatHandler(chr).PSendSysMessage(LANG_TELEPORTED_TO_BY, m_session->GetPlayer()->GetName());
-
- // stop flight if need
- if(chr->isInFlight())
- {
- chr->GetMotionMaster()->MovementExpired();
- chr->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- chr->SaveRecallPosition();
-
- chr->TeleportTo(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation);
- }
- else if (uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str()))
- {
- PSendSysMessage(LANG_TELEPORTING_TO, name.c_str(), GetMangosString(LANG_OFFLINE), tele->name.c_str());
- Player::SavePositionInDB(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation,MapManager::Instance().GetZoneId(tele->mapId,tele->position_x,tele->position_y),guid);
- }
- else
- PSendSysMessage(LANG_NO_PLAYER, name.c_str());
-
- return true;
-}
-
-//Teleport group to given game_tele.entry
-bool ChatHandler::HandleGroupTeleCommand(const char * args)
-{
- if(!*args)
- return false;
-
- Player *player = getSelectedPlayer();
- if (!player)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
- GameTele const* tele = extractGameTeleFromLink((char*)args);
- if(!tele)
- {
- SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- MapEntry const * me = sMapStore.LookupEntry(tele->mapId);
- if(!me || me->IsBattleGroundOrArena())
- {
- SendSysMessage(LANG_CANNOT_TELE_TO_BG);
- SetSentErrorMessage(true);
- return false;
- }
- Group *grp = player->GetGroup();
- if(!grp)
- {
- PSendSysMessage(LANG_NOT_IN_GROUP,player->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- Player *pl = itr->getSource();
-
- if(!pl || !pl->GetSession() )
- continue;
-
- if(pl->IsBeingTeleported())
- {
- PSendSysMessage(LANG_IS_TELEPORTED, pl->GetName());
- continue;
- }
-
- PSendSysMessage(LANG_TELEPORTING_TO, pl->GetName(),"", tele->name.c_str());
-
- if (m_session->GetPlayer() != pl && m_session->GetPlayer()->IsVisibleGloballyFor(pl))
- ChatHandler(pl).PSendSysMessage(LANG_TELEPORTED_TO_BY, m_session->GetPlayer()->GetName());
-
- // stop flight if need
- if(pl->isInFlight())
- {
- pl->GetMotionMaster()->MovementExpired();
- pl->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- pl->SaveRecallPosition();
-
- pl->TeleportTo(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation);
- }
-
- return true;
-}
-
-//Summon group of player
-bool ChatHandler::HandleGroupgoCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string name = args;
-
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *player = objmgr.GetPlayer(name.c_str());
- if (!player)
- {
- PSendSysMessage(LANG_NO_PLAYER, args);
- SetSentErrorMessage(true);
- return false;
- }
-
- Group *grp = player->GetGroup();
-
- if(!grp)
- {
- PSendSysMessage(LANG_NOT_IN_GROUP,player->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- Map* gmMap = MapManager::Instance().GetMap(m_session->GetPlayer()->GetMapId(),m_session->GetPlayer());
- bool to_instance = gmMap->Instanceable();
-
- // we are in instance, and can summon only player in our group with us as lead
- if ( to_instance && (
- !m_session->GetPlayer()->GetGroup() || (grp->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ||
- (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ) )
- // the last check is a bit excessive, but let it be, just in case
- {
- SendSysMessage(LANG_CANNOT_SUMMON_TO_INST);
- SetSentErrorMessage(true);
- return false;
- }
-
- for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- Player *pl = itr->getSource();
-
- if(!pl || pl==m_session->GetPlayer() || !pl->GetSession() )
- continue;
-
- if(pl->IsBeingTeleported()==true)
- {
- PSendSysMessage(LANG_IS_TELEPORTED, pl->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- if (to_instance)
- {
- Map* plMap = MapManager::Instance().GetMap(pl->GetMapId(),pl);
-
- if ( plMap->Instanceable() && plMap->GetInstanceId() != gmMap->GetInstanceId() )
- {
- // cannot summon from instance to instance
- PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,pl->GetName());
- SetSentErrorMessage(true);
- return false;
- }
- }
-
- PSendSysMessage(LANG_SUMMONING, pl->GetName(),"");
-
- if (m_session->GetPlayer()->IsVisibleGloballyFor(pl))
- ChatHandler(pl).PSendSysMessage(LANG_SUMMONED_BY, m_session->GetPlayer()->GetName());
-
- // stop flight if need
- if(pl->isInFlight())
- {
- pl->GetMotionMaster()->MovementExpired();
- pl->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- pl->SaveRecallPosition();
-
- // before GM
- float x,y,z;
- m_session->GetPlayer()->GetClosePoint(x,y,z,pl->GetObjectSize());
- pl->TeleportTo(m_session->GetPlayer()->GetMapId(),x,y,z,pl->GetOrientation());
- }
-
- return true;
-}
-
-//teleport at coordinates
-bool ChatHandler::HandleGoXYCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Player* _player = m_session->GetPlayer();
-
- char* px = strtok((char*)args, " ");
- char* py = strtok(NULL, " ");
- char* pmapid = strtok(NULL, " ");
-
- if (!px || !py)
- return false;
-
- float x = (float)atof(px);
- float y = (float)atof(py);
- uint32 mapid;
- if (pmapid)
- mapid = (uint32)atoi(pmapid);
- else mapid = _player->GetMapId();
-
- if(!MapManager::IsValidMapCoord(mapid,x,y))
- {
- PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
- SetSentErrorMessage(true);
- return false;
- }
-
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- Map const *map = MapManager::Instance().GetBaseMap(mapid);
- float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
-
- _player->TeleportTo(mapid, x, y, z, _player->GetOrientation());
-
- return true;
-}
-
-//teleport at coordinates, including Z
-bool ChatHandler::HandleGoXYZCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Player* _player = m_session->GetPlayer();
-
- char* px = strtok((char*)args, " ");
- char* py = strtok(NULL, " ");
- char* pz = strtok(NULL, " ");
- char* pmapid = strtok(NULL, " ");
-
- if (!px || !py || !pz)
- return false;
-
- float x = (float)atof(px);
- float y = (float)atof(py);
- float z = (float)atof(pz);
- uint32 mapid;
- if (pmapid)
- mapid = (uint32)atoi(pmapid);
- else
- mapid = _player->GetMapId();
-
- if(!MapManager::IsValidMapCoord(mapid,x,y,z))
- {
- PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
- SetSentErrorMessage(true);
- return false;
- }
-
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- _player->TeleportTo(mapid, x, y, z, _player->GetOrientation());
-
- return true;
-}
-
-//teleport at coordinates
-bool ChatHandler::HandleGoZoneXYCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Player* _player = m_session->GetPlayer();
-
- char* px = strtok((char*)args, " ");
- char* py = strtok(NULL, " ");
- char* tail = strtok(NULL,"");
-
- char* cAreaId = extractKeyFromLink(tail,"Harea"); // string or [name] Shift-click form |color|Harea:area_id|h[name]|h|r
-
- if (!px || !py)
- return false;
-
- float x = (float)atof(px);
- float y = (float)atof(py);
- uint32 areaid = cAreaId ? (uint32)atoi(cAreaId) : _player->GetZoneId();
-
- AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaid);
-
- if( x<0 || x>100 || y<0 || y>100 || !areaEntry )
- {
- PSendSysMessage(LANG_INVALID_ZONE_COORD,x,y,areaid);
- SetSentErrorMessage(true);
- return false;
- }
-
- // update to parent zone if exist (client map show only zones without parents)
- AreaTableEntry const* zoneEntry = areaEntry->zone ? GetAreaEntryByAreaID(areaEntry->zone) : areaEntry;
-
- Map const *map = MapManager::Instance().GetBaseMap(zoneEntry->mapid);
-
- if(map->Instanceable())
- {
- PSendSysMessage(LANG_INVALID_ZONE_MAP,areaEntry->ID,areaEntry->area_name[m_session->GetSessionDbcLocale()],map->GetId(),map->GetMapName());
- SetSentErrorMessage(true);
- return false;
- }
-
- Zone2MapCoordinates(x,y,zoneEntry->ID);
-
- if(!MapManager::IsValidMapCoord(zoneEntry->mapid,x,y))
- {
- PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,zoneEntry->mapid);
- SetSentErrorMessage(true);
- return false;
- }
-
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
- _player->TeleportTo(zoneEntry->mapid, x, y, z, _player->GetOrientation());
-
- return true;
-}
-
-//teleport to grid
-bool ChatHandler::HandleGoGridCommand(const char* args)
-{
- if(!*args) return false;
- Player* _player = m_session->GetPlayer();
-
- char* px = strtok((char*)args, " ");
- char* py = strtok(NULL, " ");
- char* pmapid = strtok(NULL, " ");
-
- if (!px || !py)
- return false;
-
- float grid_x = (float)atof(px);
- float grid_y = (float)atof(py);
- uint32 mapid;
- if (pmapid)
- mapid = (uint32)atoi(pmapid);
- else mapid = _player->GetMapId();
-
- // center of grid
- float x = (grid_x-CENTER_GRID_ID+0.5f)*SIZE_OF_GRIDS;
- float y = (grid_y-CENTER_GRID_ID+0.5f)*SIZE_OF_GRIDS;
-
- if(!MapManager::IsValidMapCoord(mapid,x,y))
- {
- PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
- SetSentErrorMessage(true);
- return false;
- }
-
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- Map const *map = MapManager::Instance().GetBaseMap(mapid);
- float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
- _player->TeleportTo(mapid, x, y, z, _player->GetOrientation());
-
- return true;
-}
-
-bool ChatHandler::HandleDrunkCommand(const char* args)
-{
- if(!*args) return false;
-
- uint32 drunklevel = (uint32)atoi(args);
- if(drunklevel > 100)
- drunklevel = 100;
-
- uint16 drunkMod = drunklevel * 0xFFFF / 100;
-
- m_session->GetPlayer()->SetDrunkValue(drunkMod);
-
- 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Opcodes.h"
+#include "Chat.h"
+#include "Log.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "Language.h"
+#include "CellImpl.h"
+#include "InstanceSaveMgr.h"
+#include "Util.h"
+#ifdef _DEBUG_VMAPS
+#include "VMapFactory.h"
+#endif
+
+bool ChatHandler::HandleSayCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Creature* pCreature = getSelectedCreature();
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->Say(args, LANG_UNIVERSAL, 0);
+
+ return true;
+}
+
+bool ChatHandler::HandleYellCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Creature* pCreature = getSelectedCreature();
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->Yell(args, LANG_UNIVERSAL, 0);
+
+ return true;
+}
+
+//show text emote by creature in chat
+bool ChatHandler::HandleTextEmoteCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Creature* pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->TextEmote(args, 0);
+
+ return true;
+}
+
+// make npc whisper to player
+bool ChatHandler::HandleNpcWhisperCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* receiver_str = strtok((char*)args, " ");
+ char* text = strtok(NULL, "");
+
+ uint64 guid = m_session->GetPlayer()->GetSelection();
+ Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
+
+ if(!pCreature || !receiver_str || !text)
+ {
+ return false;
+ }
+
+ uint64 receiver_guid= atol(receiver_str);
+
+ pCreature->Whisper(text,receiver_guid);
+
+ return true;
+}
+
+// global announce
+bool ChatHandler::HandleAnnounceCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ sWorld.SendWorldText(LANG_SYSTEMMESSAGE,args);
+ return true;
+}
+
+//notification player at the screen
+bool ChatHandler::HandleNotifyCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string str = GetMangosString(LANG_GLOBAL_NOTIFY);
+ str += args;
+
+ WorldPacket data(SMSG_NOTIFICATION, (str.size()+1));
+ data << str;
+ sWorld.SendGlobalMessage(&data);
+
+ return true;
+}
+
+//Enable\Dissable GM Mode
+bool ChatHandler::HandleGMmodeCommand(const char* args)
+{
+ if(!*args)
+ {
+ 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;
+
+ if (argstr == "on")
+ {
+ m_session->GetPlayer()->SetGameMaster(true);
+ m_session->SendNotification(LANG_GM_ON);
+ #ifdef _DEBUG_VMAPS
+ VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
+ vMapManager->processCommand("stoplog");
+ #endif
+ return true;
+ }
+
+ if (argstr == "off")
+ {
+ m_session->GetPlayer()->SetGameMaster(false);
+ m_session->SendNotification(LANG_GM_OFF);
+ #ifdef _DEBUG_VMAPS
+ VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
+ vMapManager->processCommand("startlog");
+ #endif
+ return true;
+ }
+
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ 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)
+{
+ if (!*args)
+ {
+ PSendSysMessage(LANG_YOU_ARE, m_session->GetPlayer()->isGMVisible() ? GetMangosString(LANG_VISIBLE) : GetMangosString(LANG_INVISIBLE));
+ return true;
+ }
+
+ std::string argstr = (char*)args;
+
+ if (argstr == "on")
+ {
+ m_session->GetPlayer()->SetGMVisible(true);
+ m_session->SendNotification(LANG_INVISIBLE_VISIBLE);
+ return true;
+ }
+
+ if (argstr == "off")
+ {
+ m_session->SendNotification(LANG_INVISIBLE_INVISIBLE);
+ m_session->GetPlayer()->SetGMVisible(false);
+ return true;
+ }
+
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+}
+
+bool ChatHandler::HandleGPSCommand(const char* args)
+{
+ WorldObject *obj = NULL;
+ if (*args)
+ {
+ std::string name = args;
+ if(normalizePlayerName(name))
+ obj = objmgr.GetPlayer(name.c_str());
+
+ if(!obj)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ obj = getSelectedUnit();
+
+ if(!obj)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ CellPair cell_val = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
+ Cell cell(cell_val);
+
+ uint32 zone_id = obj->GetZoneId();
+ uint32 area_id = obj->GetAreaId();
+
+ MapEntry const* mapEntry = sMapStore.LookupEntry(obj->GetMapId());
+ AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zone_id);
+ AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(area_id);
+
+ float zone_x = obj->GetPositionX();
+ float zone_y = obj->GetPositionY();
+
+ Map2ZoneCoordinates(zone_x,zone_y,zone_id);
+
+ Map const *map = MapManager::Instance().GetMap(obj->GetMapId(), obj);
+ float ground_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), MAX_HEIGHT);
+ float floor_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ());
+
+ GridPair p = MaNGOS::ComputeGridPair(obj->GetPositionX(), obj->GetPositionY());
+
+ int gx=63-p.x_coord;
+ int gy=63-p.y_coord;
+
+ uint32 have_map = Map::ExistMap(obj->GetMapId(),gx,gy) ? 1 : 0;
+ uint32 have_vmap = Map::ExistVMap(obj->GetMapId(),gx,gy) ? 1 : 0;
+
+ PSendSysMessage(LANG_MAP_POSITION,
+ obj->GetMapId(), (mapEntry ? mapEntry->name[m_session->GetSessionDbcLocale()] : "<unknown>" ),
+ zone_id, (zoneEntry ? zoneEntry->area_name[m_session->GetSessionDbcLocale()] : "<unknown>" ),
+ area_id, (areaEntry ? areaEntry->area_name[m_session->GetSessionDbcLocale()] : "<unknown>" ),
+ obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(),
+ cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(),
+ zone_x, zone_y, ground_z, floor_z, have_map, have_vmap );
+
+ sLog.outDebug("Player %s GPS call for %s '%s' (%s: %u):",
+ m_session->GetPlayer()->GetName(),
+ (obj->GetTypeId() == TYPEID_PLAYER ? "player" : "creature"), obj->GetName(),
+ (obj->GetTypeId() == TYPEID_PLAYER ? "GUID" : "Entry"), (obj->GetTypeId() == TYPEID_PLAYER ? obj->GetGUIDLow(): obj->GetEntry()) );
+ sLog.outDebug(GetMangosString(LANG_MAP_POSITION),
+ obj->GetMapId(), (mapEntry ? mapEntry->name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ),
+ zone_id, (zoneEntry ? zoneEntry->area_name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ),
+ area_id, (areaEntry ? areaEntry->area_name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ),
+ obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(),
+ cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(),
+ zone_x, zone_y, ground_z, floor_z, have_map, have_vmap );
+
+ return true;
+}
+
+//Summon Player
+bool ChatHandler::HandleNamegoCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string name = args;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = objmgr.GetPlayer(name.c_str());
+ if (chr)
+ {
+ if(chr->IsBeingTeleported()==true)
+ {
+ PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Map* pMap = MapManager::Instance().GetMap(m_session->GetPlayer()->GetMapId(),m_session->GetPlayer());
+
+ if(pMap->IsBattleGroundOrArena())
+ {
+ // cannot summon to bg
+ SendSysMessage(LANG_CANNOT_SUMMON_TO_BG);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ else if(pMap->IsDungeon())
+ {
+ Map* cMap = MapManager::Instance().GetMap(chr->GetMapId(),chr);
+ if( cMap->Instanceable() && cMap->GetInstanceId() != pMap->GetInstanceId() )
+ {
+ // cannot summon from instance to instance
+ PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // we are in instance, and can summon only player in our group with us as lead
+ if ( !m_session->GetPlayer()->GetGroup() || !chr->GetGroup() ||
+ (chr->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ||
+ (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) )
+ // the last check is a bit excessive, but let it be, just in case
+ {
+ PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ PSendSysMessage(LANG_SUMMONING, chr->GetName(),"");
+
+ if (m_session->GetPlayer()->IsVisibleGloballyFor(chr))
+ ChatHandler(chr).PSendSysMessage(LANG_SUMMONED_BY, m_session->GetPlayer()->GetName());
+
+ // stop flight if need
+ if(chr->isInFlight())
+ {
+ chr->GetMotionMaster()->MovementExpired();
+ chr->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ chr->SaveRecallPosition();
+
+ // before GM
+ float x,y,z;
+ m_session->GetPlayer()->GetClosePoint(x,y,z,chr->GetObjectSize());
+ chr->TeleportTo(m_session->GetPlayer()->GetMapId(),x,y,z,chr->GetOrientation());
+ }
+ else if (uint64 guid = objmgr.GetPlayerGUIDByName(name))
+ {
+ PSendSysMessage(LANG_SUMMONING, name.c_str(),GetMangosString(LANG_OFFLINE));
+
+ // in point where GM stay
+ Player::SavePositionInDB(m_session->GetPlayer()->GetMapId(),
+ m_session->GetPlayer()->GetPositionX(),
+ m_session->GetPlayer()->GetPositionY(),
+ m_session->GetPlayer()->GetPositionZ(),
+ m_session->GetPlayer()->GetOrientation(),
+ m_session->GetPlayer()->GetZoneId(),
+ guid);
+ }
+ else
+ {
+ PSendSysMessage(LANG_NO_PLAYER, args);
+ SetSentErrorMessage(true);
+ }
+
+ return true;
+}
+
+//Teleport to Player
+bool ChatHandler::HandleGonameCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ std::string name = args;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = objmgr.GetPlayer(name.c_str());
+ if (chr)
+ {
+ Map* cMap = MapManager::Instance().GetMap(chr->GetMapId(),chr);
+ if(cMap->IsBattleGroundOrArena())
+ {
+ // only allow if gm mode is on
+ if (!_player->isGameMaster())
+ {
+ SendSysMessage(LANG_CANNOT_GO_TO_BG_GM);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ // if already in a bg, don't let port to other
+ else if (_player->GetBattleGroundId())
+ {
+ SendSysMessage(LANG_CANNOT_GO_TO_BG_FROM_BG);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ // all's well, set bg id
+ // when porting out from the bg, it will be reset to 0
+ _player->SetBattleGroundId(chr->GetBattleGroundId());
+ }
+ else if(cMap->IsDungeon())
+ {
+ Map* pMap = MapManager::Instance().GetMap(_player->GetMapId(),_player);
+
+ // we have to go to instance, and can go to player only if:
+ // 1) we are in his group (either as leader or as member)
+ // 2) we are not bound to any group and have GM mode on
+ if (_player->GetGroup())
+ {
+ // we are in group, we can go only if we are in the player group
+ if (_player->GetGroup() != chr->GetGroup())
+ {
+ PSendSysMessage(LANG_CANNOT_GO_TO_INST_PARTY,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ // we are not in group, let's verify our GM mode
+ if (!_player->isGameMaster())
+ {
+ PSendSysMessage(LANG_CANNOT_GO_TO_INST_GM,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ // if the player or the player's group is bound to another instance
+ // the player will not be bound to another one
+ InstancePlayerBind *pBind = _player->GetBoundInstance(chr->GetMapId(), chr->GetDifficulty());
+ if(!pBind)
+ {
+ Group *group = _player->GetGroup();
+ InstanceGroupBind *gBind = group ? group->GetBoundInstance(chr->GetMapId(), chr->GetDifficulty()) : NULL;
+ if(!gBind)
+ {
+ // if no bind exists, create a solo bind
+ InstanceSave *save = sInstanceSaveManager.GetInstanceSave(chr->GetInstanceId());
+ if(save) _player->BindToInstance(save, !save->CanReset());
+ }
+ }
+
+ _player->SetDifficulty(chr->GetDifficulty());
+ }
+
+ PSendSysMessage(LANG_APPEARING_AT, chr->GetName());
+
+ if (_player->IsVisibleGloballyFor(chr))
+ ChatHandler(chr).PSendSysMessage(LANG_APPEARING_TO, _player->GetName());
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ // to point to see at target with same orientation
+ float x,y,z;
+ chr->GetContactPoint(m_session->GetPlayer(),x,y,z);
+
+ _player->TeleportTo(chr->GetMapId(), x, y, z, _player->GetAngle( chr ), TELE_TO_GM_MODE);
+
+ return true;
+ }
+
+ if (uint64 guid = objmgr.GetPlayerGUIDByName(name))
+ {
+ PSendSysMessage(LANG_APPEARING_AT, name.c_str());
+
+ // to point where player stay (if loaded)
+ float x,y,z,o;
+ uint32 map;
+ bool in_flight;
+ if(Player::LoadPositionFromDB(map,x,y,z,o,in_flight,guid))
+ {
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(map, x, y, z,_player->GetOrientation());
+ return true;
+ }
+ }
+
+ PSendSysMessage(LANG_NO_PLAYER, args);
+
+ SetSentErrorMessage(true);
+ return false;
+}
+
+// Teleport player to last position
+bool ChatHandler::HandleRecallCommand(const char* args)
+{
+ Player* chr = NULL;
+
+ if(!*args)
+ {
+ chr = getSelectedPlayer();
+ if(!chr)
+ chr = m_session->GetPlayer();
+ }
+ else
+ {
+ std::string name = args;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ chr = objmgr.GetPlayer(name.c_str());
+
+ if(!chr)
+ {
+ PSendSysMessage(LANG_NO_PLAYER, args);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ if(chr->IsBeingTeleported())
+ {
+ PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(chr->isInFlight())
+ {
+ chr->GetMotionMaster()->MovementExpired();
+ chr->m_taxi.ClearTaxiDestinations();
+ }
+
+ chr->TeleportTo(chr->m_recallMap, chr->m_recallX, chr->m_recallY, chr->m_recallZ, chr->m_recallO);
+ return true;
+}
+
+//Edit Player KnownTitles
+bool ChatHandler::HandleModifyKnownTitlesCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ uint64 titles = 0;
+
+ sscanf((char*)args, I64FMTD, &titles);
+
+ Player *chr = getSelectedPlayer();
+ if (!chr)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 titles2 = titles;
+
+ for(int i=1; i < sCharTitlesStore.GetNumRows(); ++i)
+ if(CharTitlesEntry const* tEntry = sCharTitlesStore.LookupEntry(i))
+ titles2 &= ~(uint64(1) << tEntry->bit_index);
+
+ titles &= ~titles2; // remove not existed titles
+
+ chr->SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES, titles);
+ SendSysMessage(LANG_DONE);
+
+ return true;
+}
+
+//Edit Player HP
+bool ChatHandler::HandleModifyHPCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // char* pHp = strtok((char*)args, " ");
+ // if (!pHp)
+ // return false;
+
+ // char* pHpMax = strtok(NULL, " ");
+ // if (!pHpMax)
+ // return false;
+
+ // int32 hpm = atoi(pHpMax);
+ // int32 hp = atoi(pHp);
+
+ int32 hp = atoi((char*)args);
+ int32 hpm = atoi((char*)args);
+
+ if (hp <= 0 || hpm <= 0 || hpm < hp)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_HP, chr->GetName(), hp, hpm);
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_HP_CHANGED, m_session->GetPlayer()->GetName(), hp, hpm);
+
+ chr->SetMaxHealth( hpm );
+ chr->SetHealth( hp );
+
+ return true;
+}
+
+//Edit Player Mana
+bool ChatHandler::HandleModifyManaCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // char* pmana = strtok((char*)args, " ");
+ // if (!pmana)
+ // return false;
+
+ // char* pmanaMax = strtok(NULL, " ");
+ // if (!pmanaMax)
+ // return false;
+
+ // int32 manam = atoi(pmanaMax);
+ // int32 mana = atoi(pmana);
+ int32 mana = atoi((char*)args);
+ int32 manam = atoi((char*)args);
+
+ if (mana <= 0 || manam <= 0 || manam < mana)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_MANA, chr->GetName(), mana, manam);
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_MANA_CHANGED, m_session->GetPlayer()->GetName(), mana, manam);
+
+ chr->SetMaxPower(POWER_MANA,manam );
+ chr->SetPower(POWER_MANA, mana );
+
+ return true;
+}
+
+//Edit Player Energy
+bool ChatHandler::HandleModifyEnergyCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // char* pmana = strtok((char*)args, " ");
+ // if (!pmana)
+ // return false;
+
+ // char* pmanaMax = strtok(NULL, " ");
+ // if (!pmanaMax)
+ // return false;
+
+ // int32 manam = atoi(pmanaMax);
+ // int32 mana = atoi(pmana);
+
+ int32 energy = atoi((char*)args)*10;
+ int32 energym = atoi((char*)args)*10;
+
+ if (energy <= 0 || energym <= 0 || energym < energy)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (!chr)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_ENERGY, chr->GetName(), energy/10, energym/10);
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_ENERGY_CHANGED, m_session->GetPlayer()->GetName(), energy/10, energym/10);
+
+ chr->SetMaxPower(POWER_ENERGY,energym );
+ chr->SetPower(POWER_ENERGY, energy );
+
+ sLog.outDetail(GetMangosString(LANG_CURRENT_ENERGY),chr->GetMaxPower(POWER_ENERGY));
+
+ return true;
+}
+
+//Edit Player Rage
+bool ChatHandler::HandleModifyRageCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // char* pmana = strtok((char*)args, " ");
+ // if (!pmana)
+ // return false;
+
+ // char* pmanaMax = strtok(NULL, " ");
+ // if (!pmanaMax)
+ // return false;
+
+ // int32 manam = atoi(pmanaMax);
+ // int32 mana = atoi(pmana);
+
+ int32 rage = atoi((char*)args)*10;
+ int32 ragem = atoi((char*)args)*10;
+
+ if (rage <= 0 || ragem <= 0 || ragem < rage)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_RAGE, chr->GetName(), rage/10, ragem/10);
+ // Special case: I use GetMangosString here to get local of destination char ;)
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_RAGE_CHANGED), m_session->GetPlayer()->GetName(), rage/10, ragem/10);
+
+ chr->SetMaxPower(POWER_RAGE,ragem );
+ chr->SetPower(POWER_RAGE, rage );
+
+ return true;
+}
+
+//Edit Player Faction
+bool ChatHandler::HandleModifyFactionCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* pfactionid = extractKeyFromLink((char*)args,"Hfaction");
+
+ Creature* chr = getSelectedCreature();
+ if(!chr)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!pfactionid)
+ {
+ if(chr)
+ {
+ uint32 factionid = chr->getFaction();
+ uint32 flag = chr->GetUInt32Value(UNIT_FIELD_FLAGS);
+ uint32 npcflag = chr->GetUInt32Value(UNIT_NPC_FLAGS);
+ uint32 dyflag = chr->GetUInt32Value(UNIT_DYNAMIC_FLAGS);
+ PSendSysMessage(LANG_CURRENT_FACTION,chr->GetGUIDLow(),factionid,flag,npcflag,dyflag);
+ }
+ return true;
+ }
+
+ if( !chr )
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 factionid = atoi(pfactionid);
+ uint32 flag;
+
+ char *pflag = strtok(NULL, " ");
+ if (!pflag)
+ flag = chr->GetUInt32Value(UNIT_FIELD_FLAGS);
+ else
+ flag = atoi(pflag);
+
+ char* pnpcflag = strtok(NULL, " ");
+
+ uint32 npcflag;
+ if(!pnpcflag)
+ npcflag = chr->GetUInt32Value(UNIT_NPC_FLAGS);
+ else
+ npcflag = atoi(pnpcflag);
+
+ char* pdyflag = strtok(NULL, " ");
+
+ uint32 dyflag;
+ if(!pdyflag)
+ dyflag = chr->GetUInt32Value(UNIT_DYNAMIC_FLAGS);
+ else
+ dyflag = atoi(pdyflag);
+
+ if(!sFactionTemplateStore.LookupEntry(factionid))
+ {
+ PSendSysMessage(LANG_WRONG_FACTION, factionid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_FACTION, chr->GetGUIDLow(),factionid,flag,npcflag,dyflag);
+
+ //sprintf((char*)buf,"%s changed your Faction to %i.", m_session->GetPlayer()->GetName(), factionid);
+ //FillSystemMessageData(&data, m_session, buf);
+
+ //chr->GetSession()->SendPacket(&data);
+
+ chr->setFaction(factionid);
+ chr->SetUInt32Value(UNIT_FIELD_FLAGS,flag);
+ chr->SetUInt32Value(UNIT_NPC_FLAGS,npcflag);
+ chr->SetUInt32Value(UNIT_DYNAMIC_FLAGS,dyflag);
+
+ return true;
+}
+
+//Edit Player Spell
+bool ChatHandler::HandleModifySpellCommand(const char* args)
+{
+ if(!*args) return false;
+ char* pspellflatid = strtok((char*)args, " ");
+ if (!pspellflatid)
+ return false;
+
+ char* pop = strtok(NULL, " ");
+ if (!pop)
+ return false;
+
+ char* pval = strtok(NULL, " ");
+ if (!pval)
+ return false;
+
+ uint16 mark;
+
+ char* pmark = strtok(NULL, " ");
+
+ uint8 spellflatid = atoi(pspellflatid);
+ uint8 op = atoi(pop);
+ uint16 val = atoi(pval);
+ if(!pmark)
+ mark = 65535;
+ else
+ mark = atoi(pmark);
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_SPELLFLATID, spellflatid, val, mark, chr->GetName());
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPELLFLATID_CHANGED, m_session->GetPlayer()->GetName(), spellflatid, val, mark);
+
+ WorldPacket data(SMSG_SET_FLAT_SPELL_MODIFIER, (1+1+2+2));
+ data << uint8(spellflatid);
+ data << uint8(op);
+ data << uint16(val);
+ data << uint16(mark);
+ chr->GetSession()->SendPacket(&data);
+
+ return true;
+}
+
+//Edit Player TP
+bool ChatHandler::HandleModifyTalentCommand (const char* args)
+{
+ if (!*args)
+ return false;
+
+ int tp = atoi((char*)args);
+ if (tp>0)
+ {
+ Player* player = getSelectedPlayer();
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ player->SetFreeTalentPoints(tp);
+ return true;
+ }
+ return false;
+}
+
+//Enable On\OFF all taxi paths
+bool ChatHandler::HandleTaxiCheatCommand(const char* args)
+{
+ if (!*args)
+ {
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ std::string argstr = (char*)args;
+
+ Player *chr = getSelectedPlayer();
+ if (!chr)
+ {
+ chr=m_session->GetPlayer();
+ }
+
+ if (argstr == "on")
+ {
+ chr->SetTaxiCheater(true);
+ PSendSysMessage(LANG_YOU_GIVE_TAXIS, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ // to send localized data to target
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_TAXIS_ADDED), m_session->GetPlayer()->GetName());
+ return true;
+ }
+
+ if (argstr == "off")
+ {
+ chr->SetTaxiCheater(false);
+ PSendSysMessage(LANG_YOU_REMOVE_TAXIS, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_TAXIS_REMOVED), m_session->GetPlayer()->GetName());
+
+ return true;
+ }
+
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+}
+
+//Edit Player Aspeed
+bool ChatHandler::HandleModifyASpeedCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float ASpeed = (float)atof((char*)args);
+
+ if (ASpeed > 10 || ASpeed < 0.1)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(chr->isInFlight())
+ {
+ PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_ASPEED, ASpeed, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_ASPEED_CHANGED), m_session->GetPlayer()->GetName(), ASpeed);
+
+ chr->SetSpeed(MOVE_WALK, ASpeed,true);
+ chr->SetSpeed(MOVE_RUN, ASpeed,true);
+ chr->SetSpeed(MOVE_SWIM, ASpeed,true);
+ //chr->SetSpeed(MOVE_TURN, ASpeed,true);
+ chr->SetSpeed(MOVE_FLY, ASpeed,true);
+ return true;
+}
+
+//Edit Player Speed
+bool ChatHandler::HandleModifySpeedCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float Speed = (float)atof((char*)args);
+
+ if (Speed > 10 || Speed < 0.1)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(chr->isInFlight())
+ {
+ PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_SPEED, Speed, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_SPEED_CHANGED), m_session->GetPlayer()->GetName(), Speed);
+
+ chr->SetSpeed(MOVE_RUN,Speed,true);
+
+ return true;
+}
+
+//Edit Player Swim Speed
+bool ChatHandler::HandleModifySwimCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float Swim = (float)atof((char*)args);
+
+ if (Swim > 10.0f || Swim < 0.01f)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(chr->isInFlight())
+ {
+ PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, Swim, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_SWIM_SPEED_CHANGED), m_session->GetPlayer()->GetName(), Swim);
+
+ chr->SetSpeed(MOVE_SWIM,Swim,true);
+
+ return true;
+}
+
+//Edit Player Walk Speed
+bool ChatHandler::HandleModifyBWalkCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float BSpeed = (float)atof((char*)args);
+
+ if (BSpeed > 10.0f || BSpeed < 0.1f)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(chr->isInFlight())
+ {
+ PSendSysMessage(LANG_CHAR_IN_FLIGHT,chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, BSpeed, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_BACK_SPEED_CHANGED), m_session->GetPlayer()->GetName(), BSpeed);
+
+ chr->SetSpeed(MOVE_WALKBACK,BSpeed,true);
+
+ return true;
+}
+
+//Edit Player Fly
+bool ChatHandler::HandleModifyFlyCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float FSpeed = (float)atof((char*)args);
+
+ if (FSpeed > 10.0f || FSpeed < 0.1f)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, FSpeed, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_FLY_SPEED_CHANGED), m_session->GetPlayer()->GetName(), FSpeed);
+
+ chr->SetSpeed(MOVE_FLY,FSpeed,true);
+
+ return true;
+}
+
+//Edit Player Scale
+bool ChatHandler::HandleModifyScaleCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ float Scale = (float)atof((char*)args);
+ if (Scale > 3.0f || Scale <= 0.0f)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_SIZE, Scale, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_SIZE_CHANGED), m_session->GetPlayer()->GetName(), Scale);
+
+ chr->SetFloatValue(OBJECT_FIELD_SCALE_X, Scale);
+
+ return true;
+}
+
+//Enable Player mount
+bool ChatHandler::HandleModifyMountCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ uint16 mId = 1147;
+ float speed = (float)15;
+ uint32 num = 0;
+
+ num = atoi((char*)args);
+ switch(num)
+ {
+ case 1:
+ mId=14340;
+ break;
+ case 2:
+ mId=4806;
+ break;
+ case 3:
+ mId=6471;
+ break;
+ case 4:
+ mId=12345;
+ break;
+ case 5:
+ mId=6472;
+ break;
+ case 6:
+ mId=6473;
+ break;
+ case 7:
+ mId=10670;
+ break;
+ case 8:
+ mId=10719;
+ break;
+ case 9:
+ mId=10671;
+ break;
+ case 10:
+ mId=10672;
+ break;
+ case 11:
+ mId=10720;
+ break;
+ case 12:
+ mId=14349;
+ break;
+ case 13:
+ mId=11641;
+ break;
+ case 14:
+ mId=12244;
+ break;
+ case 15:
+ mId=12242;
+ break;
+ case 16:
+ mId=14578;
+ break;
+ case 17:
+ mId=14579;
+ break;
+ case 18:
+ mId=14349;
+ break;
+ case 19:
+ mId=12245;
+ break;
+ case 20:
+ mId=14335;
+ break;
+ case 21:
+ mId=207;
+ break;
+ case 22:
+ mId=2328;
+ break;
+ case 23:
+ mId=2327;
+ break;
+ case 24:
+ mId=2326;
+ break;
+ case 25:
+ mId=14573;
+ break;
+ case 26:
+ mId=14574;
+ break;
+ case 27:
+ mId=14575;
+ break;
+ case 28:
+ mId=604;
+ break;
+ case 29:
+ mId=1166;
+ break;
+ case 30:
+ mId=2402;
+ break;
+ case 31:
+ mId=2410;
+ break;
+ case 32:
+ mId=2409;
+ break;
+ case 33:
+ mId=2408;
+ break;
+ case 34:
+ mId=2405;
+ break;
+ case 35:
+ mId=14337;
+ break;
+ case 36:
+ mId=6569;
+ break;
+ case 37:
+ mId=10661;
+ break;
+ case 38:
+ mId=10666;
+ break;
+ case 39:
+ mId=9473;
+ break;
+ case 40:
+ mId=9476;
+ break;
+ case 41:
+ mId=9474;
+ break;
+ case 42:
+ mId=14374;
+ break;
+ case 43:
+ mId=14376;
+ break;
+ case 44:
+ mId=14377;
+ break;
+ case 45:
+ mId=2404;
+ break;
+ case 46:
+ mId=2784;
+ break;
+ case 47:
+ mId=2787;
+ break;
+ case 48:
+ mId=2785;
+ break;
+ case 49:
+ mId=2736;
+ break;
+ case 50:
+ mId=2786;
+ break;
+ case 51:
+ mId=14347;
+ break;
+ case 52:
+ mId=14346;
+ break;
+ case 53:
+ mId=14576;
+ break;
+ case 54:
+ mId=9695;
+ break;
+ case 55:
+ mId=9991;
+ break;
+ case 56:
+ mId=6448;
+ break;
+ case 57:
+ mId=6444;
+ break;
+ case 58:
+ mId=6080;
+ break;
+ case 59:
+ mId=6447;
+ break;
+ case 60:
+ mId=4805;
+ break;
+ case 61:
+ mId=9714;
+ break;
+ case 62:
+ mId=6448;
+ break;
+ case 63:
+ mId=6442;
+ break;
+ case 64:
+ mId=14632;
+ break;
+ case 65:
+ mId=14332;
+ break;
+ case 66:
+ mId=14331;
+ break;
+ case 67:
+ mId=8469;
+ break;
+ case 68:
+ mId=2830;
+ break;
+ case 69:
+ mId=2346;
+ break;
+ default:
+ SendSysMessage(LANG_NO_MOUNT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_YOU_GIVE_MOUNT, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_MOUNT_GIVED), m_session->GetPlayer()->GetName());
+
+ chr->SetUInt32Value( UNIT_FIELD_FLAGS , 0x001000 );
+ chr->Mount(mId);
+
+ WorldPacket data( SMSG_FORCE_RUN_SPEED_CHANGE, (8+4+1+4) );
+ data.append(chr->GetPackGUID());
+ data << (uint32)0;
+ data << (uint8)0; //new 2.1.0
+ data << float(speed);
+ chr->SendMessageToSet( &data, true );
+
+ data.Initialize( SMSG_FORCE_SWIM_SPEED_CHANGE, (8+4+4) );
+ data.append(chr->GetPackGUID());
+ data << (uint32)0;
+ data << float(speed);
+ chr->SendMessageToSet( &data, true );
+
+ return true;
+}
+
+//Edit Player money
+bool ChatHandler::HandleModifyMoneyCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 addmoney = atoi((char*)args);
+
+ uint32 moneyuser = chr->GetMoney();
+
+ if(addmoney < 0)
+ {
+ int32 newmoney = moneyuser + addmoney;
+
+ sLog.outDetail(GetMangosString(LANG_CURRENT_MONEY), moneyuser, addmoney, newmoney);
+ if(newmoney <= 0 )
+ {
+ PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, chr->GetName());
+
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_ALL_MONEY_GONE), m_session->GetPlayer()->GetName());
+
+ chr->SetMoney(0);
+ }
+ else
+ {
+ PSendSysMessage(LANG_YOU_TAKE_MONEY, abs(addmoney), chr->GetName());
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_MONEY_TAKEN), m_session->GetPlayer()->GetName(), abs(addmoney));
+ chr->SetMoney( newmoney );
+ }
+ }
+ else
+ {
+ PSendSysMessage(LANG_YOU_GIVE_MONEY, addmoney, chr->GetName());
+ if(chr != m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(ChatHandler(chr).GetMangosString(LANG_YOURS_MONEY_GIVEN), m_session->GetPlayer()->GetName(), addmoney);
+ chr->ModifyMoney( addmoney );
+ }
+
+ sLog.outDetail(GetMangosString(LANG_NEW_MONEY), moneyuser, addmoney, chr->GetMoney() );
+
+ return true;
+}
+
+//Edit Player field
+bool ChatHandler::HandleModifyBitCommand(const char* args)
+{
+ if( !*args )
+ return false;
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* pField = strtok((char*)args, " ");
+ if (!pField)
+ return false;
+
+ char* pBit = strtok(NULL, " ");
+ if (!pBit)
+ return false;
+
+ uint16 field = atoi(pField);
+ uint32 bit = atoi(pBit);
+
+ if (field < 1 || field >= PLAYER_END)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (bit < 1 || bit > 32)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if ( chr->HasFlag( field, (1<<(bit-1)) ) )
+ {
+ chr->RemoveFlag( field, (1<<(bit-1)) );
+ PSendSysMessage(LANG_REMOVE_BIT, bit, field);
+ }
+ else
+ {
+ chr->SetFlag( field, (1<<(bit-1)) );
+ PSendSysMessage(LANG_SET_BIT, bit, field);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleModifyHonorCommand (const char* args)
+{
+ if (!*args)
+ return false;
+
+ Player *target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 amount = (uint32)atoi(args);
+
+ target->ModifyHonorPoints(amount);
+
+ PSendSysMessage(LANG_COMMAND_MODIFY_HONOR, target->GetName(), target->GetHonorPoints());
+
+ return true;
+}
+
+bool ChatHandler::HandleTeleCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
+ GameTele const* tele = extractGameTeleFromLink((char*)args);
+ if (!tele)
+ {
+ SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ MapEntry const * me = sMapStore.LookupEntry(tele->mapId);
+ if(!me || me->IsBattleGroundOrArena())
+ {
+ SendSysMessage(LANG_CANNOT_TELE_TO_BG);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation);
+ return true;
+}
+
+bool ChatHandler::HandleLookupAreaCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ uint32 counter = 0; // Counter for figure out that we found smth.
+
+ // converting string that we try to find to lower case
+ wstrToLower( wnamepart );
+
+ // Search in AreaTable.dbc
+ for (uint32 areaflag = 0; areaflag < sAreaStore.GetNumRows(); ++areaflag)
+ {
+ AreaTableEntry const *areaEntry = sAreaStore.LookupEntry(areaflag);
+ if(areaEntry)
+ {
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = areaEntry->area_name[loc];
+ if(name.empty())
+ continue;
+
+ if(!Utf8FitTo(name, wnamepart))
+ {
+ loc = 0;
+ for(; loc < MAX_LOCALE; ++loc)
+ {
+ if(loc==m_session->GetSessionDbcLocale())
+ continue;
+
+ name = areaEntry->area_name[loc];
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ break;
+ }
+ }
+
+ if(loc < MAX_LOCALE)
+ {
+ // send area in "id - [name]" format
+ std::ostringstream ss;
+ ss << areaEntry->ID << " - |cffffffff|Harea:" << areaEntry->ID << "|h[" << name << " " << localeNames[loc]<< "]|h|r";
+
+ SendSysMessage(ss.str().c_str());
+
+ ++counter;
+ }
+ }
+ }
+ if (counter == 0) // if counter == 0 then we found nth
+ SendSysMessage(LANG_COMMAND_NOAREAFOUND);
+ return true;
+}
+
+//Find tele in game_tele order by name
+bool ChatHandler::HandleLookupTeleCommand(const char * args)
+{
+ if(!*args)
+ {
+ SendSysMessage(LANG_COMMAND_TELE_PARAMETER);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ char const* str = strtok((char*)args, " ");
+ if(!str)
+ return false;
+
+ std::string namepart = str;
+ std::wstring wnamepart;
+
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ // converting string that we try to find to lower case
+ wstrToLower( wnamepart );
+
+ GameTeleMap const & teleMap = objmgr.GetGameTeleMap();
+
+ std::ostringstream reply;
+ for(GameTeleMap::const_iterator itr = teleMap.begin(); itr != teleMap.end(); ++itr)
+ {
+ GameTele const* tele = &itr->second;
+
+ if(tele->wnameLow.find(wnamepart) == std::wstring::npos)
+ continue;
+
+ reply << " |cffffffff|Htele:";
+ reply << itr->first;
+ reply << "|h[";
+ reply << tele->name;
+ reply << "]|h|r\n";
+ }
+
+ if(reply.str().empty())
+ SendSysMessage(LANG_COMMAND_TELE_NOLOCATION);
+ else
+ PSendSysMessage(LANG_COMMAND_TELE_LOCATION,reply.str().c_str());
+
+ return true;
+}
+
+//Enable\Dissable accept whispers (for GM)
+bool ChatHandler::HandleWhispersCommand(const char* args)
+{
+ if(!*args)
+ {
+ PSendSysMessage(LANG_COMMAND_WHISPERACCEPTING, m_session->GetPlayer()->isAcceptWhispers() ? GetMangosString(LANG_ON) : GetMangosString(LANG_OFF));
+ return true;
+ }
+
+ std::string argstr = (char*)args;
+ // whisper on
+ if (argstr == "on")
+ {
+ m_session->GetPlayer()->SetAcceptWhispers(true);
+ SendSysMessage(LANG_COMMAND_WHISPERON);
+ return true;
+ }
+
+ // whisper off
+ if (argstr == "off")
+ {
+ m_session->GetPlayer()->SetAcceptWhispers(false);
+ SendSysMessage(LANG_COMMAND_WHISPEROFF);
+ return true;
+ }
+
+ SendSysMessage(LANG_USE_BOL);
+ SetSentErrorMessage(true);
+ return false;
+}
+
+//Play sound
+bool ChatHandler::HandlePlaySoundCommand(const char* args)
+{
+ // USAGE: .debug playsound #soundid
+ // #soundid - ID decimal number from SoundEntries.dbc (1st column)
+ // this file have about 5000 sounds.
+ // In this realization only caller can hear this sound.
+ if( *args )
+ {
+ uint32 dwSoundId = atoi((char*)args);
+
+ if( !sSoundEntriesStore.LookupEntry(dwSoundId) )
+ {
+ PSendSysMessage(LANG_SOUND_NOT_EXIST, dwSoundId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ WorldPacket data(SMSG_PLAY_OBJECT_SOUND,4+8);
+ data << uint32(dwSoundId) << m_session->GetPlayer()->GetGUID();
+ m_session->SendPacket(&data);
+
+ PSendSysMessage(LANG_YOU_HEAR_SOUND, dwSoundId);
+ return true;
+ }
+
+ return false;
+}
+
+//Save all players in the world
+bool ChatHandler::HandleSaveAllCommand(const char* /*args*/)
+{
+ ObjectAccessor::Instance().SaveAllPlayers();
+ SendSysMessage(LANG_PLAYERS_SAVED);
+ return true;
+}
+
+//Send mail by command
+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, " ");
+ 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);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ 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();
+ uint32 messagetype = MAIL_NORMAL;
+ uint32 stationery = MAIL_STATIONERY_GM;
+ uint32 itemTextId = 0;
+ if (!text.empty())
+ {
+ itemTextId = objmgr.CreateItemText( text );
+ }
+
+ Player *receiver = objmgr.GetPlayer(receiver_guid);
+
+ // 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;
+}
+
+// teleport player to given game_tele.entry
+bool ChatHandler::HandleNameTeleCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ char* pName = strtok((char*)args, " ");
+
+ if(!pName)
+ return false;
+
+ std::string name = pName;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* tail = strtok(NULL, "");
+ if(!tail)
+ return false;
+
+ // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
+ GameTele const* tele = extractGameTeleFromLink(tail);
+ if(!tele)
+ {
+ SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ MapEntry const * me = sMapStore.LookupEntry(tele->mapId);
+ if(!me || me->IsBattleGroundOrArena())
+ {
+ SendSysMessage(LANG_CANNOT_TELE_TO_BG);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = objmgr.GetPlayer(name.c_str());
+ if (chr)
+ {
+
+ if(chr->IsBeingTeleported()==true)
+ {
+ PSendSysMessage(LANG_IS_TELEPORTED, chr->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_TELEPORTING_TO, chr->GetName(),"", tele->name.c_str());
+
+ if (m_session->GetPlayer()->IsVisibleGloballyFor(chr))
+ ChatHandler(chr).PSendSysMessage(LANG_TELEPORTED_TO_BY, m_session->GetPlayer()->GetName());
+
+ // stop flight if need
+ if(chr->isInFlight())
+ {
+ chr->GetMotionMaster()->MovementExpired();
+ chr->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ chr->SaveRecallPosition();
+
+ chr->TeleportTo(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation);
+ }
+ else if (uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str()))
+ {
+ PSendSysMessage(LANG_TELEPORTING_TO, name.c_str(), GetMangosString(LANG_OFFLINE), tele->name.c_str());
+ Player::SavePositionInDB(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation,MapManager::Instance().GetZoneId(tele->mapId,tele->position_x,tele->position_y),guid);
+ }
+ else
+ PSendSysMessage(LANG_NO_PLAYER, name.c_str());
+
+ return true;
+}
+
+//Teleport group to given game_tele.entry
+bool ChatHandler::HandleGroupTeleCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ Player *player = getSelectedPlayer();
+ if (!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
+ GameTele const* tele = extractGameTeleFromLink((char*)args);
+ if(!tele)
+ {
+ SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ MapEntry const * me = sMapStore.LookupEntry(tele->mapId);
+ if(!me || me->IsBattleGroundOrArena())
+ {
+ SendSysMessage(LANG_CANNOT_TELE_TO_BG);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ Group *grp = player->GetGroup();
+ if(!grp)
+ {
+ PSendSysMessage(LANG_NOT_IN_GROUP,player->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pl = itr->getSource();
+
+ if(!pl || !pl->GetSession() )
+ continue;
+
+ if(pl->IsBeingTeleported())
+ {
+ PSendSysMessage(LANG_IS_TELEPORTED, pl->GetName());
+ continue;
+ }
+
+ PSendSysMessage(LANG_TELEPORTING_TO, pl->GetName(),"", tele->name.c_str());
+
+ if (m_session->GetPlayer() != pl && m_session->GetPlayer()->IsVisibleGloballyFor(pl))
+ ChatHandler(pl).PSendSysMessage(LANG_TELEPORTED_TO_BY, m_session->GetPlayer()->GetName());
+
+ // stop flight if need
+ if(pl->isInFlight())
+ {
+ pl->GetMotionMaster()->MovementExpired();
+ pl->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ pl->SaveRecallPosition();
+
+ pl->TeleportTo(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation);
+ }
+
+ return true;
+}
+
+//Summon group of player
+bool ChatHandler::HandleGroupgoCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string name = args;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *player = objmgr.GetPlayer(name.c_str());
+ if (!player)
+ {
+ PSendSysMessage(LANG_NO_PLAYER, args);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Group *grp = player->GetGroup();
+
+ if(!grp)
+ {
+ PSendSysMessage(LANG_NOT_IN_GROUP,player->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Map* gmMap = MapManager::Instance().GetMap(m_session->GetPlayer()->GetMapId(),m_session->GetPlayer());
+ bool to_instance = gmMap->Instanceable();
+
+ // we are in instance, and can summon only player in our group with us as lead
+ if ( to_instance && (
+ !m_session->GetPlayer()->GetGroup() || (grp->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ||
+ (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ) )
+ // the last check is a bit excessive, but let it be, just in case
+ {
+ SendSysMessage(LANG_CANNOT_SUMMON_TO_INST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pl = itr->getSource();
+
+ if(!pl || pl==m_session->GetPlayer() || !pl->GetSession() )
+ continue;
+
+ if(pl->IsBeingTeleported()==true)
+ {
+ PSendSysMessage(LANG_IS_TELEPORTED, pl->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (to_instance)
+ {
+ Map* plMap = MapManager::Instance().GetMap(pl->GetMapId(),pl);
+
+ if ( plMap->Instanceable() && plMap->GetInstanceId() != gmMap->GetInstanceId() )
+ {
+ // cannot summon from instance to instance
+ PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,pl->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ PSendSysMessage(LANG_SUMMONING, pl->GetName(),"");
+
+ if (m_session->GetPlayer()->IsVisibleGloballyFor(pl))
+ ChatHandler(pl).PSendSysMessage(LANG_SUMMONED_BY, m_session->GetPlayer()->GetName());
+
+ // stop flight if need
+ if(pl->isInFlight())
+ {
+ pl->GetMotionMaster()->MovementExpired();
+ pl->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ pl->SaveRecallPosition();
+
+ // before GM
+ float x,y,z;
+ m_session->GetPlayer()->GetClosePoint(x,y,z,pl->GetObjectSize());
+ pl->TeleportTo(m_session->GetPlayer()->GetMapId(),x,y,z,pl->GetOrientation());
+ }
+
+ return true;
+}
+
+//teleport at coordinates
+bool ChatHandler::HandleGoXYCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+ char* pmapid = strtok(NULL, " ");
+
+ if (!px || !py)
+ return false;
+
+ float x = (float)atof(px);
+ float y = (float)atof(py);
+ uint32 mapid;
+ if (pmapid)
+ mapid = (uint32)atoi(pmapid);
+ else mapid = _player->GetMapId();
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ Map const *map = MapManager::Instance().GetBaseMap(mapid);
+ float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
+
+ _player->TeleportTo(mapid, x, y, z, _player->GetOrientation());
+
+ return true;
+}
+
+//teleport at coordinates, including Z
+bool ChatHandler::HandleGoXYZCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+ char* pz = strtok(NULL, " ");
+ char* pmapid = strtok(NULL, " ");
+
+ if (!px || !py || !pz)
+ return false;
+
+ float x = (float)atof(px);
+ float y = (float)atof(py);
+ float z = (float)atof(pz);
+ uint32 mapid;
+ if (pmapid)
+ mapid = (uint32)atoi(pmapid);
+ else
+ mapid = _player->GetMapId();
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y,z))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(mapid, x, y, z, _player->GetOrientation());
+
+ return true;
+}
+
+//teleport at coordinates
+bool ChatHandler::HandleGoZoneXYCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+ char* tail = strtok(NULL,"");
+
+ char* cAreaId = extractKeyFromLink(tail,"Harea"); // string or [name] Shift-click form |color|Harea:area_id|h[name]|h|r
+
+ if (!px || !py)
+ return false;
+
+ float x = (float)atof(px);
+ float y = (float)atof(py);
+ uint32 areaid = cAreaId ? (uint32)atoi(cAreaId) : _player->GetZoneId();
+
+ AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaid);
+
+ if( x<0 || x>100 || y<0 || y>100 || !areaEntry )
+ {
+ PSendSysMessage(LANG_INVALID_ZONE_COORD,x,y,areaid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // update to parent zone if exist (client map show only zones without parents)
+ AreaTableEntry const* zoneEntry = areaEntry->zone ? GetAreaEntryByAreaID(areaEntry->zone) : areaEntry;
+
+ Map const *map = MapManager::Instance().GetBaseMap(zoneEntry->mapid);
+
+ if(map->Instanceable())
+ {
+ PSendSysMessage(LANG_INVALID_ZONE_MAP,areaEntry->ID,areaEntry->area_name[m_session->GetSessionDbcLocale()],map->GetId(),map->GetMapName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Zone2MapCoordinates(x,y,zoneEntry->ID);
+
+ if(!MapManager::IsValidMapCoord(zoneEntry->mapid,x,y))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,zoneEntry->mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
+ _player->TeleportTo(zoneEntry->mapid, x, y, z, _player->GetOrientation());
+
+ return true;
+}
+
+//teleport to grid
+bool ChatHandler::HandleGoGridCommand(const char* args)
+{
+ if(!*args) return false;
+ Player* _player = m_session->GetPlayer();
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+ char* pmapid = strtok(NULL, " ");
+
+ if (!px || !py)
+ return false;
+
+ float grid_x = (float)atof(px);
+ float grid_y = (float)atof(py);
+ uint32 mapid;
+ if (pmapid)
+ mapid = (uint32)atoi(pmapid);
+ else mapid = _player->GetMapId();
+
+ // center of grid
+ float x = (grid_x-CENTER_GRID_ID+0.5f)*SIZE_OF_GRIDS;
+ float y = (grid_y-CENTER_GRID_ID+0.5f)*SIZE_OF_GRIDS;
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ Map const *map = MapManager::Instance().GetBaseMap(mapid);
+ float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
+ _player->TeleportTo(mapid, x, y, z, _player->GetOrientation());
+
+ return true;
+}
+
+bool ChatHandler::HandleDrunkCommand(const char* args)
+{
+ if(!*args) return false;
+
+ uint32 drunklevel = (uint32)atoi(args);
+ if(drunklevel > 100)
+ drunklevel = 100;
+
+ uint16 drunkMod = drunklevel * 0xFFFF / 100;
+
+ m_session->GetPlayer()->SetDrunkValue(drunkMod);
+
+ return true;
+}
diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp
index d2911057ee3..2580e8ad321 100644
--- a/src/game/Level2.cpp
+++ b/src/game/Level2.cpp
@@ -1,4009 +1,4009 @@
-/*
- * 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 "World.h"
-#include "ObjectMgr.h"
-#include "Player.h"
-#include "Item.h"
-#include "GameObject.h"
-#include "Opcodes.h"
-#include "Chat.h"
-#include "ObjectAccessor.h"
-#include "MapManager.h"
-#include "Language.h"
-#include "World.h"
-#include "GameEvent.h"
-#include "SpellMgr.h"
-#include "AccountMgr.h"
-#include "WaypointManager.h"
-#include "Util.h"
-#include <cctype>
-#include <iostream>
-#include <fstream>
-#include <map>
-
-static uint32 ReputationRankStrIndex[MAX_REPUTATION_RANK] =
-{
- LANG_REP_HATED, LANG_REP_HOSTILE, LANG_REP_UNFRIENDLY, LANG_REP_NEUTRAL,
- LANG_REP_FRIENDLY, LANG_REP_HONORED, LANG_REP_REVERED, LANG_REP_EXALTED
-};
-
-//mute player for some times
-bool ChatHandler::HandleMuteCommand(const char* args)
-{
- if (!*args)
- return false;
-
- char *charname = strtok((char*)args, " ");
- if (!charname)
- return false;
-
- std::string cname = charname;
-
- char *timetonotspeak = strtok(NULL, " ");
- if(!timetonotspeak)
- return false;
-
- uint32 notspeaktime = (uint32) atoi(timetonotspeak);
-
- if(!normalizePlayerName(cname))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 guid = objmgr.GetPlayerGUIDByName(cname.c_str());
- if(!guid)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = objmgr.GetPlayer(guid);
-
- // check security
- uint32 account_id = 0;
- uint32 security = 0;
-
- if (chr)
- {
- account_id = chr->GetSession()->GetAccountId();
- security = chr->GetSession()->GetSecurity();
- }
- else
- {
- account_id = objmgr.GetPlayerAccountIdByGUID(guid);
- security = objmgr.GetSecurityByAccount(account_id);
- }
-
- if(security >= m_session->GetSecurity())
- {
- SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
- SetSentErrorMessage(true);
- return false;
- }
-
- time_t mutetime = time(NULL) + notspeaktime*60;
-
- if (chr)
- chr->GetSession()->m_muteTime = mutetime;
-
- loginDatabase.PExecute("UPDATE account SET mutetime = " I64FMTD " WHERE id = '%u'",uint64(mutetime), account_id );
-
- if(chr)
- ChatHandler(chr).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notspeaktime);
-
- PSendSysMessage(LANG_YOU_DISABLE_CHAT, cname.c_str(), notspeaktime);
-
- return true;
-}
-
-//unmute player
-bool ChatHandler::HandleUnmuteCommand(const char* args)
-{
- if (!*args)
- return false;
-
- char *charname = strtok((char*)args, " ");
- if (!charname)
- return false;
-
- std::string cname = charname;
-
- if(!normalizePlayerName(cname))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 guid = objmgr.GetPlayerGUIDByName(cname.c_str());
- if(!guid)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = objmgr.GetPlayer(guid);
-
- // check security
- uint32 account_id = 0;
- uint32 security = 0;
-
- if (chr)
- {
- account_id = chr->GetSession()->GetAccountId();
- security = chr->GetSession()->GetSecurity();
- }
- else
- {
- account_id = objmgr.GetPlayerAccountIdByGUID(guid);
- security = objmgr.GetSecurityByAccount(account_id);
- }
-
- if(security >= m_session->GetSecurity())
- {
- SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
- SetSentErrorMessage(true);
- return false;
- }
-
- if (chr)
- {
- if(chr->CanSpeak())
- {
- SendSysMessage(LANG_CHAT_ALREADY_ENABLED);
- SetSentErrorMessage(true);
- return false;
- }
-
- chr->GetSession()->m_muteTime = 0;
- }
-
- loginDatabase.PExecute("UPDATE account SET mutetime = '0' WHERE id = '%u'", account_id );
-
- if(chr)
- ChatHandler(chr).PSendSysMessage(LANG_YOUR_CHAT_ENABLED);
-
- PSendSysMessage(LANG_YOU_ENABLE_CHAT, cname.c_str());
- return true;
-}
-
-bool ChatHandler::HandleTargetObjectCommand(const char* args)
-{
- Player* pl = m_session->GetPlayer();
- QueryResult *result;
- GameEvent::ActiveEvents const& activeEventsList = gameeventmgr.GetActiveEventList();
- if(*args)
- {
- int32 id = atoi((char*)args);
- if(id)
- result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE map = '%i' AND id = '%u' ORDER BY order_ ASC LIMIT 1",
- pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),id);
- else
- {
- std::string name = args;
- WorldDatabase.escape_string(name);
- result = WorldDatabase.PQuery(
- "SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ "
- "FROM gameobject,gameobject_template WHERE gameobject_template.entry = gameobject.id AND map = %i AND name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'")" ORDER BY order_ ASC LIMIT 1",
- pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),name.c_str());
- }
- }
- else
- {
- std::ostringstream eventFilter;
- eventFilter << " AND (event IS NULL ";
- bool initString = true;
-
- for (GameEvent::ActiveEvents::const_iterator itr = activeEventsList.begin(); itr != activeEventsList.end(); ++itr)
- {
- if (initString)
- {
- eventFilter << "OR event IN (" <<*itr;
- initString =false;
- }
- else
- eventFilter << "," << *itr;
- }
-
- if (!initString)
- eventFilter << "))";
- else
- eventFilter << ")";
-
- result = WorldDatabase.PQuery("SELECT gameobject.guid, id, position_x, position_y, position_z, orientation, map, "
- "(POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ FROM gameobject "
- "LEFT OUTER JOIN game_event_gameobject on gameobject.guid=game_event_gameobject.guid WHERE map = '%i' %s ORDER BY order_ ASC LIMIT 1",
- m_session->GetPlayer()->GetPositionX(), m_session->GetPlayer()->GetPositionY(), m_session->GetPlayer()->GetPositionZ(), m_session->GetPlayer()->GetMapId(),eventFilter.str().c_str());
- }
-
- if (!result)
- {
- SendSysMessage(LANG_COMMAND_TARGETOBJNOTFOUND);
- return true;
- }
-
- Field *fields = result->Fetch();
- uint32 lowguid = fields[0].GetUInt32();
- uint32 id = fields[1].GetUInt32();
- float x = fields[2].GetFloat();
- float y = fields[3].GetFloat();
- float z = fields[4].GetFloat();
- float o = fields[5].GetFloat();
- int mapid = fields[6].GetUInt16();
- delete result;
-
- GameObjectInfo const* goI = objmgr.GetGameObjectInfo(id);
-
- if (!goI)
- {
- PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id);
- return false;
- }
-
- GameObject* target = ObjectAccessor::GetGameObject(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,id,HIGHGUID_GAMEOBJECT));
-
- PSendSysMessage(LANG_GAMEOBJECT_DETAIL, lowguid, goI->name, lowguid, id, x, y, z, mapid, o);
-
- if(target)
- {
- int32 curRespawnDelay = target->GetRespawnTimeEx()-time(NULL);
- if(curRespawnDelay < 0)
- curRespawnDelay = 0;
-
- std::string curRespawnDelayStr = secsToTimeString(curRespawnDelay,true);
- std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(),true);
-
- PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(),curRespawnDelayStr.c_str());
- }
- return true;
-}
-
-//teleport to gameobject
-bool ChatHandler::HandleGoObjectCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Player* _player = m_session->GetPlayer();
-
- // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hgameobject");
- if(!cId)
- return false;
-
- int32 guid = atoi(cId);
- if(!guid)
- return false;
-
- float x, y, z, ort;
- int mapid;
-
- // by DB guid
- if (GameObjectData const* go_data = objmgr.GetGOData(guid))
- {
- x = go_data->posX;
- y = go_data->posY;
- z = go_data->posZ;
- ort = go_data->orientation;
- mapid = go_data->mapid;
- }
- else
- {
- SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!MapManager::IsValidMapCoord(mapid,x,y,z,ort))
- {
- PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
- SetSentErrorMessage(true);
- return false;
- }
-
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- _player->TeleportTo(mapid, x, y, z, ort);
- return true;
-}
-
-bool ChatHandler::HandleGoTriggerCommand(const char* args)
-{
- Player* _player = m_session->GetPlayer();
-
- if (!*args)
- return false;
-
- char *atId = strtok((char*)args, " ");
- if (!atId)
- return false;
-
- int32 i_atId = atoi(atId);
-
- if(!i_atId)
- return false;
-
- AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(i_atId);
- if (!at)
- {
- PSendSysMessage(LANG_COMMAND_GOAREATRNOTFOUND,i_atId);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!MapManager::IsValidMapCoord(at->mapid,at->x,at->y,at->z))
- {
- PSendSysMessage(LANG_INVALID_TARGET_COORD,at->x,at->y,at->mapid);
- SetSentErrorMessage(true);
- return false;
- }
-
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- _player->TeleportTo(at->mapid, at->x, at->y, at->z, _player->GetOrientation());
- return true;
-}
-
-bool ChatHandler::HandleGoGraveyardCommand(const char* args)
-{
- Player* _player = m_session->GetPlayer();
-
- if (!*args)
- return false;
-
- char *gyId = strtok((char*)args, " ");
- if (!gyId)
- return false;
-
- int32 i_gyId = atoi(gyId);
-
- if(!i_gyId)
- return false;
-
- WorldSafeLocsEntry const* gy = sWorldSafeLocsStore.LookupEntry(i_gyId);
- if (!gy)
- {
- PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST,i_gyId);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!MapManager::IsValidMapCoord(gy->map_id,gy->x,gy->y,gy->z))
- {
- PSendSysMessage(LANG_INVALID_TARGET_COORD,gy->x,gy->y,gy->map_id);
- SetSentErrorMessage(true);
- return false;
- }
-
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- _player->TeleportTo(gy->map_id, gy->x, gy->y, gy->z, _player->GetOrientation());
- return true;
-}
-
-/** \brief Teleport the GM to the specified creature
- *
- * .gocreature <GUID> --> TP using creature.guid
- * .gocreature azuregos --> TP player to the mob with this name
- * Warning: If there is more than one mob with this name
- * you will be teleported to the first one that is found.
- * .gocreature id 6109 --> TP player to the mob, that has this creature_template.entry
- * Warning: If there is more than one mob with this "id"
- * you will be teleported to the first one that is found.
- */
-//teleport to creature
-bool ChatHandler::HandleGoCreatureCommand(const char* args)
-{
- if(!*args)
- return false;
- Player* _player = m_session->GetPlayer();
-
- // "id" or number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
- char* pParam1 = extractKeyFromLink((char*)args,"Hcreature");
- if (!pParam1)
- return false;
-
- std::ostringstream whereClause;
-
- // User wants to teleport to the NPC's template entry
- if( strcmp(pParam1, "id") == 0 )
- {
- //sLog.outError("DEBUG: ID found");
-
- // Get the "creature_template.entry"
- // number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
- char* tail = strtok(NULL,"");
- if(!tail)
- return false;
- char* cId = extractKeyFromLink(tail,"Hcreature_entry");
- if(!cId)
- return false;
-
- int32 tEntry = atoi(cId);
- //sLog.outError("DEBUG: ID value: %d", tEntry);
- if(!tEntry)
- return false;
-
- whereClause << "WHERE id = '" << tEntry << "'";
- }
- else
- {
- //sLog.outError("DEBUG: ID *not found*");
-
- int32 guid = atoi(pParam1);
-
- // Number is invalid - maybe the user specified the mob's name
- if(!guid)
- {
- std::string name = pParam1;
- WorldDatabase.escape_string(name);
- whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name "_LIKE_" '" << name << "'";
- }
- else
- {
- whereClause << "WHERE guid = '" << guid << "'";
- }
- }
- //sLog.outError("DEBUG: %s", whereClause.c_str());
-
- QueryResult *result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM creature %s", whereClause.str().c_str() );
- if (!result)
- {
- SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
- SetSentErrorMessage(true);
- return false;
- }
- if( result->GetRowCount() > 1 )
- {
- SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
- }
-
- Field *fields = result->Fetch();
- float x = fields[0].GetFloat();
- float y = fields[1].GetFloat();
- float z = fields[2].GetFloat();
- float ort = fields[3].GetFloat();
- int mapid = fields[4].GetUInt16();
-
- delete result;
-
- if(!MapManager::IsValidMapCoord(mapid,x,y,z,ort))
- {
- PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
- SetSentErrorMessage(true);
- return false;
- }
-
- // stop flight if need
- if(_player->isInFlight())
- {
- _player->GetMotionMaster()->MovementExpired();
- _player->m_taxi.ClearTaxiDestinations();
- }
- // save only in non-flight case
- else
- _player->SaveRecallPosition();
-
- _player->TeleportTo(mapid, x, y, z, ort);
- return true;
-}
-
-bool ChatHandler::HandleGUIDCommand(const char* /*args*/)
-{
- uint64 guid = m_session->GetPlayer()->GetSelection();
-
- if (guid == 0)
- {
- SendSysMessage(LANG_NO_SELECTION);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_OBJECT_GUID, GUID_LOPART(guid), GUID_HIPART(guid));
- return true;
-}
-
-bool ChatHandler::HandleLookupFactionCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Player *target = getSelectedPlayer();
- if (!target)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- std::string namepart = args;
- std::wstring wnamepart;
-
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- // converting string that we try to find to lower case
- wstrToLower( wnamepart );
-
- uint32 counter = 0; // Counter for figure out that we found smth.
-
- for (uint32 id = 0; id < sFactionStore.GetNumRows(); id++)
- //for(FactionStateList::const_iterator itr = target->m_factions.begin(); itr != target->m_factions.end(); ++itr)
- {
- FactionEntry const *factionEntry = sFactionStore.LookupEntry(id);
- //FactionEntry const *factionEntry = sFactionStore.LookupEntry(itr->second.ID);
- if (factionEntry)
- {
- FactionStateList::const_iterator repItr = target->m_factions.find(factionEntry->reputationListID);
-
- int loc = m_session->GetSessionDbcLocale();
- std::string name = factionEntry->name[loc];
- if(name.empty())
- continue;
-
- if (!Utf8FitTo(name, wnamepart))
- {
- loc = 0;
- for(; loc < MAX_LOCALE; ++loc)
- {
- if(loc==m_session->GetSessionDbcLocale())
- continue;
-
- name = factionEntry->name[loc];
- if(name.empty())
- continue;
-
- if (Utf8FitTo(name, wnamepart))
- break;
- }
- }
-
- if(loc < MAX_LOCALE)
- {
- // send faction in "id - [faction] rank reputation [visible] [at war] [own team] [unknown] [invisible] [inactive]" format
- // or "id - [faction] [no reputation]" format
- std::ostringstream ss;
- ss << id << " - |cffffffff|Hfaction:" << id << "|h[" << name << " " << localeNames[loc] << "]|h|r";
-
- if (repItr != target->m_factions.end())
- {
- ReputationRank rank = target->GetReputationRank(factionEntry);
- std::string rankName = GetMangosString(ReputationRankStrIndex[rank]);
-
- ss << " " << rankName << "|h|r (" << target->GetReputation(factionEntry) << ")";
-
- if(repItr->second.Flags & FACTION_FLAG_VISIBLE)
- ss << GetMangosString(LANG_FACTION_VISIBLE);
- if(repItr->second.Flags & FACTION_FLAG_AT_WAR)
- ss << GetMangosString(LANG_FACTION_ATWAR);
- if(repItr->second.Flags & FACTION_FLAG_PEACE_FORCED)
- ss << GetMangosString(LANG_FACTION_PEACE_FORCED);
- if(repItr->second.Flags & FACTION_FLAG_HIDDEN)
- ss << GetMangosString(LANG_FACTION_HIDDEN);
- if(repItr->second.Flags & FACTION_FLAG_INVISIBLE_FORCED)
- ss << GetMangosString(LANG_FACTION_INVISIBLE_FORCED);
- if(repItr->second.Flags & FACTION_FLAG_INACTIVE)
- ss << GetMangosString(LANG_FACTION_INACTIVE);
- }
- else
- ss << GetMangosString(LANG_FACTION_NOREPUTATION);
-
- SendSysMessage(ss.str().c_str());
- counter++;
- }
- }
- }
-
- if (counter == 0) // if counter == 0 then we found nth
- SendSysMessage(LANG_COMMAND_FACTION_NOTFOUND);
- return true;
-}
-
-bool ChatHandler::HandleModifyRepCommand(const char * args)
-{
- if (!*args) return false;
-
- Player* target = NULL;
- target = getSelectedPlayer();
-
- if(!target)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* factionTxt = extractKeyFromLink((char*)args,"Hfaction");
- if(!factionTxt)
- return false;
-
- uint32 factionId = atoi(factionTxt);
-
- int32 amount = 0;
- char *rankTxt = strtok(NULL, " ");
- if (!factionTxt || !rankTxt)
- return false;
-
- amount = atoi(rankTxt);
- if ((amount == 0) && (rankTxt[0] != '-') && !isdigit(rankTxt[0]))
- {
- std::string rankStr = rankTxt;
- std::wstring wrankStr;
- if(!Utf8toWStr(rankStr,wrankStr))
- return false;
- wstrToLower( wrankStr );
-
- int r = 0;
- amount = -42000;
- for (; r < MAX_REPUTATION_RANK; ++r)
- {
- std::string rank = GetMangosString(ReputationRankStrIndex[r]);
- if(rank.empty())
- continue;
-
- std::wstring wrank;
- if(!Utf8toWStr(rank,wrank))
- continue;
-
- wstrToLower(wrank);
-
- if(wrank.substr(0,wrankStr.size())==wrankStr)
- {
- char *deltaTxt = strtok(NULL, " ");
- if (deltaTxt)
- {
- int32 delta = atoi(deltaTxt);
- if ((delta < 0) || (delta > Player::ReputationRank_Length[r] -1))
- {
- PSendSysMessage(LANG_COMMAND_FACTION_DELTA, (Player::ReputationRank_Length[r]-1));
- SetSentErrorMessage(true);
- return false;
- }
- amount += delta;
- }
- break;
- }
- amount += Player::ReputationRank_Length[r];
- }
- if (r >= MAX_REPUTATION_RANK)
- {
- PSendSysMessage(LANG_COMMAND_FACTION_INVPARAM, rankTxt);
- SetSentErrorMessage(true);
- return false;
- }
- }
-
- FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionId);
-
- if (!factionEntry)
- {
- PSendSysMessage(LANG_COMMAND_FACTION_UNKNOWN, factionId);
- SetSentErrorMessage(true);
- return false;
- }
-
- if (factionEntry->reputationListID < 0)
- {
- PSendSysMessage(LANG_COMMAND_FACTION_NOREP_ERROR, factionEntry->name[m_session->GetSessionDbcLocale()], factionId);
- SetSentErrorMessage(true);
- return false;
- }
-
- target->SetFactionReputation(factionEntry,amount);
- PSendSysMessage(LANG_COMMAND_MODIFY_REP, factionEntry->name[m_session->GetSessionDbcLocale()], factionId, target->GetName(), target->GetReputation(factionId));
- return true;
-}
-
-bool ChatHandler::HandleNameCommand(const char* args)
-{
- /* Temp. disabled
- if(!*args)
- return false;
-
- if(strlen((char*)args)>75)
- {
- PSendSysMessage(LANG_TOO_LONG_NAME, strlen((char*)args)-75);
- return true;
- }
-
- for (uint8 i = 0; i < strlen(args); i++)
- {
- if(!isalpha(args[i]) && args[i]!=' ')
- {
- SendSysMessage(LANG_CHARS_ONLY);
- return false;
- }
- }
-
- uint64 guid;
- guid = m_session->GetPlayer()->GetSelection();
- if (guid == 0)
- {
- SendSysMessage(LANG_NO_SELECTION);
- return true;
- }
-
- Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
-
- if(!pCreature)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- return true;
- }
-
- pCreature->SetName(args);
- uint32 idname = objmgr.AddCreatureTemplate(pCreature->GetName());
- pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname);
-
- pCreature->SaveToDB();
- */
-
- return true;
-}
-
-bool ChatHandler::HandleSubNameCommand(const char* /*args*/)
-{
- /* Temp. disabled
-
- if(!*args)
- args = "";
-
- if(strlen((char*)args)>75)
- {
-
- PSendSysMessage(LANG_TOO_LONG_SUBNAME, strlen((char*)args)-75);
- return true;
- }
-
- for (uint8 i = 0; i < strlen(args); i++)
- {
- if(!isalpha(args[i]) && args[i]!=' ')
- {
- SendSysMessage(LANG_CHARS_ONLY);
- return false;
- }
- }
- uint64 guid;
- guid = m_session->GetPlayer()->GetSelection();
- if (guid == 0)
- {
- SendSysMessage(LANG_NO_SELECTION);
- return true;
- }
-
- Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
-
- if(!pCreature)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- return true;
- }
-
- uint32 idname = objmgr.AddCreatureSubName(pCreature->GetName(),args,pCreature->GetUInt32Value(UNIT_FIELD_DISPLAYID));
- pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname);
-
- pCreature->SaveToDB();
- */
- return true;
-}
-
-//move item to other slot
-bool ChatHandler::HandleItemMoveCommand(const char* args)
-{
- if(!*args)
- return false;
- uint8 srcslot, dstslot;
-
- char* pParam1 = strtok((char*)args, " ");
- if (!pParam1)
- return false;
-
- char* pParam2 = strtok(NULL, " ");
- if (!pParam2)
- return false;
-
- srcslot = (uint8)atoi(pParam1);
- dstslot = (uint8)atoi(pParam2);
-
- uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot);
- uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot);
-
- if(srcslot==dstslot)
- return true;
-
- m_session->GetPlayer()->SwapItem( src, dst );
-
- return true;
-}
-
-//add spawn of creature
-bool ChatHandler::HandleAddSpwCommand(const char* args)
-{
- if(!*args)
- return false;
- char* charID = strtok((char*)args, " ");
- if (!charID)
- return false;
-
- char* team = strtok(NULL, " ");
- int32 teamval = 0;
- if (team) { teamval = atoi(team); }
- if (teamval < 0) { teamval = 0; }
-
- uint32 id = atoi(charID);
-
- Player *chr = m_session->GetPlayer();
- float x = chr->GetPositionX();
- float y = chr->GetPositionY();
- float z = chr->GetPositionZ();
- float o = chr->GetOrientation();
- Map *map = chr->GetMap();
-
- Creature* pCreature = new Creature;
- if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, (uint32)teamval))
- {
- delete pCreature;
- return false;
- }
-
- pCreature->Relocate(x,y,z,o);
-
- if(!pCreature->IsPositionValid())
- {
- sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
- delete pCreature;
- return false;
- }
-
- pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
-
- uint32 db_guid = pCreature->GetDBTableGUIDLow();
-
- // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
- pCreature->LoadFromDB(db_guid, map);
-
- map->Add(pCreature);
- objmgr.AddCreatureToGrid(db_guid, objmgr.GetCreatureData(db_guid));
- return true;
-}
-
-bool ChatHandler::HandleDelCreatureCommand(const char* args)
-{
- Creature* unit = NULL;
-
- if(*args)
- {
- // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hcreature");
- if(!cId)
- return false;
-
- uint32 lowguid = atoi(cId);
- if(!lowguid)
- return false;
-
- if (CreatureData const* cr_data = objmgr.GetCreatureData(lowguid))
- unit = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, cr_data->id, HIGHGUID_UNIT));
- }
- else
- unit = getSelectedCreature();
-
- if(!unit || unit->isPet() || unit->isTotem())
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- // Delete the creature
- unit->CombatStop();
- unit->DeleteFromDB();
- unit->CleanupsBeforeDelete();
- unit->AddObjectToRemoveList();
-
- SendSysMessage(LANG_COMMAND_DELCREATMESSAGE);
-
- return true;
-}
-
-//delete object by selection or guid
-bool ChatHandler::HandleDelObjectCommand(const char* args)
-{
- // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hgameobject");
- if(!cId)
- return false;
-
- uint32 lowguid = atoi(cId);
- if(!lowguid)
- return false;
-
- GameObject* obj = NULL;
-
- // by DB guid
- if (GameObjectData const* go_data = objmgr.GetGOData(lowguid))
- obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id);
-
- if(!obj)
- {
- PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 owner_guid = obj->GetOwnerGUID();
- if(owner_guid)
- {
- Unit* owner = ObjectAccessor::GetUnit(*m_session->GetPlayer(),owner_guid);
- if(!owner && !IS_PLAYER_GUID(owner_guid))
- {
- PSendSysMessage(LANG_COMMAND_DELOBJREFERCREATURE, GUID_LOPART(owner_guid), obj->GetGUIDLow());
- SetSentErrorMessage(true);
- return false;
- }
-
- owner->RemoveGameObject(obj,false);
- }
-
- obj->SetRespawnTime(0); // not save respawn time
- obj->Delete();
- obj->DeleteFromDB();
-
- PSendSysMessage(LANG_COMMAND_DELOBJMESSAGE, obj->GetGUIDLow());
-
- return true;
-}
-
-//turn selected object
-bool ChatHandler::HandleTurnObjectCommand(const char* args)
-{
- // number or [name] Shift-click form |color|Hgameobject:go_id|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hgameobject");
- if(!cId)
- return false;
-
- uint32 lowguid = atoi(cId);
- if(!lowguid)
- return false;
-
- GameObject* obj = NULL;
-
- // by DB guid
- if (GameObjectData const* go_data = objmgr.GetGOData(lowguid))
- obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id);
-
- if(!obj)
- {
- PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* po = strtok(NULL, " ");
- float o;
-
- if (po)
- {
- o = (float)atof(po);
- }
- else
- {
- Player *chr = m_session->GetPlayer();
- o = chr->GetOrientation();
- }
-
- float rot2 = sin(o/2);
- float rot3 = cos(o/2);
-
- Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj);
- map->Remove(obj,false);
-
- obj->Relocate(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), o);
-
- obj->SetFloatValue(GAMEOBJECT_FACING, o);
- obj->SetFloatValue(GAMEOBJECT_ROTATION+2, rot2);
- obj->SetFloatValue(GAMEOBJECT_ROTATION+3, rot3);
-
- map->Add(obj);
-
- obj->SaveToDB();
- obj->Refresh();
-
- PSendSysMessage(LANG_COMMAND_TURNOBJMESSAGE, obj->GetGUIDLow(), o);
-
- return true;
-}
-
-//move selected creature
-bool ChatHandler::HandleMoveCreatureCommand(const char* args)
-{
- uint32 lowguid = 0;
-
- Creature* pCreature = getSelectedCreature();
-
- if(!pCreature)
- {
- // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hcreature");
- if(!cId)
- return false;
-
- uint32 lowguid = atoi(cId);
-
- /* FIXME: impossibel without entry
- if(lowguid)
- pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
- */
-
- // Attempting creature load from DB data
- if(!pCreature)
- {
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint32 map_id = data->mapid;
-
- if(m_session->GetPlayer()->GetMapId()!=map_id)
- {
- PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
- }
- else
- {
- lowguid = pCreature->GetDBTableGUIDLow();
- }
- }
- else
- {
- lowguid = pCreature->GetDBTableGUIDLow();
- }
-
- float x = m_session->GetPlayer()->GetPositionX();
- float y = m_session->GetPlayer()->GetPositionY();
- float z = m_session->GetPlayer()->GetPositionZ();
- float o = m_session->GetPlayer()->GetOrientation();
-
- if (pCreature)
- {
- if(CreatureData const* data = objmgr.GetCreatureData(pCreature->GetDBTableGUIDLow()))
- {
- const_cast<CreatureData*>(data)->posX = x;
- const_cast<CreatureData*>(data)->posY = y;
- const_cast<CreatureData*>(data)->posZ = z;
- const_cast<CreatureData*>(data)->orientation = o;
- }
- MapManager::Instance().GetMap(pCreature->GetMapId(),pCreature)->CreatureRelocation(pCreature,x, y, z,o);
- pCreature->GetMotionMaster()->Initialize();
- if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
- {
- pCreature->setDeathState(JUST_DIED);
- pCreature->Respawn();
- }
- }
-
- WorldDatabase.PExecuteLog("UPDATE creature SET position_x = '%f', position_y = '%f', position_z = '%f', orientation = '%f' WHERE guid = '%u'", x, y, z, o, lowguid);
- PSendSysMessage(LANG_COMMAND_CREATUREMOVED);
- return true;
-}
-
-//move selected object
-bool ChatHandler::HandleMoveObjectCommand(const char* args)
-{
- // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hgameobject");
- if(!cId)
- return false;
-
- uint32 lowguid = atoi(cId);
- if(!lowguid)
- return false;
-
- GameObject* obj = NULL;
-
- // by DB guid
- if (GameObjectData const* go_data = objmgr.GetGOData(lowguid))
- obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id);
-
- if(!obj)
- {
- PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* px = strtok(NULL, " ");
- char* py = strtok(NULL, " ");
- char* pz = strtok(NULL, " ");
-
- if (!px)
- {
- Player *chr = m_session->GetPlayer();
-
- Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj);
- map->Remove(obj,false);
-
- obj->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), obj->GetOrientation());
- obj->SetFloatValue(GAMEOBJECT_POS_X, chr->GetPositionX());
- obj->SetFloatValue(GAMEOBJECT_POS_Y, chr->GetPositionY());
- obj->SetFloatValue(GAMEOBJECT_POS_Z, chr->GetPositionZ());
-
- map->Add(obj);
- }
- else
- {
- if(!py || !pz)
- return false;
-
- float x = (float)atof(px);
- float y = (float)atof(py);
- float z = (float)atof(pz);
-
- if(!MapManager::IsValidMapCoord(obj->GetMapId(),x,y,z))
- {
- PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,obj->GetMapId());
- SetSentErrorMessage(true);
- return false;
- }
-
- Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj);
- map->Remove(obj,false);
-
- obj->Relocate(x, y, z, obj->GetOrientation());
- obj->SetFloatValue(GAMEOBJECT_POS_X, x);
- obj->SetFloatValue(GAMEOBJECT_POS_Y, y);
- obj->SetFloatValue(GAMEOBJECT_POS_Z, z);
-
- map->Add(obj);
- }
-
- obj->SaveToDB();
- obj->Refresh();
-
- PSendSysMessage(LANG_COMMAND_MOVEOBJMESSAGE, obj->GetGUIDLow());
-
- return true;
-}
-
-//demorph player or unit
-bool ChatHandler::HandleDeMorphCommand(const char* /*args*/)
-{
- Unit *target = getSelectedUnit();
- if(!target)
- target = m_session->GetPlayer();
-
- target->DeMorph();
-
- return true;
-}
-
-//add item in vendorlist
-bool ChatHandler::HandleAddVendorItemCommand(const char* args)
-{
- if (!*args)
- return false;
-
- char* pitem = extractKeyFromLink((char*)args,"Hitem");
- if (!pitem)
- {
- SendSysMessage(LANG_COMMAND_NEEDITEMSEND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint32 itemId = atol(pitem);
-
- char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0
- uint32 maxcount = 0;
- if (fmaxcount)
- maxcount = atol(fmaxcount);
-
- char* fincrtime = strtok(NULL, " "); //add incrtime, default: 0
- uint32 incrtime = 0;
- if (fincrtime)
- incrtime = atol(fincrtime);
-
- char* fextendedcost = strtok(NULL, " "); //add ExtendedCost, default: 0
- uint32 extendedcost = fextendedcost ? atol(fextendedcost) : 0;
-
- Creature* vendor = getSelectedCreature();
-
- uint32 vendor_entry = vendor ? vendor->GetEntry() : 0;
-
- if(!objmgr.IsVendorItemValid(vendor_entry,itemId,maxcount,incrtime,extendedcost,m_session->GetPlayer()))
- {
- SetSentErrorMessage(true);
- return false;
- }
-
- objmgr.AddVendorItem(vendor_entry,itemId,maxcount,incrtime,extendedcost);
-
- ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId);
-
- PSendSysMessage(LANG_ITEM_ADDED_TO_LIST,itemId,pProto->Name1,maxcount,incrtime,extendedcost);
- return true;
-}
-
-//del item from vendor list
-bool ChatHandler::HandleDelVendorItemCommand(const char* args)
-{
- if (!*args)
- return false;
-
- Creature* vendor = getSelectedCreature();
- if (!vendor || !vendor->isVendor())
- {
- SendSysMessage(LANG_COMMAND_VENDORSELECTION);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* pitem = extractKeyFromLink((char*)args,"Hitem");
- if (!pitem)
- {
- SendSysMessage(LANG_COMMAND_NEEDITEMSEND);
- SetSentErrorMessage(true);
- return false;
- }
- uint32 itemId = atol(pitem);
-
-
- if(!objmgr.RemoveVendorItem(vendor->GetEntry(),itemId))
- {
- PSendSysMessage(LANG_ITEM_NOT_IN_LIST,itemId);
- SetSentErrorMessage(true);
- return false;
- }
-
- ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId);
-
- PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST,itemId,pProto->Name1);
- return true;
-}
-
-//add move for creature
-bool ChatHandler::HandleAddMoveCommand(const char* args)
-{
- if(!*args)
- return false;
-
- char* guid_str = strtok((char*)args, " ");
- char* wait_str = strtok((char*)NULL, " ");
-
- uint32 lowguid = atoi((char*)guid_str);
-
- Creature* pCreature = NULL;
-
- /* FIXME: impossible without entry
- if(lowguid)
- pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
- */
-
- // attempt check creature existence by DB data
- if(!pCreature)
- {
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
- }
- else
- {
- // obtain real GUID for DB operations
- lowguid = pCreature->GetDBTableGUIDLow();
- }
-
- int wait = wait_str ? atoi(wait_str) : 0;
-
- if(wait < 0)
- wait = 0;
-
- Player* player = m_session->GetPlayer();
-
- WaypointMgr.AddLastNode(lowguid, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), wait, 0);
-
- // update movement type
- WorldDatabase.PExecuteLog("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE,lowguid);
- if(pCreature)
- {
- pCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
- pCreature->GetMotionMaster()->Initialize();
- if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
- {
- pCreature->setDeathState(JUST_DIED);
- pCreature->Respawn();
- }
- pCreature->SaveToDB();
- }
-
- SendSysMessage(LANG_WAYPOINT_ADDED);
-
- return true;
-}
-
-/**
- * Set the movement type for an NPC.<br/>
- * <br/>
- * Valid movement types are:
- * <ul>
- * <li> stay - NPC wont move </li>
- * <li> random - NPC will move randomly according to the spawndist </li>
- * <li> way - NPC will move with given waypoints set </li>
- * </ul>
- * additional parameter: NODEL - so no waypoints are deleted, if you
- * change the movement type
- */
-bool ChatHandler::HandleSetMoveTypeCommand(const char* args)
-{
- if(!*args)
- return false;
-
- // 3 arguments:
- // GUID (optional - you can also select the creature)
- // stay|random|way (determines the kind of movement)
- // NODEL (optional - tells the system NOT to delete any waypoints)
- // this is very handy if you want to do waypoints, that are
- // later switched on/off according to special events (like escort
- // quests, etc)
- char* guid_str = strtok((char*)args, " ");
- char* type_str = strtok((char*)NULL, " ");
- char* dontdel_str = strtok((char*)NULL, " ");
-
- bool doNotDelete = false;
-
- if(!guid_str)
- return false;
-
- uint32 lowguid = 0;
- Creature* pCreature = NULL;
-
- if( dontdel_str )
- {
- //sLog.outError("DEBUG: All 3 params are set");
-
- // All 3 params are set
- // GUID
- // type
- // doNotDEL
- if( stricmp( dontdel_str, "NODEL" ) == 0 )
- {
- //sLog.outError("DEBUG: doNotDelete = true;");
- doNotDelete = true;
- }
- }
- else
- {
- // Only 2 params - but maybe NODEL is set
- if( type_str )
- {
- sLog.outError("DEBUG: Only 2 params ");
- if( stricmp( type_str, "NODEL" ) == 0 )
- {
- //sLog.outError("DEBUG: type_str, NODEL ");
- doNotDelete = true;
- type_str = NULL;
- }
- }
- }
-
- if(!type_str) // case .setmovetype $move_type (with selected creature)
- {
- type_str = guid_str;
- pCreature = getSelectedCreature();
- if(!pCreature)
- return false;
- lowguid = pCreature->GetDBTableGUIDLow();
- }
- else // case .setmovetype #creature_guid $move_type (with selected creature)
- {
- lowguid = atoi((char*)guid_str);
-
- /* impossible without entry
- if(lowguid)
- pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
- */
-
- // attempt check creature existence by DB data
- if(!pCreature)
- {
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
- }
- else
- {
- lowguid = pCreature->GetDBTableGUIDLow();
- }
- }
-
- // now lowguid is low guid really existed creature
- // and pCreature point (maybe) to this creature or NULL
-
- MovementGeneratorType move_type;
-
- std::string type = type_str;
-
- if(type == "stay")
- move_type = IDLE_MOTION_TYPE;
- else if(type == "random")
- move_type = RANDOM_MOTION_TYPE;
- else if(type == "way")
- move_type = WAYPOINT_MOTION_TYPE;
- else
- return false;
-
- // update movement type
- if(doNotDelete == false)
- WaypointMgr.DeletePath(lowguid);
-
- if(pCreature)
- {
- pCreature->SetDefaultMovementType(move_type);
- pCreature->GetMotionMaster()->Initialize();
- if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
- {
- pCreature->setDeathState(JUST_DIED);
- pCreature->Respawn();
- }
- pCreature->SaveToDB();
- }
- if( doNotDelete == false )
- {
- PSendSysMessage(LANG_MOVE_TYPE_SET,type_str);
- }
- else
- {
- PSendSysMessage(LANG_MOVE_TYPE_SET_NODEL,type_str);
- }
-
- return true;
-} // HandleSetMoveTypeCommand
-
-//change level of creature or pet
-bool ChatHandler::HandleChangeLevelCommand(const char* args)
-{
- if (!*args)
- return false;
-
- uint8 lvl = (uint8) atoi((char*)args);
- if ( lvl < 1 || lvl > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) + 3)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Creature* pCreature = getSelectedCreature();
- if(!pCreature)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(pCreature->isPet())
- {
- ((Pet*)pCreature)->GivePetLevel(lvl);
- }
- else
- {
- pCreature->SetMaxHealth( 100 + 30*lvl);
- pCreature->SetHealth( 100 + 30*lvl);
- pCreature->SetLevel( lvl);
- pCreature->SaveToDB();
- }
-
- return true;
-}
-
-//set npcflag of creature
-bool ChatHandler::HandleNPCFlagCommand(const char* args)
-{
- if (!*args)
- return false;
-
- uint32 npcFlags = (uint32) atoi((char*)args);
-
- Creature* pCreature = getSelectedCreature();
-
- if(!pCreature)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- pCreature->SetUInt32Value(UNIT_NPC_FLAGS, npcFlags);
-
- WorldDatabase.PExecuteLog("UPDATE creature_template SET npcflag = '%u' WHERE entry = '%u'", npcFlags, pCreature->GetEntry());
-
- SendSysMessage(LANG_VALUE_SAVED_REJOIN);
-
- return true;
-}
-
-//set model of creature
-bool ChatHandler::HandleSetModelCommand(const char* args)
-{
- if (!*args)
- return false;
-
- uint32 displayId = (uint32) atoi((char*)args);
-
- Creature *pCreature = getSelectedCreature();
-
- if(!pCreature)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- pCreature->SetDisplayId(displayId);
- pCreature->SetNativeDisplayId(displayId);
-
- pCreature->SaveToDB();
-
- return true;
-}
-
-//morph creature or player
-bool ChatHandler::HandleMorphCommand(const char* args)
-{
- if (!*args)
- return false;
-
- uint16 display_id = (uint16)atoi((char*)args);
-
- Unit *target = getSelectedUnit();
- if(!target)
- target = m_session->GetPlayer();
-
- target->SetDisplayId(display_id);
-
- return true;
-}
-
-//set faction of creature or go
-bool ChatHandler::HandleFactionIdCommand(const char* args)
-{
- if (!*args)
- return false;
-
- uint32 factionId = (uint32) atoi((char*)args);
-
- if (!sFactionTemplateStore.LookupEntry(factionId))
- {
- PSendSysMessage(LANG_WRONG_FACTION, factionId);
- SetSentErrorMessage(true);
- return false;
- }
-
- Creature* pCreature = getSelectedCreature();
-
- if(!pCreature)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- pCreature->setFaction(factionId);
-
- // faction is set in creature_template - not inside creature
-
- // update in memory
- if(CreatureInfo const *cinfo = pCreature->GetCreatureInfo())
- {
- const_cast<CreatureInfo*>(cinfo)->faction_A = factionId;
- const_cast<CreatureInfo*>(cinfo)->faction_H = factionId;
- }
-
- // and DB
- WorldDatabase.PExecuteLog("UPDATE creature_template SET faction_A = '%u', faction_H = '%u' WHERE entry = '%u'", factionId, factionId, pCreature->GetEntry());
-
- return true;
-}
-
-//kick player
-bool ChatHandler::HandleKickPlayerCommand(const char *args)
-{
- char* kickName = strtok((char*)args, " ");
- if (!kickName)
- {
- Player* player = getSelectedPlayer();
-
- if(!player)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(player==m_session->GetPlayer())
- {
- SendSysMessage(LANG_COMMAND_KICKSELF);
- SetSentErrorMessage(true);
- return false;
- }
-
- player->GetSession()->KickPlayer();
- }
- else
- {
- std::string name = kickName;
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(name==m_session->GetPlayer()->GetName())
- {
- SendSysMessage(LANG_COMMAND_KICKSELF);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(sWorld.KickPlayer(name))
- {
- PSendSysMessage(LANG_COMMAND_KICKMESSAGE,name.c_str());
- }
- else
- PSendSysMessage(LANG_COMMAND_KICKNOTFOUNDPLAYER,name.c_str());
- }
-
- return true;
-}
-
-//show info of player
-bool ChatHandler::HandlePInfoCommand(const char* args)
-{
- Player* target = NULL;
- uint64 targetGUID = 0;
-
- char* px = strtok((char*)args, " ");
- char* py = NULL;
-
- std::string name;
-
- if (px)
- {
- name = px;
-
- if(name.empty())
- return false;
-
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- target = objmgr.GetPlayer(name.c_str());
- if (target)
- py = strtok(NULL, " ");
- else
- {
- targetGUID = objmgr.GetPlayerGUIDByName(name);
- if(targetGUID)
- py = strtok(NULL, " ");
- else
- py = px;
- }
- }
-
- if(!target && !targetGUID)
- {
- target = getSelectedPlayer();
- }
-
- if(!target && !targetGUID)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint32 accId = 0;
- uint32 money = 0;
- uint32 total_player_time = 0;
- uint32 level = 0;
- uint32 latency = 0;
-
- // get additional information from Player object
- if(target)
- {
- targetGUID = target->GetGUID();
- name = target->GetName(); // re-read for case getSelectedPlayer() target
- accId = target->GetSession()->GetAccountId();
- money = target->GetMoney();
- total_player_time = target->GetTotalPlayedTime();
- level = target->getLevel();
- latency = target->GetSession()->GetLatency();
- }
- // get additional information from DB
- else
- {
- accId = objmgr.GetPlayerAccountIdByGUID(targetGUID);
- Player plr(m_session); // use current session for temporary load
- plr.MinimalLoadFromDB(NULL, targetGUID);
- money = plr.GetMoney();
- total_player_time = plr.GetTotalPlayedTime();
- level = plr.getLevel();
- }
-
- std::string username = GetMangosString(LANG_ERROR);
- std::string last_ip = GetMangosString(LANG_ERROR);
- uint32 security = 0;
- std::string last_login = GetMangosString(LANG_ERROR);
-
- QueryResult* result = loginDatabase.PQuery("SELECT username,gmlevel,last_ip,last_login FROM account WHERE id = '%u'",accId);
- if(result)
- {
- Field* fields = result->Fetch();
- username = fields[0].GetCppString();
- security = fields[1].GetUInt32();
- if(m_session->GetSecurity() >= security)
- {
- last_ip = fields[2].GetCppString();
- last_login = fields[3].GetCppString();
- }
- else
- {
- last_ip = "-";
- last_login = "-";
- }
-
- delete result;
- }
-
- PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetMangosString(LANG_OFFLINE)), name.c_str(), GUID_LOPART(targetGUID), username.c_str(), accId, security, last_ip.c_str(), last_login.c_str(), latency);
-
- std::string timeStr = secsToTimeString(total_player_time,true,true);
- uint32 gold = money /GOLD;
- uint32 silv = (money % GOLD) / SILVER;
- uint32 copp = (money % GOLD) % SILVER;
- PSendSysMessage(LANG_PINFO_LEVEL, timeStr.c_str(), level, gold,silv,copp );
-
- if ( py && strncmp(py, "rep", 3) == 0 )
- {
- if(!target)
- {
- // rep option not implemented for offline case
- SendSysMessage(LANG_PINFO_NO_REP);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* FactionName;
- for(FactionStateList::const_iterator itr = target->m_factions.begin(); itr != target->m_factions.end(); ++itr)
- {
- FactionEntry const *factionEntry = sFactionStore.LookupEntry(itr->second.ID);
- if (factionEntry)
- FactionName = factionEntry->name[m_session->GetSessionDbcLocale()];
- else
- FactionName = "#Not found#";
- ReputationRank rank = target->GetReputationRank(factionEntry);
- std::string rankName = GetMangosString(ReputationRankStrIndex[rank]);
- std::ostringstream ss;
- ss << itr->second.ID << ": |cffffffff|Hfaction:" << itr->second.ID << "|h[" << FactionName << "]|h|r " << rankName << "|h|r (" << target->GetReputation(factionEntry) << ")";
-
- if(itr->second.Flags & FACTION_FLAG_VISIBLE)
- ss << GetMangosString(LANG_FACTION_VISIBLE);
- if(itr->second.Flags & FACTION_FLAG_AT_WAR)
- ss << GetMangosString(LANG_FACTION_ATWAR);
- if(itr->second.Flags & FACTION_FLAG_PEACE_FORCED)
- ss << GetMangosString(LANG_FACTION_PEACE_FORCED);
- if(itr->second.Flags & FACTION_FLAG_HIDDEN)
- ss << GetMangosString(LANG_FACTION_HIDDEN);
- if(itr->second.Flags & FACTION_FLAG_INVISIBLE_FORCED)
- ss << GetMangosString(LANG_FACTION_INVISIBLE_FORCED);
- if(itr->second.Flags & FACTION_FLAG_INACTIVE)
- ss << GetMangosString(LANG_FACTION_INACTIVE);
-
- SendSysMessage(ss.str().c_str());
- }
- }
- return true;
-}
-
-//show tickets
-void ChatHandler::ShowTicket(uint64 guid, char const* text, char const* time)
-{
- std::string name;
- if(!objmgr.GetPlayerNameByGUID(guid,name))
- name = GetMangosString(LANG_UNKNOWN);
-
- PSendSysMessage(LANG_COMMAND_TICKETVIEW, name.c_str(),time,text);
-}
-
-//ticket commands
-bool ChatHandler::HandleTicketCommand(const char* args)
-{
- char* px = strtok((char*)args, " ");
-
- // ticket<end>
- if (!px)
- {
- size_t count;
- QueryResult *result = CharacterDatabase.Query("SELECT COUNT(ticket_id) FROM character_ticket");
- if(result)
- {
- count = (*result)[0].GetUInt32();
- delete result;
- }
- else
- count = 0;
-
- PSendSysMessage(LANG_COMMAND_TICKETCOUNT, count, m_session->GetPlayer()->isAcceptTickets() ? GetMangosString(LANG_ON) : GetMangosString(LANG_OFF));
- return true;
- }
-
- // ticket on
- if(strncmp(px,"on",3) == 0)
- {
- m_session->GetPlayer()->SetAcceptTicket(true);
- SendSysMessage(LANG_COMMAND_TICKETON);
- return true;
- }
-
- // ticket off
- if(strncmp(px,"off",4) == 0)
- {
- m_session->GetPlayer()->SetAcceptTicket(false);
- SendSysMessage(LANG_COMMAND_TICKETOFF);
- return true;
- }
-
- // ticket #num
- int num = atoi(px);
- if(num > 0)
- {
- QueryResult *result = CharacterDatabase.PQuery("SELECT guid,ticket_text,ticket_lastchange FROM character_ticket ORDER BY ticket_id ASC LIMIT %d,1",num-1);
-
- if(!result)
- {
- PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num);
- delete result;
- SetSentErrorMessage(true);
- return false;
- }
-
- Field* fields = result->Fetch();
-
- uint64 guid = fields[0].GetUInt64();
- char const* text = fields[1].GetString();
- char const* time = fields[2].GetString();
-
- ShowTicket(guid,text,time);
- delete result;
- return true;
- }
-
- std::string name = px;
-
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 guid = objmgr.GetPlayerGUIDByName(name);
-
- if(!guid)
- return false;
-
- // ticket $char_name
- QueryResult *result = CharacterDatabase.PQuery("SELECT ticket_text,ticket_lastchange FROM character_ticket WHERE guid = '%u' ORDER BY ticket_id ASC",GUID_LOPART(guid));
-
- if(!result)
- return false;
-
- Field* fields = result->Fetch();
-
- char const* text = fields[0].GetString();
- char const* time = fields[1].GetString();
-
- ShowTicket(guid,text,time);
- delete result;
-
- return true;
-}
-
-uint32 ChatHandler::GetTicketIDByNum(uint32 num)
-{
- QueryResult *result = CharacterDatabase.Query("SELECT ticket_id FROM character_ticket");
-
- if(!result || num > result->GetRowCount())
- {
- PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num);
- delete result;
- return 0;
- }
-
- for(uint32 i = 1; i < num; ++i)
- result->NextRow();
-
- Field* fields = result->Fetch();
-
- uint32 id = fields[0].GetUInt32();
- delete result;
- return id;
-}
-
-//dell all tickets
-bool ChatHandler::HandleDelTicketCommand(const char *args)
-{
- char* px = strtok((char*)args, " ");
- if (!px)
- return false;
-
- // delticket all
- if(strncmp(px,"all",4) == 0)
- {
- QueryResult *result = CharacterDatabase.Query("SELECT guid FROM character_ticket");
-
- if(!result)
- return true;
-
- // notify players about ticket deleting
- do
- {
- Field* fields = result->Fetch();
-
- uint64 guid = fields[0].GetUInt64();
-
- if(Player* sender = objmgr.GetPlayer(guid))
- sender->GetSession()->SendGMTicketGetTicket(0x0A,0);
-
- }while(result->NextRow());
-
- delete result;
-
- CharacterDatabase.PExecute("DELETE FROM character_ticket");
- SendSysMessage(LANG_COMMAND_ALLTICKETDELETED);
- return true;
- }
-
- int num = (uint32)atoi(px);
-
- // delticket #num
- if(num > 0)
- {
- QueryResult *result = CharacterDatabase.PQuery("SELECT ticket_id,guid FROM character_ticket LIMIT %i",num);
-
- if(!result || uint64(num) > result->GetRowCount())
- {
- PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num);
- delete result;
- SetSentErrorMessage(true);
- return false;
- }
-
- for(int i = 1; i < num; ++i)
- result->NextRow();
-
- Field* fields = result->Fetch();
-
- uint32 id = fields[0].GetUInt32();
- uint64 guid = fields[1].GetUInt64();
- delete result;
-
- CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE ticket_id = '%u'", id);
-
- // notify players about ticket deleting
- if(Player* sender = objmgr.GetPlayer(guid))
- {
- sender->GetSession()->SendGMTicketGetTicket(0x0A,0);
- PSendSysMessage(LANG_COMMAND_TICKETPLAYERDEL,sender->GetName());
- }
- else
- SendSysMessage(LANG_COMMAND_TICKETDEL);
-
- return true;
- }
-
- std::string name = px;
-
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 guid = objmgr.GetPlayerGUIDByName(name);
-
- if(!guid)
- return false;
-
- // delticket $char_name
- CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u'",GUID_LOPART(guid));
-
- // notify players about ticket deleting
- if(Player* sender = objmgr.GetPlayer(guid))
- sender->GetSession()->SendGMTicketGetTicket(0x0A,0);
-
- PSendSysMessage(LANG_COMMAND_TICKETPLAYERDEL,px);
- return true;
-}
-
-//set spawn dist of creature
-bool ChatHandler::HandleSpawnDistCommand(const char* args)
-{
- if(!*args)
- return false;
-
- float option = atof((char*)args);
- if (option < 0.0f)
- {
- SendSysMessage(LANG_BAD_VALUE);
- return false;
- }
-
- MovementGeneratorType mtype = IDLE_MOTION_TYPE;
- if (option >0.0f)
- mtype = RANDOM_MOTION_TYPE;
-
- Creature *pCreature = getSelectedCreature();
- uint32 u_guidlow = 0;
-
- if (pCreature)
- u_guidlow = pCreature->GetDBTableGUIDLow();
- else
- return false;
-
- pCreature->SetRespawnRadius((float)option);
- pCreature->SetDefaultMovementType(mtype);
- pCreature->GetMotionMaster()->Initialize();
- if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
- {
- pCreature->setDeathState(JUST_DIED);
- pCreature->Respawn();
- }
-
- WorldDatabase.PExecuteLog("UPDATE creature SET spawndist=%f, MovementType=%i WHERE guid=%u",option,mtype,u_guidlow);
- PSendSysMessage(LANG_COMMAND_SPAWNDIST,option);
- return true;
-}
-
-bool ChatHandler::HandleSpawnTimeCommand(const char* args)
-{
- if(!*args)
- return false;
-
- char* stime = strtok((char*)args, " ");
-
- if (!stime)
- return false;
-
- int i_stime = atoi((char*)stime);
-
- if (i_stime < 0)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- Creature *pCreature = getSelectedCreature();
- uint32 u_guidlow = 0;
-
- if (pCreature)
- u_guidlow = pCreature->GetDBTableGUIDLow();
- else
- return false;
-
- WorldDatabase.PExecuteLog("UPDATE creature SET spawntimesecs=%i WHERE guid=%u",i_stime,u_guidlow);
- pCreature->SetRespawnDelay((uint32)i_stime);
- PSendSysMessage(LANG_COMMAND_SPAWNTIME,i_stime);
-
- return true;
-}
-
-/**
- * Add a waypoint to a creature.
- *
- * The user can either select an npc or provide its GUID.
- *
- * The user can even select a visual waypoint - then the new waypoint
- * is placed *after* the selected one - this makes insertion of new
- * waypoints possible.
- *
- * eg:
- * .wp add 12345
- * -> adds a waypoint to the npc with the GUID 12345
- *
- * .wp add
- * -> adds a waypoint to the currently selected creature
- *
- *
- * @param args if the user did not provide a GUID, it is NULL
- *
- * @return true - command did succeed, false - something went wrong
- */
-bool ChatHandler::HandleWpAddCommand(const char* args)
-{
- sLog.outDebug("DEBUG: HandleWpAddCommand");
-
- // optional
- char* guid_str = NULL;
-
- if(*args)
- {
- guid_str = strtok((char*)args, " ");
- }
-
- uint32 lowguid = 0;
- uint32 point = 0;
- Creature* target = getSelectedCreature();
- // Did player provide a GUID?
- if (!guid_str)
- {
- sLog.outDebug("DEBUG: HandleWpAddCommand - No GUID provided");
-
- // No GUID provided
- // -> Player must have selected a creature
-
- if(!target)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
- if (target->GetEntry() == VISUAL_WAYPOINT )
- {
- sLog.outDebug("DEBUG: HandleWpAddCommand - target->GetEntry() == VISUAL_WAYPOINT (1) ");
-
- QueryResult *result =
- WorldDatabase.PQuery( "SELECT id, point FROM creature_movement WHERE wpguid = %u",
- target->GetGUIDLow() );
- if(!result)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, target->GetGUIDLow());
- // User selected a visual spawnpoint -> get the NPC
- // Select NPC GUID
- // Since we compare float values, we have to deal with
- // some difficulties.
- // Here we search for all waypoints that only differ in one from 1 thousand
- // (0.001) - There is no other way to compare C++ floats with mySQL floats
- // See also: http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html
- const char* maxDIFF = "0.01";
- result = WorldDatabase.PQuery( "SELECT id, point FROM creature_movement WHERE (abs(position_x - %f) <= %s ) and (abs(position_y - %f) <= %s ) and (abs(position_z - %f) <= %s )",
- target->GetPositionX(), maxDIFF, target->GetPositionY(), maxDIFF, target->GetPositionZ(), maxDIFF);
- if(!result)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, target->GetGUIDLow());
- SetSentErrorMessage(true);
- return false;
- }
- }
- do
- {
- Field *fields = result->Fetch();
- lowguid = fields[0].GetUInt32();
- point = fields[1].GetUInt32();
- }while( result->NextRow() );
- delete result;
-
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- target = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT));
- if(!target)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
- }
- else
- {
- lowguid = target->GetDBTableGUIDLow();
- }
- }
- else
- {
- sLog.outDebug("DEBUG: HandleWpAddCommand - GUID provided");
-
- // GUID provided
- // Warn if player also selected a creature
- // -> Creature selection is ignored <-
- if(target)
- {
- SendSysMessage(LANG_WAYPOINT_CREATSELECTED);
- }
- lowguid = atoi((char*)guid_str);
-
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- target = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT));
- if(!target)
- {
- PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
- }
- // lowguid -> GUID of the NPC
- // point -> number of the waypoint (if not 0)
- sLog.outDebug("DEBUG: HandleWpAddCommand - danach");
-
- sLog.outDebug("DEBUG: HandleWpAddCommand - point == 0");
-
- Player* player = m_session->GetPlayer();
- WaypointMgr.AddLastNode(lowguid, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), 0, 0);
-
- // update movement type
- if(target)
- {
- target->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
- target->GetMotionMaster()->Initialize();
- if(target->isAlive()) // dead creature will reset movement generator at respawn
- {
- target->setDeathState(JUST_DIED);
- target->Respawn();
- }
- target->SaveToDB();
- }
- else
- WorldDatabase.PExecuteLog("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE,lowguid);
-
- PSendSysMessage(LANG_WAYPOINT_ADDED, point, lowguid);
-
- return true;
-} // HandleWpAddCommand
-
-/**
- * .wp modify emote | spell | text | del | move | add
- *
- * add -> add a WP after the selected visual waypoint
- * User must select a visual waypoint and then issue ".wp modify add"
- *
- * emote <emoteID>
- * User has selected a visual waypoint before.
- * <emoteID> is added to this waypoint. Everytime the
- * NPC comes to this waypoint, the emote is called.
- *
- * emote <GUID> <WPNUM> <emoteID>
- * User has not selected visual waypoint before.
- * For the waypoint <WPNUM> for the NPC with <GUID>
- * an emote <emoteID> is added.
- * Everytime the NPC comes to this waypoint, the emote is called.
- *
- *
- * info <GUID> <WPNUM> -> User did not select a visual waypoint and
- */
-bool ChatHandler::HandleWpModifyCommand(const char* args)
-{
- sLog.outDebug("DEBUG: HandleWpModifyCommand");
-
- if(!*args)
- return false;
-
- // first arg: add del text emote spell waittime move
- char* show_str = strtok((char*)args, " ");
- if (!show_str)
- {
- return false;
- }
-
- std::string show = show_str;
- // Check
- // Remember: "show" must also be the name of a column!
- if( (show != "emote") && (show != "spell") && (show != "text1") && (show != "text2")
- && (show != "text3") && (show != "text4") && (show != "text5")
- && (show != "waittime") && (show != "del") && (show != "move") && (show != "add")
- && (show != "model1") && (show != "model2") && (show != "orientation"))
- {
- return false;
- }
-
- // Next arg is: <GUID> <WPNUM> <ARGUMENT>
-
- // Did user provide a GUID
- // or did the user select a creature?
- // -> variable lowguid is filled with the GUID of the NPC
- uint32 lowguid = 0;
- uint32 point = 0;
- uint32 wpGuid = 0;
- Creature* target = getSelectedCreature();
-
- if(target)
- {
- sLog.outDebug("DEBUG: HandleWpModifyCommand - User did select an NPC");
-
- // Did the user select a visual spawnpoint?
- if (target->GetEntry() != VISUAL_WAYPOINT )
- {
- PSendSysMessage(LANG_WAYPOINT_VP_SELECT);
- SetSentErrorMessage(true);
- return false;
- }
-
- wpGuid = target->GetGUIDLow();
-
- // The visual waypoint
- QueryResult *result =
- WorldDatabase.PQuery( "SELECT id, point FROM creature_movement WHERE wpguid = %u LIMIT 1",
- target->GetGUIDLow() );
- if(!result)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, wpGuid);
- SetSentErrorMessage(true);
- return false;
- }
- sLog.outDebug("DEBUG: HandleWpModifyCommand - After getting wpGuid");
-
- Field *fields = result->Fetch();
- lowguid = fields[0].GetUInt32();
- point = fields[1].GetUInt32();
-
- // Cleanup memory
- sLog.outDebug("DEBUG: HandleWpModifyCommand - Cleanup memory");
- delete result;
- }
- else
- {
- // User did provide <GUID> <WPNUM>
-
- char* guid_str = strtok((char*)NULL, " ");
- if( !guid_str )
- {
- SendSysMessage(LANG_WAYPOINT_NOGUID);
- return false;
- }
- lowguid = atoi((char*)guid_str);
-
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage("DEBUG: GUID provided: %d", lowguid);
-
- char* point_str = strtok((char*)NULL, " ");
- if( !point_str )
- {
- SendSysMessage(LANG_WAYPOINT_NOWAYPOINTGIVEN);
- return false;
- }
- point = atoi((char*)point_str);
-
- PSendSysMessage("DEBUG: wpNumber provided: %d", point);
-
- // Now we need the GUID of the visual waypoint
- // -> "del", "move", "add" command
-
- QueryResult *result = WorldDatabase.PQuery( "SELECT wpguid FROM creature_movement WHERE id = '%u' AND point = '%u' LIMIT 1", lowguid, point);
- if (!result)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, lowguid, point);
- SetSentErrorMessage(true);
- return false;
- }
-
- Field *fields = result->Fetch();
- wpGuid = fields[0].GetUInt32();
-
- // Free memory
- delete result;
- }
-
- char* arg_str = NULL;
- // Check for argument
- if( (show.find("text") == std::string::npos ) && (show != "del") && (show != "move") && (show != "add"))
- {
- // Text is enclosed in "<>", all other arguments not
- if( show.find("text") != std::string::npos )
- arg_str = strtok((char*)NULL, "<>");
- else
- arg_str = strtok((char*)NULL, " ");
-
- if( !arg_str)
- {
- PSendSysMessage(LANG_WAYPOINT_ARGUMENTREQ, show_str);
- return false;
- }
- }
-
- sLog.outDebug("DEBUG: HandleWpModifyCommand - Parameters parsed - now execute the command");
-
- // wpGuid -> GUID of the waypoint creature
- // lowguid -> GUID of the NPC
- // point -> waypoint number
-
- // Special functions:
- // add - move - del -> no args commands
- // Add a waypoint after the selected visual
- if(show == "add" && target)
- {
- PSendSysMessage("DEBUG: wp modify add, GUID: %u", lowguid);
-
- // Get the creature for which we read the waypoint
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
-
- if( !npcCreature )
- {
- PSendSysMessage(LANG_WAYPOINT_NPCNOTFOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- sLog.outDebug("DEBUG: HandleWpModifyCommand - add -- npcCreature");
-
- // What to do:
- // Add the visual spawnpoint (DB only)
- // Adjust the waypoints
- // Respawn the owner of the waypoints
- sLog.outDebug("DEBUG: HandleWpModifyCommand - add");
-
- Player* chr = m_session->GetPlayer();
- Map *map = chr->GetMap();
-
- if(npcCreature)
- {
- npcCreature->GetMotionMaster()->Initialize();
- if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
- {
- npcCreature->setDeathState(JUST_DIED);
- npcCreature->Respawn();
- }
- }
-
- // create the waypoint creature
- wpGuid = 0;
- Creature* wpCreature = new Creature;
- if (!wpCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map,VISUAL_WAYPOINT,0))
- {
- PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
- delete wpCreature;
- }
- else
- {
- wpCreature->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation());
-
- if(!wpCreature->IsPositionValid())
- {
- sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature->GetGUIDLow(),wpCreature->GetEntry(),wpCreature->GetPositionX(),wpCreature->GetPositionY());
- delete wpCreature;
- }
- else
- {
- wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
- // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
- wpCreature->LoadFromDB(wpCreature->GetDBTableGUIDLow(), map);
- map->Add(wpCreature);
- wpGuid = wpCreature->GetGUIDLow();
- }
- }
-
- WaypointMgr.AddAfterNode(lowguid, point, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), 0, 0, wpGuid);
-
- if(!wpGuid)
- return false;
-
- PSendSysMessage(LANG_WAYPOINT_ADDED_NO, point+1);
- return true;
- } // add
-
- if(show == "del" && target)
- {
- PSendSysMessage("DEBUG: wp modify del, GUID: %u", lowguid);
-
- // Get the creature for which we read the waypoint
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
-
- // wpCreature
- Creature* wpCreature = NULL;
- if( wpGuid != 0 )
- {
- wpCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT));
- wpCreature->DeleteFromDB();
- wpCreature->CleanupsBeforeDelete();
- wpCreature->AddObjectToRemoveList();
- }
-
- // What to do:
- // Remove the visual spawnpoint
- // Adjust the waypoints
- // Respawn the owner of the waypoints
-
- WaypointMgr.DeleteNode(lowguid, point);
-
- if(npcCreature)
- {
- // Any waypoints left?
- QueryResult *result2 = WorldDatabase.PQuery( "SELECT point FROM creature_movement WHERE id = '%u'",lowguid);
- if(!result2)
- {
- npcCreature->SetDefaultMovementType(RANDOM_MOTION_TYPE);
- }
- else
- {
- npcCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
- delete result2;
- }
- npcCreature->GetMotionMaster()->Initialize();
- if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
- {
- npcCreature->setDeathState(JUST_DIED);
- npcCreature->Respawn();
- }
- npcCreature->SaveToDB();
- }
-
- PSendSysMessage(LANG_WAYPOINT_REMOVED);
- return true;
- } // del
-
- if(show == "move" && target)
- {
- PSendSysMessage("DEBUG: wp move, GUID: %u", lowguid);
-
- Player *chr = m_session->GetPlayer();
- Map *map = chr->GetMap();
- {
- // Get the creature for which we read the waypoint
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
-
- // wpCreature
- Creature* wpCreature = NULL;
- // What to do:
- // Move the visual spawnpoint
- // Respawn the owner of the waypoints
- if( wpGuid != 0 )
- {
- wpCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT));
- wpCreature->DeleteFromDB();
- wpCreature->CleanupsBeforeDelete();
- wpCreature->AddObjectToRemoveList();
- // re-create
- Creature* wpCreature2 = new Creature;
- if (!wpCreature2->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, VISUAL_WAYPOINT, 0))
- {
- PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
- delete wpCreature2;
- return false;
- }
-
- wpCreature2->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation());
-
- if(!wpCreature2->IsPositionValid())
- {
- sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature2->GetGUIDLow(),wpCreature2->GetEntry(),wpCreature2->GetPositionX(),wpCreature2->GetPositionY());
- delete wpCreature2;
- return false;
- }
-
- wpCreature2->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
- // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
- wpCreature2->LoadFromDB(wpCreature2->GetDBTableGUIDLow(), map);
- map->Add(wpCreature2);
- //MapManager::Instance().GetMap(npcCreature->GetMapId())->Add(wpCreature2);
- }
-
- WaypointMgr.SetNodePosition(lowguid, point, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ());
-
- if(npcCreature)
- {
- npcCreature->GetMotionMaster()->Initialize();
- if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
- {
- npcCreature->setDeathState(JUST_DIED);
- npcCreature->Respawn();
- }
- }
- PSendSysMessage(LANG_WAYPOINT_CHANGED);
- }
- return true;
- } // move
-
- // Create creature - npc that has the waypoint
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- WaypointMgr.SetNodeText(lowguid, point, show_str, arg_str);
-
- Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
- if(npcCreature)
- {
- npcCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
- npcCreature->GetMotionMaster()->Initialize();
- if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
- {
- npcCreature->setDeathState(JUST_DIED);
- npcCreature->Respawn();
- }
- }
- PSendSysMessage(LANG_WAYPOINT_CHANGED_NO, show_str);
-
- return true;
-}
-
-/**
- * .wp show info | on | off
- *
- * info -> User has selected a visual waypoint before
- *
- * info <GUID> <WPNUM> -> User did not select a visual waypoint and
- * provided the GUID of the NPC and the number of
- * the waypoint.
- *
- * on -> User has selected an NPC; all visual waypoints for this
- * NPC are added to the world
- *
- * on <GUID> -> User did not select an NPC - instead the GUID of the
- * NPC is provided. All visual waypoints for this NPC
- * are added from the world.
- *
- * off -> User has selected an NPC; all visual waypoints for this
- * NPC are removed from the world.
- *
- * on <GUID> -> User did not select an NPC - instead the GUID of the
- * NPC is provided. All visual waypoints for this NPC
- * are removed from the world.
- *
- *
- */
-bool ChatHandler::HandleWpShowCommand(const char* args)
-{
- sLog.outDebug("DEBUG: HandleWpShowCommand");
-
- if(!*args)
- return false;
-
- // first arg: on, off, first, last
- char* show_str = strtok((char*)args, " ");
- if (!show_str)
- {
- return false;
- }
- // second arg: GUID (optional, if a creature is selected)
- char* guid_str = strtok((char*)NULL, " ");
- sLog.outDebug("DEBUG: HandleWpShowCommand: show_str: %s guid_str: %s", show_str, guid_str);
- //if (!guid_str) {
- // return false;
- //}
-
- // Did user provide a GUID
- // or did the user select a creature?
- // -> variable lowguid is filled with the GUID
- Creature* target = getSelectedCreature();
- // Did player provide a GUID?
- if (!guid_str)
- {
- sLog.outDebug("DEBUG: HandleWpShowCommand: !guid_str");
- // No GUID provided
- // -> Player must have selected a creature
-
- if(!target)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
- }
- else
- {
- sLog.outDebug("DEBUG: HandleWpShowCommand: GUID provided");
- // GUID provided
- // Warn if player also selected a creature
- // -> Creature selection is ignored <-
- if(target)
- {
- SendSysMessage(LANG_WAYPOINT_CREATSELECTED);
- }
-
- uint32 lowguid = atoi((char*)guid_str);
-
- CreatureData const* data = objmgr.GetCreatureData(lowguid);
- if(!data)
- {
- PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- target = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT));
-
- if(!target)
- {
- PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
- }
-
- uint32 lowguid = target->GetDBTableGUIDLow();
-
- std::string show = show_str;
- uint32 Maxpoint;
-
- sLog.outDebug("DEBUG: HandleWpShowCommand: lowguid: %u", lowguid);
-
- sLog.outDebug("DEBUG: HandleWpShowCommand: Habe creature: %ld", target );
-
- sLog.outDebug("DEBUG: HandleWpShowCommand: wpshow - show: %s", show_str);
- //PSendSysMessage("wpshow - show: %s", show);
-
- // Show info for the selected waypoint
- if(show == "info")
- {
- PSendSysMessage("DEBUG: wp info, GUID: %u", lowguid);
-
- // Check if the user did specify a visual waypoint
- if( target->GetEntry() != VISUAL_WAYPOINT )
- {
- PSendSysMessage(LANG_WAYPOINT_VP_SELECT);
- SetSentErrorMessage(true);
- return false;
- }
-
- //PSendSysMessage("wp on, GUID: %u", lowguid);
-
- //pCreature->GetPositionX();
-
- QueryResult *result =
- WorldDatabase.PQuery( "SELECT id, point, waittime, emote, spell, text1, text2, text3, text4, text5, model1, model2 FROM creature_movement WHERE wpguid = %u",
- target->GetGUID() );
- if(!result)
- {
- // Since we compare float values, we have to deal with
- // some difficulties.
- // Here we search for all waypoints that only differ in one from 1 thousand
- // (0.001) - There is no other way to compare C++ floats with mySQL floats
- // See also: http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html
- const char* maxDIFF = "0.01";
- PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, target->GetGUID());
-
- result = WorldDatabase.PQuery( "SELECT id, point, waittime, emote, spell, text1, text2, text3, text4, text5, model1, model2 FROM creature_movement WHERE (abs(position_x - %f) <= %s ) and (abs(position_y - %f) <= %s ) and (abs(position_z - %f) <= %s )",
- target->GetPositionX(), maxDIFF, target->GetPositionY(), maxDIFF, target->GetPositionZ(), maxDIFF);
- if(!result)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
- }
- do
- {
- Field *fields = result->Fetch();
- uint32 creGUID = fields[0].GetUInt32();
- uint32 point = fields[1].GetUInt32();
- int waittime = fields[2].GetUInt32();
- uint32 emote = fields[3].GetUInt32();
- uint32 spell = fields[4].GetUInt32();
- const char * text1 = fields[5].GetString();
- const char * text2 = fields[6].GetString();
- const char * text3 = fields[7].GetString();
- const char * text4 = fields[8].GetString();
- const char * text5 = fields[9].GetString();
- uint32 model1 = fields[10].GetUInt32();
- uint32 model2 = fields[11].GetUInt32();
-
- // Get the creature for which we read the waypoint
- Creature* wpCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(creGUID,VISUAL_WAYPOINT,HIGHGUID_UNIT));
-
- PSendSysMessage(LANG_WAYPOINT_INFO_TITLE, point, (wpCreature ? wpCreature->GetName() : "<not found>"), creGUID);
- PSendSysMessage(LANG_WAYPOINT_INFO_WAITTIME, waittime);
- PSendSysMessage(LANG_WAYPOINT_INFO_MODEL, 1, model1);
- PSendSysMessage(LANG_WAYPOINT_INFO_MODEL, 2, model2);
- PSendSysMessage(LANG_WAYPOINT_INFO_EMOTE, emote);
- PSendSysMessage(LANG_WAYPOINT_INFO_SPELL, spell);
- PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 1, text1);
- PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 2, text2);
- PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 3, text3);
- PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 4, text4);
- PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 5, text5);
-
- }while( result->NextRow() );
- // Cleanup memory
- delete result;
- return true;
- }
-
- if(show == "on")
- {
- PSendSysMessage("DEBUG: wp on, GUID: %u", lowguid);
-
- QueryResult *result = WorldDatabase.PQuery( "SELECT point, position_x,position_y,position_z FROM creature_movement WHERE id = '%u'",lowguid);
- if(!result)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
- // Delete all visuals for this NPC
- QueryResult *result2 = WorldDatabase.PQuery( "SELECT wpguid FROM creature_movement WHERE id = '%u' and wpguid <> 0", lowguid);
- if(result2)
- {
- bool hasError = false;
- do
- {
- Field *fields = result2->Fetch();
- uint32 wpguid = fields[0].GetUInt32();
- Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(wpguid,VISUAL_WAYPOINT,HIGHGUID_UNIT));
-
- if(!pCreature)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, wpguid);
- hasError = true;
- WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", wpguid);
- }
- else
- {
- pCreature->DeleteFromDB();
- pCreature->CleanupsBeforeDelete();
- pCreature->AddObjectToRemoveList();
- }
-
- }while( result2->NextRow() );
- delete result2;
- if( hasError )
- {
- PSendSysMessage(LANG_WAYPOINT_TOOFAR1);
- PSendSysMessage(LANG_WAYPOINT_TOOFAR2);
- PSendSysMessage(LANG_WAYPOINT_TOOFAR3);
- }
- }
-
- do
- {
- Field *fields = result->Fetch();
- uint32 point = fields[0].GetUInt32();
- float x = fields[1].GetFloat();
- float y = fields[2].GetFloat();
- float z = fields[3].GetFloat();
-
- uint32 id = VISUAL_WAYPOINT;
-
- Player *chr = m_session->GetPlayer();
- Map *map = chr->GetMap();
- float o = chr->GetOrientation();
-
- Creature* wpCreature = new Creature;
- if (!wpCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, 0))
- {
- PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
- delete wpCreature;
- delete result;
- return false;
- }
-
- wpCreature->Relocate(x, y, z, o);
-
- if(!wpCreature->IsPositionValid())
- {
- sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature->GetGUIDLow(),wpCreature->GetEntry(),wpCreature->GetPositionX(),wpCreature->GetPositionY());
- delete wpCreature;
- delete result;
- return false;
- }
-
- wpCreature->SetVisibility(VISIBILITY_OFF);
- sLog.outDebug("DEBUG: UPDATE creature_movement SET wpguid = '%u");
- // set "wpguid" column to the visual waypoint
- WorldDatabase.PExecuteLog("UPDATE creature_movement SET wpguid = '%u' WHERE id = '%u' and point = '%u'", wpCreature->GetGUIDLow(), lowguid, point);
-
- wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
- // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
- wpCreature->LoadFromDB(wpCreature->GetDBTableGUIDLow(),map);
- map->Add(wpCreature);
- //MapManager::Instance().GetMap(wpCreature->GetMapId())->Add(wpCreature);
- }while( result->NextRow() );
-
- // Cleanup memory
- delete result;
- return true;
- }
-
- if(show == "first")
- {
- PSendSysMessage("DEBUG: wp first, GUID: %u", lowguid);
-
- QueryResult *result = WorldDatabase.PQuery( "SELECT position_x,position_y,position_z FROM creature_movement WHERE point='1' AND id = '%u'",lowguid);
- if(!result)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTFOUND, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
-
- Field *fields = result->Fetch();
- float x = fields[0].GetFloat();
- float y = fields[1].GetFloat();
- float z = fields[2].GetFloat();
- uint32 id = VISUAL_WAYPOINT;
-
- Player *chr = m_session->GetPlayer();
- float o = chr->GetOrientation();
- Map *map = chr->GetMap();
-
- Creature* pCreature = new Creature;
- if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT),map, id, 0))
- {
- PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
- delete pCreature;
- delete result;
- return false;
- }
-
- pCreature->Relocate(x, y, z, o);
-
- if(!pCreature->IsPositionValid())
- {
- sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
- delete pCreature;
- delete result;
- return false;
- }
-
- pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
- pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map);
- map->Add(pCreature);
- //player->PlayerTalkClass->SendPointOfInterest(x, y, 6, 6, 0, "First Waypoint");
-
- // Cleanup memory
- delete result;
- return true;
- }
-
- if(show == "last")
- {
- PSendSysMessage("DEBUG: wp last, GUID: %u", lowguid);
-
- QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'",lowguid);
- if( result )
- {
- Maxpoint = (*result)[0].GetUInt32();
-
- delete result;
- }
- else
- Maxpoint = 0;
-
- result = WorldDatabase.PQuery( "SELECT position_x,position_y,position_z FROM creature_movement WHERE point ='%u' AND id = '%u'",Maxpoint, lowguid);
- if(!result)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTFOUNDLAST, lowguid);
- SetSentErrorMessage(true);
- return false;
- }
- Field *fields = result->Fetch();
- float x = fields[0].GetFloat();
- float y = fields[1].GetFloat();
- float z = fields[2].GetFloat();
- uint32 id = VISUAL_WAYPOINT;
-
- Player *chr = m_session->GetPlayer();
- float o = chr->GetOrientation();
- Map *map = chr->GetMap();
-
- Creature* pCreature = new Creature;
- if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, 0))
- {
- PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id);
- delete pCreature;
- delete result;
- return false;
- }
-
- pCreature->Relocate(x, y, z, o);
-
- if(!pCreature->IsPositionValid())
- {
- sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
- delete pCreature;
- delete result;
- return false;
- }
-
- pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
- pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map);
- map->Add(pCreature);
- //player->PlayerTalkClass->SendPointOfInterest(x, y, 6, 6, 0, "Last Waypoint");
- // Cleanup memory
- delete result;
- return true;
- }
-
- if(show == "off")
- {
- QueryResult *result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE id = '%d'", VISUAL_WAYPOINT);
- if(!result)
- {
- SendSysMessage(LANG_WAYPOINT_VP_NOTFOUND);
- SetSentErrorMessage(true);
- return false;
- }
- bool hasError = false;
- do
- {
- Field *fields = result->Fetch();
- uint32 guid = fields[0].GetUInt32();
- Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(guid,VISUAL_WAYPOINT,HIGHGUID_UNIT));
-
- //Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
-
- if(!pCreature)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid);
- hasError = true;
- WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", guid);
- }
- else
- {
- pCreature->DeleteFromDB();
- pCreature->CleanupsBeforeDelete();
- pCreature->AddObjectToRemoveList();
- }
- }while(result->NextRow());
- // set "wpguid" column to "empty" - no visual waypoint spawned
- WorldDatabase.PExecuteLog("UPDATE creature_movement SET wpguid = '0'");
-
- if( hasError )
- {
- PSendSysMessage(LANG_WAYPOINT_TOOFAR1);
- PSendSysMessage(LANG_WAYPOINT_TOOFAR2);
- PSendSysMessage(LANG_WAYPOINT_TOOFAR3);
- }
-
- SendSysMessage(LANG_WAYPOINT_VP_ALLREMOVED);
- // Cleanup memory
- delete result;
-
- return true;
- }
-
- PSendSysMessage("DEBUG: wpshow - no valid command found");
-
- return true;
-} // HandleWpShowCommand
-
-bool ChatHandler::HandleWpExportCommand(const char *args)
-{
- if(!*args)
- return false;
-
- // Next arg is: <GUID> <ARGUMENT>
-
- // Did user provide a GUID
- // or did the user select a creature?
- // -> variable lowguid is filled with the GUID of the NPC
- uint32 lowguid = 0;
- Creature* target = getSelectedCreature();
- char* arg_str = NULL;
- if (target)
- {
- if (target->GetEntry() != VISUAL_WAYPOINT)
- lowguid = target->GetGUIDLow();
- else
- {
- QueryResult *result = WorldDatabase.PQuery( "SELECT id FROM creature_movement WHERE wpguid = %u LIMIT 1", target->GetGUIDLow() );
- if (!result)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, target->GetGUIDLow());
- return true;
- }
- Field *fields = result->Fetch();
- lowguid = fields[0].GetUInt32();;
- delete result;
- }
-
- arg_str = strtok((char*)args, " ");
- }
- else
- {
- // user provided <GUID>
- char* guid_str = strtok((char*)args, " ");
- if( !guid_str )
- {
- SendSysMessage(LANG_WAYPOINT_NOGUID);
- return false;
- }
- lowguid = atoi((char*)guid_str);
-
- arg_str = strtok((char*)NULL, " ");
- }
-
- if( !arg_str)
- {
- PSendSysMessage(LANG_WAYPOINT_ARGUMENTREQ, "export");
- return false;
- }
-
- PSendSysMessage("DEBUG: wp export, GUID: %u", lowguid);
-
- QueryResult *result = WorldDatabase.PQuery(
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- "SELECT point, position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5, id FROM creature_movement WHERE id = '%u' ORDER BY point", lowguid );
-
- if (!result)
- {
- PSendSysMessage(LANG_WAYPOINT_NOTHINGTOEXPORT);
- SetSentErrorMessage(true);
- return false;
- }
-
- std::ofstream outfile;
- outfile.open (arg_str);
-
- do
- {
- Field *fields = result->Fetch();
-
- outfile << "INSERT INTO creature_movement ";
- outfile << "( id, point, position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5 ) VALUES ";
-
- outfile << "( ";
- outfile << fields[15].GetUInt32(); // id
- outfile << ", ";
- outfile << fields[0].GetUInt32(); // point
- outfile << ", ";
- outfile << fields[1].GetFloat(); // position_x
- outfile << ", ";
- outfile << fields[2].GetFloat(); // position_y
- outfile << ", ";
- outfile << fields[3].GetUInt32(); // position_z
- outfile << ", ";
- outfile << fields[4].GetUInt32(); // orientation
- outfile << ", ";
- outfile << fields[5].GetUInt32(); // model1
- outfile << ", ";
- outfile << fields[6].GetUInt32(); // model2
- outfile << ", ";
- outfile << fields[7].GetUInt16(); // waittime
- outfile << ", ";
- outfile << fields[8].GetUInt32(); // emote
- outfile << ", ";
- outfile << fields[9].GetUInt32(); // spell
- outfile << ", ";
- const char *tmpChar = fields[10].GetString();
- if( !tmpChar )
- {
- outfile << "NULL"; // text1
- }
- else
- {
- outfile << "'";
- outfile << tmpChar; // text1
- outfile << "'";
- }
- outfile << ", ";
- tmpChar = fields[11].GetString();
- if( !tmpChar )
- {
- outfile << "NULL"; // text2
- }
- else
- {
- outfile << "'";
- outfile << tmpChar; // text2
- outfile << "'";
- }
- outfile << ", ";
- tmpChar = fields[12].GetString();
- if( !tmpChar )
- {
- outfile << "NULL"; // text3
- }
- else
- {
- outfile << "'";
- outfile << tmpChar; // text3
- outfile << "'";
- }
- outfile << ", ";
- tmpChar = fields[13].GetString();
- if( !tmpChar )
- {
- outfile << "NULL"; // text4
- }
- else
- {
- outfile << "'";
- outfile << tmpChar; // text4
- outfile << "'";
- }
- outfile << ", ";
- tmpChar = fields[14].GetString();
- if( !tmpChar )
- {
- outfile << "NULL"; // text5
- }
- else
- {
- outfile << "'";
- outfile << tmpChar; // text5
- outfile << "'";
- }
- outfile << ");\n ";
-
- } while( result->NextRow() );
- delete result;
-
- PSendSysMessage(LANG_WAYPOINT_EXPORTED);
- outfile.close();
-
- return true;
-}
-
-bool ChatHandler::HandleWpImportCommand(const char *args)
-{
- if(!*args)
- return false;
-
- char* arg_str = strtok((char*)args, " ");
- if (!arg_str)
- return false;
-
- std::string line;
- std::ifstream infile (arg_str);
- if (infile.is_open())
- {
- while (! infile.eof() )
- {
- getline (infile,line);
- //cout << line << endl;
- QueryResult *result = WorldDatabase.PQuery(line.c_str());
- delete result;
- }
- infile.close();
- }
- PSendSysMessage(LANG_WAYPOINT_IMPORTED);
-
- return true;
-}
-
-//rename characters
-bool ChatHandler::HandleRenameCommand(const char* args)
-{
- Player* target = NULL;
- uint64 targetGUID = 0;
- std::string oldname;
-
- char* px = strtok((char*)args, " ");
-
- if(px)
- {
- oldname = px;
-
- if(!normalizePlayerName(oldname))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- target = objmgr.GetPlayer(oldname.c_str());
-
- if (!target)
- targetGUID = objmgr.GetPlayerGUIDByName(oldname);
- }
-
- if(!target && !targetGUID)
- {
- target = getSelectedPlayer();
- }
-
- if(!target && !targetGUID)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(target)
- {
- PSendSysMessage(LANG_RENAME_PLAYER, target->GetName());
- target->SetAtLoginFlag(AT_LOGIN_RENAME);
- CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '1' WHERE guid = '%u'", target->GetGUIDLow());
- }
- else
- {
- PSendSysMessage(LANG_RENAME_PLAYER_GUID, oldname.c_str(), GUID_LOPART(targetGUID));
- CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '1' WHERE guid = '%u'", GUID_LOPART(targetGUID));
- }
-
- return true;
-}
-
-//spawn go
-bool ChatHandler::HandleGameObjectCommand(const char* args)
-{
- if (!*args)
- return false;
-
- char* pParam1 = strtok((char*)args, " ");
- if (!pParam1)
- return false;
-
- uint32 id = atoi((char*)pParam1);
- if(!id)
- return false;
-
- char* spawntimeSecs = strtok(NULL, " ");
-
- const GameObjectInfo *goI = objmgr.GetGameObjectInfo(id);
-
- if (!goI)
- {
- PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player *chr = m_session->GetPlayer();
- float x = float(chr->GetPositionX());
- float y = float(chr->GetPositionY());
- float z = float(chr->GetPositionZ());
- float o = float(chr->GetOrientation());
- Map *map = chr->GetMap();
-
- float rot2 = sin(o/2);
- float rot3 = cos(o/2);
-
- GameObject* pGameObj = new GameObject;
- uint32 db_lowGUID = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
-
- if(!pGameObj->Create(db_lowGUID, goI->id, map, x, y, z, o, 0, 0, rot2, rot3, 0, 1))
- {
- delete pGameObj;
- return false;
- }
-
- if( spawntimeSecs )
- {
- uint32 value = atoi((char*)spawntimeSecs);
- pGameObj->SetRespawnTime(value);
- //sLog.outDebug("*** spawntimeSecs: %d", value);
- }
-
- // fill the gameobject data and save to the db
- pGameObj->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
-
- // this will generate a new guid if the object is in an instance
- if(!pGameObj->LoadFromDB(db_lowGUID, map))
- {
- delete pGameObj;
- return false;
- }
-
- sLog.outDebug(GetMangosString(LANG_GAMEOBJECT_CURRENT), goI->name, db_lowGUID, x, y, z, o);
-
- map->Add(pGameObj);
-
- // TODO: is it really necessary to add both the real and DB table guid here ?
- objmgr.AddGameobjectToGrid(db_lowGUID, objmgr.GetGOData(db_lowGUID));
-
- PSendSysMessage(LANG_GAMEOBJECT_ADD,id,goI->name,db_lowGUID,x,y,z);
- return true;
-}
-
-//show animation
-bool ChatHandler::HandleAnimCommand(const char* args)
-{
- if (!*args)
- return false;
-
- uint32 anim_id = atoi((char*)args);
- m_session->GetPlayer()->HandleEmoteCommand(anim_id);
- return true;
-}
-
-//change standstate
-bool ChatHandler::HandleStandStateCommand(const char* args)
-{
- if (!*args)
- return false;
-
- uint32 anim_id = atoi((char*)args);
- m_session->GetPlayer( )->SetUInt32Value( UNIT_NPC_EMOTESTATE , anim_id );
-
- return true;
-}
-
-bool ChatHandler::HandleAddHonorCommand(const char* args)
-{
- if (!*args)
- return false;
-
- Player *target = getSelectedPlayer();
- if(!target)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint32 amount = (uint32)atoi(args);
- target->RewardHonor(NULL, 1, amount);
- return true;
-}
-
-bool ChatHandler::HandleHonorAddKillCommand(const char* /*args*/)
-{
- Unit *target = getSelectedUnit();
- if(!target)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- m_session->GetPlayer()->RewardHonor(target, 1);
- return true;
-}
-
-bool ChatHandler::HandleUpdateHonorFieldsCommand(const char* /*args*/)
-{
- Player *target = getSelectedPlayer();
- if(!target)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- target->UpdateHonorFields();
- return true;
-}
-
-bool ChatHandler::HandleLookupEventCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string namepart = args;
- std::wstring wnamepart;
-
- // converting string that we try to find to lower case
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- wstrToLower(wnamepart);
-
- uint32 counter = 0;
-
- GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
- GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
-
- for(uint32 id = 0; id < events.size(); ++id )
- {
- GameEventData const& eventData = events[id];
-
- std::string descr = eventData.description;
- if(descr.empty())
- continue;
-
- if (Utf8FitTo(descr, wnamepart))
- {
- char const* active = activeEvents.find(id) != activeEvents.end() ? GetMangosString(LANG_ACTIVE) : "";
- PSendSysMessage(LANG_EVENT_ENTRY_LIST,id,id,descr.c_str(),active );
- ++counter;
- }
- }
-
- if (counter==0)
- SendSysMessage(LANG_NOEVENTFOUND);
-
- return true;
-}
-
-bool ChatHandler::HandleEventActiveListCommand(const char* args)
-{
- uint32 counter = 0;
-
- GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
- GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
-
- char const* active = GetMangosString(LANG_ACTIVE);
-
- for(GameEvent::ActiveEvents::const_iterator itr = activeEvents.begin(); itr != activeEvents.end(); ++itr )
- {
- uint32 event_id = *itr;
- GameEventData const& eventData = events[event_id];
-
- PSendSysMessage(LANG_EVENT_ENTRY_LIST,event_id,event_id,eventData.description.c_str(),active );
- ++counter;
- }
-
- if (counter==0)
- SendSysMessage(LANG_NOEVENTFOUND);
-
- return true;
-}
-
-bool ChatHandler::HandleEventInfoCommand(const char* args)
-{
- if(!*args)
- return false;
-
- // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hgameevent");
- if(!cId)
- return false;
-
- uint32 event_id = atoi(cId);
-
- GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
-
- if(event_id >=events.size())
- {
- SendSysMessage(LANG_EVENT_NOT_EXIST);
- SetSentErrorMessage(true);
- return false;
- }
-
- GameEventData const& eventData = events[event_id];
- if(!eventData.isValid())
- {
- SendSysMessage(LANG_EVENT_NOT_EXIST);
- SetSentErrorMessage(true);
- return false;
- }
-
- GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
- bool active = activeEvents.find(event_id) != activeEvents.end();
- char const* activeStr = active ? GetMangosString(LANG_ACTIVE) : "";
-
- std::string startTimeStr = TimeToTimestampStr(eventData.start);
- std::string endTimeStr = TimeToTimestampStr(eventData.end);
-
- uint32 delay = gameeventmgr.NextCheck(event_id);
- time_t nextTime = time(NULL)+delay;
- std::string nextStr = nextTime >= eventData.start && nextTime < eventData.end ? TimeToTimestampStr(time(NULL)+delay) : "-";
-
- std::string occurenceStr = secsToTimeString(eventData.occurence * MINUTE);
- std::string lengthStr = secsToTimeString(eventData.length * MINUTE);
-
- PSendSysMessage(LANG_EVENT_INFO,event_id,eventData.description.c_str(),activeStr,
- startTimeStr.c_str(),endTimeStr.c_str(),occurenceStr.c_str(),lengthStr.c_str(),
- nextStr.c_str());
- return true;
-}
-
-bool ChatHandler::HandleEventStartCommand(const char* args)
-{
- if(!*args)
- return false;
-
- // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hgameevent");
- if(!cId)
- return false;
-
- int32 event_id = atoi(cId);
-
- GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
-
- if(event_id < 1 || event_id >=events.size())
- {
- SendSysMessage(LANG_EVENT_NOT_EXIST);
- SetSentErrorMessage(true);
- return false;
- }
-
- GameEventData const& eventData = events[event_id];
- if(!eventData.isValid())
- {
- SendSysMessage(LANG_EVENT_NOT_EXIST);
- SetSentErrorMessage(true);
- return false;
- }
-
- GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
- if(activeEvents.find(event_id) != activeEvents.end())
- {
- PSendSysMessage(LANG_EVENT_ALREADY_ACTIVE,event_id);
- SetSentErrorMessage(true);
- return false;
- }
-
- gameeventmgr.StartEvent(event_id,true);
- return true;
-}
-
-bool ChatHandler::HandleEventStopCommand(const char* args)
-{
- if(!*args)
- return false;
-
- // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hgameevent");
- if(!cId)
- return false;
-
- int32 event_id = atoi(cId);
-
- GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
-
- if(event_id < 1 || event_id >=events.size())
- {
- SendSysMessage(LANG_EVENT_NOT_EXIST);
- SetSentErrorMessage(true);
- return false;
- }
-
- GameEventData const& eventData = events[event_id];
- if(!eventData.isValid())
- {
- SendSysMessage(LANG_EVENT_NOT_EXIST);
- SetSentErrorMessage(true);
- return false;
- }
-
- GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
-
- if(activeEvents.find(event_id) == activeEvents.end())
- {
- PSendSysMessage(LANG_EVENT_NOT_ACTIVE,event_id);
- SetSentErrorMessage(true);
- return false;
- }
-
- gameeventmgr.StopEvent(event_id,true);
- return true;
-}
-
-bool ChatHandler::HandleCombatStopCommand(const char* args)
-{
- Player *player;
-
- if(*args)
- {
- std::string playername = args;
-
- if(!normalizePlayerName(playername))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- player = objmgr.GetPlayer(playername.c_str());
-
- if(!player)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
- }
- else
- {
- player = getSelectedPlayer();
-
- if (!player)
- player = m_session->GetPlayer();
- }
-
- player->CombatStop();
- player->getHostilRefManager().deleteReferences();
- return true;
-}
-
-bool ChatHandler::HandleLearnAllCraftsCommand(const char* /*args*/)
-{
- uint32 classmask = m_session->GetPlayer()->getClassMask();
-
- for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
- {
- SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(i);
- if( !skillInfo )
- continue;
-
- if( skillInfo->categoryId == SKILL_CATEGORY_PROFESSION || skillInfo->categoryId == SKILL_CATEGORY_SECONDARY )
- {
- for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
- {
- SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j);
- if( !skillLine )
- continue;
-
- // skip racial skills
- if( skillLine->racemask != 0 )
- continue;
-
- // skip wrong class skills
- if( skillLine->classmask && (skillLine->classmask & classmask) == 0)
- continue;
-
- if( skillLine->skillId != i || skillLine->forward_spellid )
- continue;
-
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId);
- if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
- continue;
-
- m_session->GetPlayer()->learnSpell(skillLine->spellId);
- }
- }
- }
-
- SendSysMessage(LANG_COMMAND_LEARN_ALL_CRAFT);
- return true;
-}
-
-bool ChatHandler::HandleLearnAllRecipesCommand(const char* args)
-{
- // Learns all recipes of specified profession and sets skill to max
- // Example: .learn all_recipes enchanting
-
- Player* target = getSelectedPlayer();
- if( !target )
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- return false;
- }
-
- if(!*args)
- return false;
-
- std::wstring wnamepart;
-
- if(!Utf8toWStr(args,wnamepart))
- return false;
-
- uint32 counter = 0; // Counter for figure out that we found smth.
-
- // converting string that we try to find to lower case
- wstrToLower( wnamepart );
-
- uint32 classmask = m_session->GetPlayer()->getClassMask();
-
- for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
- {
- SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(i);
- if( !skillInfo )
- continue;
-
- if( skillInfo->categoryId != SKILL_CATEGORY_PROFESSION &&
- skillInfo->categoryId != SKILL_CATEGORY_SECONDARY )
- continue;
-
- int loc = m_session->GetSessionDbcLocale();
- std::string name = skillInfo->name[loc];
-
- if(Utf8FitTo(name, wnamepart))
- {
- for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
- {
- SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j);
- if( !skillLine )
- continue;
-
- if( skillLine->skillId != i || skillLine->forward_spellid )
- continue;
-
- // skip racial skills
- if( skillLine->racemask != 0 )
- continue;
-
- // skip wrong class skills
- if( skillLine->classmask && (skillLine->classmask & classmask) == 0)
- continue;
-
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId);
- if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
- continue;
-
- if( !target->HasSpell(spellInfo->Id) )
- m_session->GetPlayer()->learnSpell(skillLine->spellId);
- }
-
- uint16 maxLevel = target->GetPureMaxSkillValue(skillInfo->id);
- target->SetSkill(skillInfo->id, maxLevel, maxLevel);
- PSendSysMessage(LANG_COMMAND_LEARN_ALL_RECIPES, name.c_str());
- return true;
- }
- }
-
- return false;
-}
-
-bool ChatHandler::HandleLookupPlayerIpCommand(const char* args)
-{
-
- if(!*args)
- return false;
-
- std::string ip = strtok((char*)args, " ");
- char* limit_str = strtok(NULL, " ");
- int32 limit = limit_str ? atoi(limit_str) : -1;
-
- loginDatabase.escape_string(ip);
-
- QueryResult* result = loginDatabase.PQuery("SELECT id,username FROM account WHERE last_ip = '%s'", ip.c_str());
-
- return LookupPlayerSearchCommand(result,limit);
-}
-
-bool ChatHandler::HandleLookupPlayerAccountCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string account = strtok((char*)args, " ");
- char* limit_str = strtok(NULL, " ");
- int32 limit = limit_str ? atoi(limit_str) : -1;
-
- if(!AccountMgr::normilizeString(account))
- return false;
-
- loginDatabase.escape_string(account);
-
- QueryResult* result = loginDatabase.PQuery("SELECT id,username FROM account WHERE username = '%s'", account.c_str());
-
- return LookupPlayerSearchCommand(result,limit);
-}
-
-bool ChatHandler::HandleLookupPlayerEmailCommand(const char* args)
-{
-
- if(!*args)
- return false;
-
- std::string email = strtok((char*)args, " ");
- char* limit_str = strtok(NULL, " ");
- int32 limit = limit_str ? atoi(limit_str) : -1;
-
- loginDatabase.escape_string(email);
-
- QueryResult* result = loginDatabase.PQuery("SELECT id,username FROM account WHERE email = '%s'", email.c_str());
-
- return LookupPlayerSearchCommand(result,limit);
-}
-
-bool ChatHandler::LookupPlayerSearchCommand(QueryResult* result, int32 limit)
-{
- if(!result)
- {
- PSendSysMessage(LANG_NO_PLAYERS_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- int i =0;
- do
- {
- Field* fields = result->Fetch();
- uint32 acc_id = fields[0].GetUInt32();
- std::string acc_name = fields[1].GetCppString();
-
- QueryResult* chars = CharacterDatabase.PQuery("SELECT guid,name FROM characters WHERE account = '%u'", acc_id);
- if(chars)
- {
- PSendSysMessage(LANG_LOOKUP_PLAYER_ACCOUNT,acc_name.c_str(),acc_id);
-
- uint64 guid = 0;
- std::string name;
-
- do
- {
- Field* charfields = chars->Fetch();
- guid = charfields[0].GetUInt64();
- name = charfields[1].GetCppString();
-
- PSendSysMessage(LANG_LOOKUP_PLAYER_CHARACTER,name.c_str(),guid);
- ++i;
-
- } while( chars->NextRow() && ( limit == -1 || i < limit ) );
-
- delete chars;
- }
- } while(result->NextRow());
-
- delete result;
-
- 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Item.h"
+#include "GameObject.h"
+#include "Opcodes.h"
+#include "Chat.h"
+#include "ObjectAccessor.h"
+#include "MapManager.h"
+#include "Language.h"
+#include "World.h"
+#include "GameEvent.h"
+#include "SpellMgr.h"
+#include "AccountMgr.h"
+#include "WaypointManager.h"
+#include "Util.h"
+#include <cctype>
+#include <iostream>
+#include <fstream>
+#include <map>
+
+static uint32 ReputationRankStrIndex[MAX_REPUTATION_RANK] =
+{
+ LANG_REP_HATED, LANG_REP_HOSTILE, LANG_REP_UNFRIENDLY, LANG_REP_NEUTRAL,
+ LANG_REP_FRIENDLY, LANG_REP_HONORED, LANG_REP_REVERED, LANG_REP_EXALTED
+};
+
+//mute player for some times
+bool ChatHandler::HandleMuteCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ char *charname = strtok((char*)args, " ");
+ if (!charname)
+ return false;
+
+ std::string cname = charname;
+
+ char *timetonotspeak = strtok(NULL, " ");
+ if(!timetonotspeak)
+ return false;
+
+ uint32 notspeaktime = (uint32) atoi(timetonotspeak);
+
+ if(!normalizePlayerName(cname))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(cname.c_str());
+ if(!guid)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = objmgr.GetPlayer(guid);
+
+ // check security
+ uint32 account_id = 0;
+ uint32 security = 0;
+
+ if (chr)
+ {
+ account_id = chr->GetSession()->GetAccountId();
+ security = chr->GetSession()->GetSecurity();
+ }
+ else
+ {
+ account_id = objmgr.GetPlayerAccountIdByGUID(guid);
+ security = objmgr.GetSecurityByAccount(account_id);
+ }
+
+ if(security >= m_session->GetSecurity())
+ {
+ SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ time_t mutetime = time(NULL) + notspeaktime*60;
+
+ if (chr)
+ chr->GetSession()->m_muteTime = mutetime;
+
+ loginDatabase.PExecute("UPDATE account SET mutetime = " I64FMTD " WHERE id = '%u'",uint64(mutetime), account_id );
+
+ if(chr)
+ ChatHandler(chr).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notspeaktime);
+
+ PSendSysMessage(LANG_YOU_DISABLE_CHAT, cname.c_str(), notspeaktime);
+
+ return true;
+}
+
+//unmute player
+bool ChatHandler::HandleUnmuteCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ char *charname = strtok((char*)args, " ");
+ if (!charname)
+ return false;
+
+ std::string cname = charname;
+
+ if(!normalizePlayerName(cname))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(cname.c_str());
+ if(!guid)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = objmgr.GetPlayer(guid);
+
+ // check security
+ uint32 account_id = 0;
+ uint32 security = 0;
+
+ if (chr)
+ {
+ account_id = chr->GetSession()->GetAccountId();
+ security = chr->GetSession()->GetSecurity();
+ }
+ else
+ {
+ account_id = objmgr.GetPlayerAccountIdByGUID(guid);
+ security = objmgr.GetSecurityByAccount(account_id);
+ }
+
+ if(security >= m_session->GetSecurity())
+ {
+ SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (chr)
+ {
+ if(chr->CanSpeak())
+ {
+ SendSysMessage(LANG_CHAT_ALREADY_ENABLED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ chr->GetSession()->m_muteTime = 0;
+ }
+
+ loginDatabase.PExecute("UPDATE account SET mutetime = '0' WHERE id = '%u'", account_id );
+
+ if(chr)
+ ChatHandler(chr).PSendSysMessage(LANG_YOUR_CHAT_ENABLED);
+
+ PSendSysMessage(LANG_YOU_ENABLE_CHAT, cname.c_str());
+ return true;
+}
+
+bool ChatHandler::HandleTargetObjectCommand(const char* args)
+{
+ Player* pl = m_session->GetPlayer();
+ QueryResult *result;
+ GameEvent::ActiveEvents const& activeEventsList = gameeventmgr.GetActiveEventList();
+ if(*args)
+ {
+ int32 id = atoi((char*)args);
+ if(id)
+ result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE map = '%i' AND id = '%u' ORDER BY order_ ASC LIMIT 1",
+ pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),id);
+ else
+ {
+ std::string name = args;
+ WorldDatabase.escape_string(name);
+ result = WorldDatabase.PQuery(
+ "SELECT guid, id, position_x, position_y, position_z, orientation, map, (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ "
+ "FROM gameobject,gameobject_template WHERE gameobject_template.entry = gameobject.id AND map = %i AND name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'")" ORDER BY order_ ASC LIMIT 1",
+ pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), pl->GetMapId(),name.c_str());
+ }
+ }
+ else
+ {
+ std::ostringstream eventFilter;
+ eventFilter << " AND (event IS NULL ";
+ bool initString = true;
+
+ for (GameEvent::ActiveEvents::const_iterator itr = activeEventsList.begin(); itr != activeEventsList.end(); ++itr)
+ {
+ if (initString)
+ {
+ eventFilter << "OR event IN (" <<*itr;
+ initString =false;
+ }
+ else
+ eventFilter << "," << *itr;
+ }
+
+ if (!initString)
+ eventFilter << "))";
+ else
+ eventFilter << ")";
+
+ result = WorldDatabase.PQuery("SELECT gameobject.guid, id, position_x, position_y, position_z, orientation, map, "
+ "(POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ FROM gameobject "
+ "LEFT OUTER JOIN game_event_gameobject on gameobject.guid=game_event_gameobject.guid WHERE map = '%i' %s ORDER BY order_ ASC LIMIT 1",
+ m_session->GetPlayer()->GetPositionX(), m_session->GetPlayer()->GetPositionY(), m_session->GetPlayer()->GetPositionZ(), m_session->GetPlayer()->GetMapId(),eventFilter.str().c_str());
+ }
+
+ if (!result)
+ {
+ SendSysMessage(LANG_COMMAND_TARGETOBJNOTFOUND);
+ return true;
+ }
+
+ Field *fields = result->Fetch();
+ uint32 lowguid = fields[0].GetUInt32();
+ uint32 id = fields[1].GetUInt32();
+ float x = fields[2].GetFloat();
+ float y = fields[3].GetFloat();
+ float z = fields[4].GetFloat();
+ float o = fields[5].GetFloat();
+ int mapid = fields[6].GetUInt16();
+ delete result;
+
+ GameObjectInfo const* goI = objmgr.GetGameObjectInfo(id);
+
+ if (!goI)
+ {
+ PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id);
+ return false;
+ }
+
+ GameObject* target = ObjectAccessor::GetGameObject(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,id,HIGHGUID_GAMEOBJECT));
+
+ PSendSysMessage(LANG_GAMEOBJECT_DETAIL, lowguid, goI->name, lowguid, id, x, y, z, mapid, o);
+
+ if(target)
+ {
+ int32 curRespawnDelay = target->GetRespawnTimeEx()-time(NULL);
+ if(curRespawnDelay < 0)
+ curRespawnDelay = 0;
+
+ std::string curRespawnDelayStr = secsToTimeString(curRespawnDelay,true);
+ std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(),true);
+
+ PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(),curRespawnDelayStr.c_str());
+ }
+ return true;
+}
+
+//teleport to gameobject
+bool ChatHandler::HandleGoObjectCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player* _player = m_session->GetPlayer();
+
+ // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameobject");
+ if(!cId)
+ return false;
+
+ int32 guid = atoi(cId);
+ if(!guid)
+ return false;
+
+ float x, y, z, ort;
+ int mapid;
+
+ // by DB guid
+ if (GameObjectData const* go_data = objmgr.GetGOData(guid))
+ {
+ x = go_data->posX;
+ y = go_data->posY;
+ z = go_data->posZ;
+ ort = go_data->orientation;
+ mapid = go_data->mapid;
+ }
+ else
+ {
+ SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y,z,ort))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(mapid, x, y, z, ort);
+ return true;
+}
+
+bool ChatHandler::HandleGoTriggerCommand(const char* args)
+{
+ Player* _player = m_session->GetPlayer();
+
+ if (!*args)
+ return false;
+
+ char *atId = strtok((char*)args, " ");
+ if (!atId)
+ return false;
+
+ int32 i_atId = atoi(atId);
+
+ if(!i_atId)
+ return false;
+
+ AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(i_atId);
+ if (!at)
+ {
+ PSendSysMessage(LANG_COMMAND_GOAREATRNOTFOUND,i_atId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!MapManager::IsValidMapCoord(at->mapid,at->x,at->y,at->z))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,at->x,at->y,at->mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(at->mapid, at->x, at->y, at->z, _player->GetOrientation());
+ return true;
+}
+
+bool ChatHandler::HandleGoGraveyardCommand(const char* args)
+{
+ Player* _player = m_session->GetPlayer();
+
+ if (!*args)
+ return false;
+
+ char *gyId = strtok((char*)args, " ");
+ if (!gyId)
+ return false;
+
+ int32 i_gyId = atoi(gyId);
+
+ if(!i_gyId)
+ return false;
+
+ WorldSafeLocsEntry const* gy = sWorldSafeLocsStore.LookupEntry(i_gyId);
+ if (!gy)
+ {
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST,i_gyId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!MapManager::IsValidMapCoord(gy->map_id,gy->x,gy->y,gy->z))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,gy->x,gy->y,gy->map_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(gy->map_id, gy->x, gy->y, gy->z, _player->GetOrientation());
+ return true;
+}
+
+/** \brief Teleport the GM to the specified creature
+ *
+ * .gocreature <GUID> --> TP using creature.guid
+ * .gocreature azuregos --> TP player to the mob with this name
+ * Warning: If there is more than one mob with this name
+ * you will be teleported to the first one that is found.
+ * .gocreature id 6109 --> TP player to the mob, that has this creature_template.entry
+ * Warning: If there is more than one mob with this "id"
+ * you will be teleported to the first one that is found.
+ */
+//teleport to creature
+bool ChatHandler::HandleGoCreatureCommand(const char* args)
+{
+ if(!*args)
+ return false;
+ Player* _player = m_session->GetPlayer();
+
+ // "id" or number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
+ char* pParam1 = extractKeyFromLink((char*)args,"Hcreature");
+ if (!pParam1)
+ return false;
+
+ std::ostringstream whereClause;
+
+ // User wants to teleport to the NPC's template entry
+ if( strcmp(pParam1, "id") == 0 )
+ {
+ //sLog.outError("DEBUG: ID found");
+
+ // Get the "creature_template.entry"
+ // number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
+ char* tail = strtok(NULL,"");
+ if(!tail)
+ return false;
+ char* cId = extractKeyFromLink(tail,"Hcreature_entry");
+ if(!cId)
+ return false;
+
+ int32 tEntry = atoi(cId);
+ //sLog.outError("DEBUG: ID value: %d", tEntry);
+ if(!tEntry)
+ return false;
+
+ whereClause << "WHERE id = '" << tEntry << "'";
+ }
+ else
+ {
+ //sLog.outError("DEBUG: ID *not found*");
+
+ int32 guid = atoi(pParam1);
+
+ // Number is invalid - maybe the user specified the mob's name
+ if(!guid)
+ {
+ std::string name = pParam1;
+ WorldDatabase.escape_string(name);
+ whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name "_LIKE_" '" << name << "'";
+ }
+ else
+ {
+ whereClause << "WHERE guid = '" << guid << "'";
+ }
+ }
+ //sLog.outError("DEBUG: %s", whereClause.c_str());
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM creature %s", whereClause.str().c_str() );
+ if (!result)
+ {
+ SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ if( result->GetRowCount() > 1 )
+ {
+ SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
+ }
+
+ Field *fields = result->Fetch();
+ float x = fields[0].GetFloat();
+ float y = fields[1].GetFloat();
+ float z = fields[2].GetFloat();
+ float ort = fields[3].GetFloat();
+ int mapid = fields[4].GetUInt16();
+
+ delete result;
+
+ if(!MapManager::IsValidMapCoord(mapid,x,y,z,ort))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // stop flight if need
+ if(_player->isInFlight())
+ {
+ _player->GetMotionMaster()->MovementExpired();
+ _player->m_taxi.ClearTaxiDestinations();
+ }
+ // save only in non-flight case
+ else
+ _player->SaveRecallPosition();
+
+ _player->TeleportTo(mapid, x, y, z, ort);
+ return true;
+}
+
+bool ChatHandler::HandleGUIDCommand(const char* /*args*/)
+{
+ uint64 guid = m_session->GetPlayer()->GetSelection();
+
+ if (guid == 0)
+ {
+ SendSysMessage(LANG_NO_SELECTION);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_OBJECT_GUID, GUID_LOPART(guid), GUID_HIPART(guid));
+ return true;
+}
+
+bool ChatHandler::HandleLookupFactionCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Player *target = getSelectedPlayer();
+ if (!target)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ // converting string that we try to find to lower case
+ wstrToLower( wnamepart );
+
+ uint32 counter = 0; // Counter for figure out that we found smth.
+
+ for (uint32 id = 0; id < sFactionStore.GetNumRows(); id++)
+ //for(FactionStateList::const_iterator itr = target->m_factions.begin(); itr != target->m_factions.end(); ++itr)
+ {
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(id);
+ //FactionEntry const *factionEntry = sFactionStore.LookupEntry(itr->second.ID);
+ if (factionEntry)
+ {
+ FactionStateList::const_iterator repItr = target->m_factions.find(factionEntry->reputationListID);
+
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = factionEntry->name[loc];
+ if(name.empty())
+ continue;
+
+ if (!Utf8FitTo(name, wnamepart))
+ {
+ loc = 0;
+ for(; loc < MAX_LOCALE; ++loc)
+ {
+ if(loc==m_session->GetSessionDbcLocale())
+ continue;
+
+ name = factionEntry->name[loc];
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ break;
+ }
+ }
+
+ if(loc < MAX_LOCALE)
+ {
+ // send faction in "id - [faction] rank reputation [visible] [at war] [own team] [unknown] [invisible] [inactive]" format
+ // or "id - [faction] [no reputation]" format
+ std::ostringstream ss;
+ ss << id << " - |cffffffff|Hfaction:" << id << "|h[" << name << " " << localeNames[loc] << "]|h|r";
+
+ if (repItr != target->m_factions.end())
+ {
+ ReputationRank rank = target->GetReputationRank(factionEntry);
+ std::string rankName = GetMangosString(ReputationRankStrIndex[rank]);
+
+ ss << " " << rankName << "|h|r (" << target->GetReputation(factionEntry) << ")";
+
+ if(repItr->second.Flags & FACTION_FLAG_VISIBLE)
+ ss << GetMangosString(LANG_FACTION_VISIBLE);
+ if(repItr->second.Flags & FACTION_FLAG_AT_WAR)
+ ss << GetMangosString(LANG_FACTION_ATWAR);
+ if(repItr->second.Flags & FACTION_FLAG_PEACE_FORCED)
+ ss << GetMangosString(LANG_FACTION_PEACE_FORCED);
+ if(repItr->second.Flags & FACTION_FLAG_HIDDEN)
+ ss << GetMangosString(LANG_FACTION_HIDDEN);
+ if(repItr->second.Flags & FACTION_FLAG_INVISIBLE_FORCED)
+ ss << GetMangosString(LANG_FACTION_INVISIBLE_FORCED);
+ if(repItr->second.Flags & FACTION_FLAG_INACTIVE)
+ ss << GetMangosString(LANG_FACTION_INACTIVE);
+ }
+ else
+ ss << GetMangosString(LANG_FACTION_NOREPUTATION);
+
+ SendSysMessage(ss.str().c_str());
+ counter++;
+ }
+ }
+ }
+
+ if (counter == 0) // if counter == 0 then we found nth
+ SendSysMessage(LANG_COMMAND_FACTION_NOTFOUND);
+ return true;
+}
+
+bool ChatHandler::HandleModifyRepCommand(const char * args)
+{
+ if (!*args) return false;
+
+ Player* target = NULL;
+ target = getSelectedPlayer();
+
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* factionTxt = extractKeyFromLink((char*)args,"Hfaction");
+ if(!factionTxt)
+ return false;
+
+ uint32 factionId = atoi(factionTxt);
+
+ int32 amount = 0;
+ char *rankTxt = strtok(NULL, " ");
+ if (!factionTxt || !rankTxt)
+ return false;
+
+ amount = atoi(rankTxt);
+ if ((amount == 0) && (rankTxt[0] != '-') && !isdigit(rankTxt[0]))
+ {
+ std::string rankStr = rankTxt;
+ std::wstring wrankStr;
+ if(!Utf8toWStr(rankStr,wrankStr))
+ return false;
+ wstrToLower( wrankStr );
+
+ int r = 0;
+ amount = -42000;
+ for (; r < MAX_REPUTATION_RANK; ++r)
+ {
+ std::string rank = GetMangosString(ReputationRankStrIndex[r]);
+ if(rank.empty())
+ continue;
+
+ std::wstring wrank;
+ if(!Utf8toWStr(rank,wrank))
+ continue;
+
+ wstrToLower(wrank);
+
+ if(wrank.substr(0,wrankStr.size())==wrankStr)
+ {
+ char *deltaTxt = strtok(NULL, " ");
+ if (deltaTxt)
+ {
+ int32 delta = atoi(deltaTxt);
+ if ((delta < 0) || (delta > Player::ReputationRank_Length[r] -1))
+ {
+ PSendSysMessage(LANG_COMMAND_FACTION_DELTA, (Player::ReputationRank_Length[r]-1));
+ SetSentErrorMessage(true);
+ return false;
+ }
+ amount += delta;
+ }
+ break;
+ }
+ amount += Player::ReputationRank_Length[r];
+ }
+ if (r >= MAX_REPUTATION_RANK)
+ {
+ PSendSysMessage(LANG_COMMAND_FACTION_INVPARAM, rankTxt);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionId);
+
+ if (!factionEntry)
+ {
+ PSendSysMessage(LANG_COMMAND_FACTION_UNKNOWN, factionId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (factionEntry->reputationListID < 0)
+ {
+ PSendSysMessage(LANG_COMMAND_FACTION_NOREP_ERROR, factionEntry->name[m_session->GetSessionDbcLocale()], factionId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target->SetFactionReputation(factionEntry,amount);
+ PSendSysMessage(LANG_COMMAND_MODIFY_REP, factionEntry->name[m_session->GetSessionDbcLocale()], factionId, target->GetName(), target->GetReputation(factionId));
+ return true;
+}
+
+bool ChatHandler::HandleNameCommand(const char* args)
+{
+ /* Temp. disabled
+ if(!*args)
+ return false;
+
+ if(strlen((char*)args)>75)
+ {
+ PSendSysMessage(LANG_TOO_LONG_NAME, strlen((char*)args)-75);
+ return true;
+ }
+
+ for (uint8 i = 0; i < strlen(args); i++)
+ {
+ if(!isalpha(args[i]) && args[i]!=' ')
+ {
+ SendSysMessage(LANG_CHARS_ONLY);
+ return false;
+ }
+ }
+
+ uint64 guid;
+ guid = m_session->GetPlayer()->GetSelection();
+ if (guid == 0)
+ {
+ SendSysMessage(LANG_NO_SELECTION);
+ return true;
+ }
+
+ Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ return true;
+ }
+
+ pCreature->SetName(args);
+ uint32 idname = objmgr.AddCreatureTemplate(pCreature->GetName());
+ pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname);
+
+ pCreature->SaveToDB();
+ */
+
+ return true;
+}
+
+bool ChatHandler::HandleSubNameCommand(const char* /*args*/)
+{
+ /* Temp. disabled
+
+ if(!*args)
+ args = "";
+
+ if(strlen((char*)args)>75)
+ {
+
+ PSendSysMessage(LANG_TOO_LONG_SUBNAME, strlen((char*)args)-75);
+ return true;
+ }
+
+ for (uint8 i = 0; i < strlen(args); i++)
+ {
+ if(!isalpha(args[i]) && args[i]!=' ')
+ {
+ SendSysMessage(LANG_CHARS_ONLY);
+ return false;
+ }
+ }
+ uint64 guid;
+ guid = m_session->GetPlayer()->GetSelection();
+ if (guid == 0)
+ {
+ SendSysMessage(LANG_NO_SELECTION);
+ return true;
+ }
+
+ Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ return true;
+ }
+
+ uint32 idname = objmgr.AddCreatureSubName(pCreature->GetName(),args,pCreature->GetUInt32Value(UNIT_FIELD_DISPLAYID));
+ pCreature->SetUInt32Value(OBJECT_FIELD_ENTRY, idname);
+
+ pCreature->SaveToDB();
+ */
+ return true;
+}
+
+//move item to other slot
+bool ChatHandler::HandleItemMoveCommand(const char* args)
+{
+ if(!*args)
+ return false;
+ uint8 srcslot, dstslot;
+
+ char* pParam1 = strtok((char*)args, " ");
+ if (!pParam1)
+ return false;
+
+ char* pParam2 = strtok(NULL, " ");
+ if (!pParam2)
+ return false;
+
+ srcslot = (uint8)atoi(pParam1);
+ dstslot = (uint8)atoi(pParam2);
+
+ uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot);
+ uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot);
+
+ if(srcslot==dstslot)
+ return true;
+
+ m_session->GetPlayer()->SwapItem( src, dst );
+
+ return true;
+}
+
+//add spawn of creature
+bool ChatHandler::HandleAddSpwCommand(const char* args)
+{
+ if(!*args)
+ return false;
+ char* charID = strtok((char*)args, " ");
+ if (!charID)
+ return false;
+
+ char* team = strtok(NULL, " ");
+ int32 teamval = 0;
+ if (team) { teamval = atoi(team); }
+ if (teamval < 0) { teamval = 0; }
+
+ uint32 id = atoi(charID);
+
+ Player *chr = m_session->GetPlayer();
+ float x = chr->GetPositionX();
+ float y = chr->GetPositionY();
+ float z = chr->GetPositionZ();
+ float o = chr->GetOrientation();
+ Map *map = chr->GetMap();
+
+ Creature* pCreature = new Creature;
+ if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, (uint32)teamval))
+ {
+ delete pCreature;
+ return false;
+ }
+
+ pCreature->Relocate(x,y,z,o);
+
+ if(!pCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
+ delete pCreature;
+ return false;
+ }
+
+ pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+
+ uint32 db_guid = pCreature->GetDBTableGUIDLow();
+
+ // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
+ pCreature->LoadFromDB(db_guid, map);
+
+ map->Add(pCreature);
+ objmgr.AddCreatureToGrid(db_guid, objmgr.GetCreatureData(db_guid));
+ return true;
+}
+
+bool ChatHandler::HandleDelCreatureCommand(const char* args)
+{
+ Creature* unit = NULL;
+
+ if(*args)
+ {
+ // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hcreature");
+ if(!cId)
+ return false;
+
+ uint32 lowguid = atoi(cId);
+ if(!lowguid)
+ return false;
+
+ if (CreatureData const* cr_data = objmgr.GetCreatureData(lowguid))
+ unit = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, cr_data->id, HIGHGUID_UNIT));
+ }
+ else
+ unit = getSelectedCreature();
+
+ if(!unit || unit->isPet() || unit->isTotem())
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // Delete the creature
+ unit->CombatStop();
+ unit->DeleteFromDB();
+ unit->CleanupsBeforeDelete();
+ unit->AddObjectToRemoveList();
+
+ SendSysMessage(LANG_COMMAND_DELCREATMESSAGE);
+
+ return true;
+}
+
+//delete object by selection or guid
+bool ChatHandler::HandleDelObjectCommand(const char* args)
+{
+ // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameobject");
+ if(!cId)
+ return false;
+
+ uint32 lowguid = atoi(cId);
+ if(!lowguid)
+ return false;
+
+ GameObject* obj = NULL;
+
+ // by DB guid
+ if (GameObjectData const* go_data = objmgr.GetGOData(lowguid))
+ obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id);
+
+ if(!obj)
+ {
+ PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 owner_guid = obj->GetOwnerGUID();
+ if(owner_guid)
+ {
+ Unit* owner = ObjectAccessor::GetUnit(*m_session->GetPlayer(),owner_guid);
+ if(!owner && !IS_PLAYER_GUID(owner_guid))
+ {
+ PSendSysMessage(LANG_COMMAND_DELOBJREFERCREATURE, GUID_LOPART(owner_guid), obj->GetGUIDLow());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ owner->RemoveGameObject(obj,false);
+ }
+
+ obj->SetRespawnTime(0); // not save respawn time
+ obj->Delete();
+ obj->DeleteFromDB();
+
+ PSendSysMessage(LANG_COMMAND_DELOBJMESSAGE, obj->GetGUIDLow());
+
+ return true;
+}
+
+//turn selected object
+bool ChatHandler::HandleTurnObjectCommand(const char* args)
+{
+ // number or [name] Shift-click form |color|Hgameobject:go_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameobject");
+ if(!cId)
+ return false;
+
+ uint32 lowguid = atoi(cId);
+ if(!lowguid)
+ return false;
+
+ GameObject* obj = NULL;
+
+ // by DB guid
+ if (GameObjectData const* go_data = objmgr.GetGOData(lowguid))
+ obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id);
+
+ if(!obj)
+ {
+ PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* po = strtok(NULL, " ");
+ float o;
+
+ if (po)
+ {
+ o = (float)atof(po);
+ }
+ else
+ {
+ Player *chr = m_session->GetPlayer();
+ o = chr->GetOrientation();
+ }
+
+ float rot2 = sin(o/2);
+ float rot3 = cos(o/2);
+
+ Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj);
+ map->Remove(obj,false);
+
+ obj->Relocate(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), o);
+
+ obj->SetFloatValue(GAMEOBJECT_FACING, o);
+ obj->SetFloatValue(GAMEOBJECT_ROTATION+2, rot2);
+ obj->SetFloatValue(GAMEOBJECT_ROTATION+3, rot3);
+
+ map->Add(obj);
+
+ obj->SaveToDB();
+ obj->Refresh();
+
+ PSendSysMessage(LANG_COMMAND_TURNOBJMESSAGE, obj->GetGUIDLow(), o);
+
+ return true;
+}
+
+//move selected creature
+bool ChatHandler::HandleMoveCreatureCommand(const char* args)
+{
+ uint32 lowguid = 0;
+
+ Creature* pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ // number or [name] Shift-click form |color|Hcreature:creature_guid|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hcreature");
+ if(!cId)
+ return false;
+
+ uint32 lowguid = atoi(cId);
+
+ /* FIXME: impossibel without entry
+ if(lowguid)
+ pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
+ */
+
+ // Attempting creature load from DB data
+ if(!pCreature)
+ {
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 map_id = data->mapid;
+
+ if(m_session->GetPlayer()->GetMapId()!=map_id)
+ {
+ PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ lowguid = pCreature->GetDBTableGUIDLow();
+ }
+ }
+ else
+ {
+ lowguid = pCreature->GetDBTableGUIDLow();
+ }
+
+ float x = m_session->GetPlayer()->GetPositionX();
+ float y = m_session->GetPlayer()->GetPositionY();
+ float z = m_session->GetPlayer()->GetPositionZ();
+ float o = m_session->GetPlayer()->GetOrientation();
+
+ if (pCreature)
+ {
+ if(CreatureData const* data = objmgr.GetCreatureData(pCreature->GetDBTableGUIDLow()))
+ {
+ const_cast<CreatureData*>(data)->posX = x;
+ const_cast<CreatureData*>(data)->posY = y;
+ const_cast<CreatureData*>(data)->posZ = z;
+ const_cast<CreatureData*>(data)->orientation = o;
+ }
+ MapManager::Instance().GetMap(pCreature->GetMapId(),pCreature)->CreatureRelocation(pCreature,x, y, z,o);
+ pCreature->GetMotionMaster()->Initialize();
+ if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ pCreature->setDeathState(JUST_DIED);
+ pCreature->Respawn();
+ }
+ }
+
+ WorldDatabase.PExecuteLog("UPDATE creature SET position_x = '%f', position_y = '%f', position_z = '%f', orientation = '%f' WHERE guid = '%u'", x, y, z, o, lowguid);
+ PSendSysMessage(LANG_COMMAND_CREATUREMOVED);
+ return true;
+}
+
+//move selected object
+bool ChatHandler::HandleMoveObjectCommand(const char* args)
+{
+ // number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameobject");
+ if(!cId)
+ return false;
+
+ uint32 lowguid = atoi(cId);
+ if(!lowguid)
+ return false;
+
+ GameObject* obj = NULL;
+
+ // by DB guid
+ if (GameObjectData const* go_data = objmgr.GetGOData(lowguid))
+ obj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid,go_data->id);
+
+ if(!obj)
+ {
+ PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* px = strtok(NULL, " ");
+ char* py = strtok(NULL, " ");
+ char* pz = strtok(NULL, " ");
+
+ if (!px)
+ {
+ Player *chr = m_session->GetPlayer();
+
+ Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj);
+ map->Remove(obj,false);
+
+ obj->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), obj->GetOrientation());
+ obj->SetFloatValue(GAMEOBJECT_POS_X, chr->GetPositionX());
+ obj->SetFloatValue(GAMEOBJECT_POS_Y, chr->GetPositionY());
+ obj->SetFloatValue(GAMEOBJECT_POS_Z, chr->GetPositionZ());
+
+ map->Add(obj);
+ }
+ else
+ {
+ if(!py || !pz)
+ return false;
+
+ float x = (float)atof(px);
+ float y = (float)atof(py);
+ float z = (float)atof(pz);
+
+ if(!MapManager::IsValidMapCoord(obj->GetMapId(),x,y,z))
+ {
+ PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,obj->GetMapId());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Map* map = MapManager::Instance().GetMap(obj->GetMapId(),obj);
+ map->Remove(obj,false);
+
+ obj->Relocate(x, y, z, obj->GetOrientation());
+ obj->SetFloatValue(GAMEOBJECT_POS_X, x);
+ obj->SetFloatValue(GAMEOBJECT_POS_Y, y);
+ obj->SetFloatValue(GAMEOBJECT_POS_Z, z);
+
+ map->Add(obj);
+ }
+
+ obj->SaveToDB();
+ obj->Refresh();
+
+ PSendSysMessage(LANG_COMMAND_MOVEOBJMESSAGE, obj->GetGUIDLow());
+
+ return true;
+}
+
+//demorph player or unit
+bool ChatHandler::HandleDeMorphCommand(const char* /*args*/)
+{
+ Unit *target = getSelectedUnit();
+ if(!target)
+ target = m_session->GetPlayer();
+
+ target->DeMorph();
+
+ return true;
+}
+
+//add item in vendorlist
+bool ChatHandler::HandleAddVendorItemCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ char* pitem = extractKeyFromLink((char*)args,"Hitem");
+ if (!pitem)
+ {
+ SendSysMessage(LANG_COMMAND_NEEDITEMSEND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 itemId = atol(pitem);
+
+ char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0
+ uint32 maxcount = 0;
+ if (fmaxcount)
+ maxcount = atol(fmaxcount);
+
+ char* fincrtime = strtok(NULL, " "); //add incrtime, default: 0
+ uint32 incrtime = 0;
+ if (fincrtime)
+ incrtime = atol(fincrtime);
+
+ char* fextendedcost = strtok(NULL, " "); //add ExtendedCost, default: 0
+ uint32 extendedcost = fextendedcost ? atol(fextendedcost) : 0;
+
+ Creature* vendor = getSelectedCreature();
+
+ uint32 vendor_entry = vendor ? vendor->GetEntry() : 0;
+
+ if(!objmgr.IsVendorItemValid(vendor_entry,itemId,maxcount,incrtime,extendedcost,m_session->GetPlayer()))
+ {
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ objmgr.AddVendorItem(vendor_entry,itemId,maxcount,incrtime,extendedcost);
+
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId);
+
+ PSendSysMessage(LANG_ITEM_ADDED_TO_LIST,itemId,pProto->Name1,maxcount,incrtime,extendedcost);
+ return true;
+}
+
+//del item from vendor list
+bool ChatHandler::HandleDelVendorItemCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ Creature* vendor = getSelectedCreature();
+ if (!vendor || !vendor->isVendor())
+ {
+ SendSysMessage(LANG_COMMAND_VENDORSELECTION);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* pitem = extractKeyFromLink((char*)args,"Hitem");
+ if (!pitem)
+ {
+ SendSysMessage(LANG_COMMAND_NEEDITEMSEND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ uint32 itemId = atol(pitem);
+
+
+ if(!objmgr.RemoveVendorItem(vendor->GetEntry(),itemId))
+ {
+ PSendSysMessage(LANG_ITEM_NOT_IN_LIST,itemId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId);
+
+ PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST,itemId,pProto->Name1);
+ return true;
+}
+
+//add move for creature
+bool ChatHandler::HandleAddMoveCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* guid_str = strtok((char*)args, " ");
+ char* wait_str = strtok((char*)NULL, " ");
+
+ uint32 lowguid = atoi((char*)guid_str);
+
+ Creature* pCreature = NULL;
+
+ /* FIXME: impossible without entry
+ if(lowguid)
+ pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
+ */
+
+ // attempt check creature existence by DB data
+ if(!pCreature)
+ {
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ // obtain real GUID for DB operations
+ lowguid = pCreature->GetDBTableGUIDLow();
+ }
+
+ int wait = wait_str ? atoi(wait_str) : 0;
+
+ if(wait < 0)
+ wait = 0;
+
+ Player* player = m_session->GetPlayer();
+
+ WaypointMgr.AddLastNode(lowguid, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), wait, 0);
+
+ // update movement type
+ WorldDatabase.PExecuteLog("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE,lowguid);
+ if(pCreature)
+ {
+ pCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
+ pCreature->GetMotionMaster()->Initialize();
+ if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ pCreature->setDeathState(JUST_DIED);
+ pCreature->Respawn();
+ }
+ pCreature->SaveToDB();
+ }
+
+ SendSysMessage(LANG_WAYPOINT_ADDED);
+
+ return true;
+}
+
+/**
+ * Set the movement type for an NPC.<br/>
+ * <br/>
+ * Valid movement types are:
+ * <ul>
+ * <li> stay - NPC wont move </li>
+ * <li> random - NPC will move randomly according to the spawndist </li>
+ * <li> way - NPC will move with given waypoints set </li>
+ * </ul>
+ * additional parameter: NODEL - so no waypoints are deleted, if you
+ * change the movement type
+ */
+bool ChatHandler::HandleSetMoveTypeCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // 3 arguments:
+ // GUID (optional - you can also select the creature)
+ // stay|random|way (determines the kind of movement)
+ // NODEL (optional - tells the system NOT to delete any waypoints)
+ // this is very handy if you want to do waypoints, that are
+ // later switched on/off according to special events (like escort
+ // quests, etc)
+ char* guid_str = strtok((char*)args, " ");
+ char* type_str = strtok((char*)NULL, " ");
+ char* dontdel_str = strtok((char*)NULL, " ");
+
+ bool doNotDelete = false;
+
+ if(!guid_str)
+ return false;
+
+ uint32 lowguid = 0;
+ Creature* pCreature = NULL;
+
+ if( dontdel_str )
+ {
+ //sLog.outError("DEBUG: All 3 params are set");
+
+ // All 3 params are set
+ // GUID
+ // type
+ // doNotDEL
+ if( stricmp( dontdel_str, "NODEL" ) == 0 )
+ {
+ //sLog.outError("DEBUG: doNotDelete = true;");
+ doNotDelete = true;
+ }
+ }
+ else
+ {
+ // Only 2 params - but maybe NODEL is set
+ if( type_str )
+ {
+ sLog.outError("DEBUG: Only 2 params ");
+ if( stricmp( type_str, "NODEL" ) == 0 )
+ {
+ //sLog.outError("DEBUG: type_str, NODEL ");
+ doNotDelete = true;
+ type_str = NULL;
+ }
+ }
+ }
+
+ if(!type_str) // case .setmovetype $move_type (with selected creature)
+ {
+ type_str = guid_str;
+ pCreature = getSelectedCreature();
+ if(!pCreature)
+ return false;
+ lowguid = pCreature->GetDBTableGUIDLow();
+ }
+ else // case .setmovetype #creature_guid $move_type (with selected creature)
+ {
+ lowguid = atoi((char*)guid_str);
+
+ /* impossible without entry
+ if(lowguid)
+ pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_GUID(lowguid,HIGHGUID_UNIT));
+ */
+
+ // attempt check creature existence by DB data
+ if(!pCreature)
+ {
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_COMMAND_CREATGUIDNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ lowguid = pCreature->GetDBTableGUIDLow();
+ }
+ }
+
+ // now lowguid is low guid really existed creature
+ // and pCreature point (maybe) to this creature or NULL
+
+ MovementGeneratorType move_type;
+
+ std::string type = type_str;
+
+ if(type == "stay")
+ move_type = IDLE_MOTION_TYPE;
+ else if(type == "random")
+ move_type = RANDOM_MOTION_TYPE;
+ else if(type == "way")
+ move_type = WAYPOINT_MOTION_TYPE;
+ else
+ return false;
+
+ // update movement type
+ if(doNotDelete == false)
+ WaypointMgr.DeletePath(lowguid);
+
+ if(pCreature)
+ {
+ pCreature->SetDefaultMovementType(move_type);
+ pCreature->GetMotionMaster()->Initialize();
+ if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ pCreature->setDeathState(JUST_DIED);
+ pCreature->Respawn();
+ }
+ pCreature->SaveToDB();
+ }
+ if( doNotDelete == false )
+ {
+ PSendSysMessage(LANG_MOVE_TYPE_SET,type_str);
+ }
+ else
+ {
+ PSendSysMessage(LANG_MOVE_TYPE_SET_NODEL,type_str);
+ }
+
+ return true;
+} // HandleSetMoveTypeCommand
+
+//change level of creature or pet
+bool ChatHandler::HandleChangeLevelCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint8 lvl = (uint8) atoi((char*)args);
+ if ( lvl < 1 || lvl > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) + 3)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature* pCreature = getSelectedCreature();
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(pCreature->isPet())
+ {
+ ((Pet*)pCreature)->GivePetLevel(lvl);
+ }
+ else
+ {
+ pCreature->SetMaxHealth( 100 + 30*lvl);
+ pCreature->SetHealth( 100 + 30*lvl);
+ pCreature->SetLevel( lvl);
+ pCreature->SaveToDB();
+ }
+
+ return true;
+}
+
+//set npcflag of creature
+bool ChatHandler::HandleNPCFlagCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 npcFlags = (uint32) atoi((char*)args);
+
+ Creature* pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->SetUInt32Value(UNIT_NPC_FLAGS, npcFlags);
+
+ WorldDatabase.PExecuteLog("UPDATE creature_template SET npcflag = '%u' WHERE entry = '%u'", npcFlags, pCreature->GetEntry());
+
+ SendSysMessage(LANG_VALUE_SAVED_REJOIN);
+
+ return true;
+}
+
+//set model of creature
+bool ChatHandler::HandleSetModelCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 displayId = (uint32) atoi((char*)args);
+
+ Creature *pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->SetDisplayId(displayId);
+ pCreature->SetNativeDisplayId(displayId);
+
+ pCreature->SaveToDB();
+
+ return true;
+}
+
+//morph creature or player
+bool ChatHandler::HandleMorphCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint16 display_id = (uint16)atoi((char*)args);
+
+ Unit *target = getSelectedUnit();
+ if(!target)
+ target = m_session->GetPlayer();
+
+ target->SetDisplayId(display_id);
+
+ return true;
+}
+
+//set faction of creature or go
+bool ChatHandler::HandleFactionIdCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 factionId = (uint32) atoi((char*)args);
+
+ if (!sFactionTemplateStore.LookupEntry(factionId))
+ {
+ PSendSysMessage(LANG_WRONG_FACTION, factionId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature* pCreature = getSelectedCreature();
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ pCreature->setFaction(factionId);
+
+ // faction is set in creature_template - not inside creature
+
+ // update in memory
+ if(CreatureInfo const *cinfo = pCreature->GetCreatureInfo())
+ {
+ const_cast<CreatureInfo*>(cinfo)->faction_A = factionId;
+ const_cast<CreatureInfo*>(cinfo)->faction_H = factionId;
+ }
+
+ // and DB
+ WorldDatabase.PExecuteLog("UPDATE creature_template SET faction_A = '%u', faction_H = '%u' WHERE entry = '%u'", factionId, factionId, pCreature->GetEntry());
+
+ return true;
+}
+
+//kick player
+bool ChatHandler::HandleKickPlayerCommand(const char *args)
+{
+ char* kickName = strtok((char*)args, " ");
+ if (!kickName)
+ {
+ Player* player = getSelectedPlayer();
+
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(player==m_session->GetPlayer())
+ {
+ SendSysMessage(LANG_COMMAND_KICKSELF);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player->GetSession()->KickPlayer();
+ }
+ else
+ {
+ std::string name = kickName;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(name==m_session->GetPlayer()->GetName())
+ {
+ SendSysMessage(LANG_COMMAND_KICKSELF);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(sWorld.KickPlayer(name))
+ {
+ PSendSysMessage(LANG_COMMAND_KICKMESSAGE,name.c_str());
+ }
+ else
+ PSendSysMessage(LANG_COMMAND_KICKNOTFOUNDPLAYER,name.c_str());
+ }
+
+ return true;
+}
+
+//show info of player
+bool ChatHandler::HandlePInfoCommand(const char* args)
+{
+ Player* target = NULL;
+ uint64 targetGUID = 0;
+
+ char* px = strtok((char*)args, " ");
+ char* py = NULL;
+
+ std::string name;
+
+ if (px)
+ {
+ name = px;
+
+ if(name.empty())
+ return false;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target = objmgr.GetPlayer(name.c_str());
+ if (target)
+ py = strtok(NULL, " ");
+ else
+ {
+ targetGUID = objmgr.GetPlayerGUIDByName(name);
+ if(targetGUID)
+ py = strtok(NULL, " ");
+ else
+ py = px;
+ }
+ }
+
+ if(!target && !targetGUID)
+ {
+ target = getSelectedPlayer();
+ }
+
+ if(!target && !targetGUID)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 accId = 0;
+ uint32 money = 0;
+ uint32 total_player_time = 0;
+ uint32 level = 0;
+ uint32 latency = 0;
+
+ // get additional information from Player object
+ if(target)
+ {
+ targetGUID = target->GetGUID();
+ name = target->GetName(); // re-read for case getSelectedPlayer() target
+ accId = target->GetSession()->GetAccountId();
+ money = target->GetMoney();
+ total_player_time = target->GetTotalPlayedTime();
+ level = target->getLevel();
+ latency = target->GetSession()->GetLatency();
+ }
+ // get additional information from DB
+ else
+ {
+ accId = objmgr.GetPlayerAccountIdByGUID(targetGUID);
+ Player plr(m_session); // use current session for temporary load
+ plr.MinimalLoadFromDB(NULL, targetGUID);
+ money = plr.GetMoney();
+ total_player_time = plr.GetTotalPlayedTime();
+ level = plr.getLevel();
+ }
+
+ std::string username = GetMangosString(LANG_ERROR);
+ std::string last_ip = GetMangosString(LANG_ERROR);
+ uint32 security = 0;
+ std::string last_login = GetMangosString(LANG_ERROR);
+
+ QueryResult* result = loginDatabase.PQuery("SELECT username,gmlevel,last_ip,last_login FROM account WHERE id = '%u'",accId);
+ if(result)
+ {
+ Field* fields = result->Fetch();
+ username = fields[0].GetCppString();
+ security = fields[1].GetUInt32();
+ if(m_session->GetSecurity() >= security)
+ {
+ last_ip = fields[2].GetCppString();
+ last_login = fields[3].GetCppString();
+ }
+ else
+ {
+ last_ip = "-";
+ last_login = "-";
+ }
+
+ delete result;
+ }
+
+ PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetMangosString(LANG_OFFLINE)), name.c_str(), GUID_LOPART(targetGUID), username.c_str(), accId, security, last_ip.c_str(), last_login.c_str(), latency);
+
+ std::string timeStr = secsToTimeString(total_player_time,true,true);
+ uint32 gold = money /GOLD;
+ uint32 silv = (money % GOLD) / SILVER;
+ uint32 copp = (money % GOLD) % SILVER;
+ PSendSysMessage(LANG_PINFO_LEVEL, timeStr.c_str(), level, gold,silv,copp );
+
+ if ( py && strncmp(py, "rep", 3) == 0 )
+ {
+ if(!target)
+ {
+ // rep option not implemented for offline case
+ SendSysMessage(LANG_PINFO_NO_REP);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* FactionName;
+ for(FactionStateList::const_iterator itr = target->m_factions.begin(); itr != target->m_factions.end(); ++itr)
+ {
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(itr->second.ID);
+ if (factionEntry)
+ FactionName = factionEntry->name[m_session->GetSessionDbcLocale()];
+ else
+ FactionName = "#Not found#";
+ ReputationRank rank = target->GetReputationRank(factionEntry);
+ std::string rankName = GetMangosString(ReputationRankStrIndex[rank]);
+ std::ostringstream ss;
+ ss << itr->second.ID << ": |cffffffff|Hfaction:" << itr->second.ID << "|h[" << FactionName << "]|h|r " << rankName << "|h|r (" << target->GetReputation(factionEntry) << ")";
+
+ if(itr->second.Flags & FACTION_FLAG_VISIBLE)
+ ss << GetMangosString(LANG_FACTION_VISIBLE);
+ if(itr->second.Flags & FACTION_FLAG_AT_WAR)
+ ss << GetMangosString(LANG_FACTION_ATWAR);
+ if(itr->second.Flags & FACTION_FLAG_PEACE_FORCED)
+ ss << GetMangosString(LANG_FACTION_PEACE_FORCED);
+ if(itr->second.Flags & FACTION_FLAG_HIDDEN)
+ ss << GetMangosString(LANG_FACTION_HIDDEN);
+ if(itr->second.Flags & FACTION_FLAG_INVISIBLE_FORCED)
+ ss << GetMangosString(LANG_FACTION_INVISIBLE_FORCED);
+ if(itr->second.Flags & FACTION_FLAG_INACTIVE)
+ ss << GetMangosString(LANG_FACTION_INACTIVE);
+
+ SendSysMessage(ss.str().c_str());
+ }
+ }
+ return true;
+}
+
+//show tickets
+void ChatHandler::ShowTicket(uint64 guid, char const* text, char const* time)
+{
+ std::string name;
+ if(!objmgr.GetPlayerNameByGUID(guid,name))
+ name = GetMangosString(LANG_UNKNOWN);
+
+ PSendSysMessage(LANG_COMMAND_TICKETVIEW, name.c_str(),time,text);
+}
+
+//ticket commands
+bool ChatHandler::HandleTicketCommand(const char* args)
+{
+ char* px = strtok((char*)args, " ");
+
+ // ticket<end>
+ if (!px)
+ {
+ size_t count;
+ QueryResult *result = CharacterDatabase.Query("SELECT COUNT(ticket_id) FROM character_ticket");
+ if(result)
+ {
+ count = (*result)[0].GetUInt32();
+ delete result;
+ }
+ else
+ count = 0;
+
+ PSendSysMessage(LANG_COMMAND_TICKETCOUNT, count, m_session->GetPlayer()->isAcceptTickets() ? GetMangosString(LANG_ON) : GetMangosString(LANG_OFF));
+ return true;
+ }
+
+ // ticket on
+ if(strncmp(px,"on",3) == 0)
+ {
+ m_session->GetPlayer()->SetAcceptTicket(true);
+ SendSysMessage(LANG_COMMAND_TICKETON);
+ return true;
+ }
+
+ // ticket off
+ if(strncmp(px,"off",4) == 0)
+ {
+ m_session->GetPlayer()->SetAcceptTicket(false);
+ SendSysMessage(LANG_COMMAND_TICKETOFF);
+ return true;
+ }
+
+ // ticket #num
+ int num = atoi(px);
+ if(num > 0)
+ {
+ QueryResult *result = CharacterDatabase.PQuery("SELECT guid,ticket_text,ticket_lastchange FROM character_ticket ORDER BY ticket_id ASC LIMIT %d,1",num-1);
+
+ if(!result)
+ {
+ PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num);
+ delete result;
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Field* fields = result->Fetch();
+
+ uint64 guid = fields[0].GetUInt64();
+ char const* text = fields[1].GetString();
+ char const* time = fields[2].GetString();
+
+ ShowTicket(guid,text,time);
+ delete result;
+ return true;
+ }
+
+ std::string name = px;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name);
+
+ if(!guid)
+ return false;
+
+ // ticket $char_name
+ QueryResult *result = CharacterDatabase.PQuery("SELECT ticket_text,ticket_lastchange FROM character_ticket WHERE guid = '%u' ORDER BY ticket_id ASC",GUID_LOPART(guid));
+
+ if(!result)
+ return false;
+
+ Field* fields = result->Fetch();
+
+ char const* text = fields[0].GetString();
+ char const* time = fields[1].GetString();
+
+ ShowTicket(guid,text,time);
+ delete result;
+
+ return true;
+}
+
+uint32 ChatHandler::GetTicketIDByNum(uint32 num)
+{
+ QueryResult *result = CharacterDatabase.Query("SELECT ticket_id FROM character_ticket");
+
+ if(!result || num > result->GetRowCount())
+ {
+ PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num);
+ delete result;
+ return 0;
+ }
+
+ for(uint32 i = 1; i < num; ++i)
+ result->NextRow();
+
+ Field* fields = result->Fetch();
+
+ uint32 id = fields[0].GetUInt32();
+ delete result;
+ return id;
+}
+
+//dell all tickets
+bool ChatHandler::HandleDelTicketCommand(const char *args)
+{
+ char* px = strtok((char*)args, " ");
+ if (!px)
+ return false;
+
+ // delticket all
+ if(strncmp(px,"all",4) == 0)
+ {
+ QueryResult *result = CharacterDatabase.Query("SELECT guid FROM character_ticket");
+
+ if(!result)
+ return true;
+
+ // notify players about ticket deleting
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint64 guid = fields[0].GetUInt64();
+
+ if(Player* sender = objmgr.GetPlayer(guid))
+ sender->GetSession()->SendGMTicketGetTicket(0x0A,0);
+
+ }while(result->NextRow());
+
+ delete result;
+
+ CharacterDatabase.PExecute("DELETE FROM character_ticket");
+ SendSysMessage(LANG_COMMAND_ALLTICKETDELETED);
+ return true;
+ }
+
+ int num = (uint32)atoi(px);
+
+ // delticket #num
+ if(num > 0)
+ {
+ QueryResult *result = CharacterDatabase.PQuery("SELECT ticket_id,guid FROM character_ticket LIMIT %i",num);
+
+ if(!result || uint64(num) > result->GetRowCount())
+ {
+ PSendSysMessage(LANG_COMMAND_TICKENOTEXIST, num);
+ delete result;
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ for(int i = 1; i < num; ++i)
+ result->NextRow();
+
+ Field* fields = result->Fetch();
+
+ uint32 id = fields[0].GetUInt32();
+ uint64 guid = fields[1].GetUInt64();
+ delete result;
+
+ CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE ticket_id = '%u'", id);
+
+ // notify players about ticket deleting
+ if(Player* sender = objmgr.GetPlayer(guid))
+ {
+ sender->GetSession()->SendGMTicketGetTicket(0x0A,0);
+ PSendSysMessage(LANG_COMMAND_TICKETPLAYERDEL,sender->GetName());
+ }
+ else
+ SendSysMessage(LANG_COMMAND_TICKETDEL);
+
+ return true;
+ }
+
+ std::string name = px;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name);
+
+ if(!guid)
+ return false;
+
+ // delticket $char_name
+ CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u'",GUID_LOPART(guid));
+
+ // notify players about ticket deleting
+ if(Player* sender = objmgr.GetPlayer(guid))
+ sender->GetSession()->SendGMTicketGetTicket(0x0A,0);
+
+ PSendSysMessage(LANG_COMMAND_TICKETPLAYERDEL,px);
+ return true;
+}
+
+//set spawn dist of creature
+bool ChatHandler::HandleSpawnDistCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ float option = atof((char*)args);
+ if (option < 0.0f)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ return false;
+ }
+
+ MovementGeneratorType mtype = IDLE_MOTION_TYPE;
+ if (option >0.0f)
+ mtype = RANDOM_MOTION_TYPE;
+
+ Creature *pCreature = getSelectedCreature();
+ uint32 u_guidlow = 0;
+
+ if (pCreature)
+ u_guidlow = pCreature->GetDBTableGUIDLow();
+ else
+ return false;
+
+ pCreature->SetRespawnRadius((float)option);
+ pCreature->SetDefaultMovementType(mtype);
+ pCreature->GetMotionMaster()->Initialize();
+ if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ pCreature->setDeathState(JUST_DIED);
+ pCreature->Respawn();
+ }
+
+ WorldDatabase.PExecuteLog("UPDATE creature SET spawndist=%f, MovementType=%i WHERE guid=%u",option,mtype,u_guidlow);
+ PSendSysMessage(LANG_COMMAND_SPAWNDIST,option);
+ return true;
+}
+
+bool ChatHandler::HandleSpawnTimeCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* stime = strtok((char*)args, " ");
+
+ if (!stime)
+ return false;
+
+ int i_stime = atoi((char*)stime);
+
+ if (i_stime < 0)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature *pCreature = getSelectedCreature();
+ uint32 u_guidlow = 0;
+
+ if (pCreature)
+ u_guidlow = pCreature->GetDBTableGUIDLow();
+ else
+ return false;
+
+ WorldDatabase.PExecuteLog("UPDATE creature SET spawntimesecs=%i WHERE guid=%u",i_stime,u_guidlow);
+ pCreature->SetRespawnDelay((uint32)i_stime);
+ PSendSysMessage(LANG_COMMAND_SPAWNTIME,i_stime);
+
+ return true;
+}
+
+/**
+ * Add a waypoint to a creature.
+ *
+ * The user can either select an npc or provide its GUID.
+ *
+ * The user can even select a visual waypoint - then the new waypoint
+ * is placed *after* the selected one - this makes insertion of new
+ * waypoints possible.
+ *
+ * eg:
+ * .wp add 12345
+ * -> adds a waypoint to the npc with the GUID 12345
+ *
+ * .wp add
+ * -> adds a waypoint to the currently selected creature
+ *
+ *
+ * @param args if the user did not provide a GUID, it is NULL
+ *
+ * @return true - command did succeed, false - something went wrong
+ */
+bool ChatHandler::HandleWpAddCommand(const char* args)
+{
+ sLog.outDebug("DEBUG: HandleWpAddCommand");
+
+ // optional
+ char* guid_str = NULL;
+
+ if(*args)
+ {
+ guid_str = strtok((char*)args, " ");
+ }
+
+ uint32 lowguid = 0;
+ uint32 point = 0;
+ Creature* target = getSelectedCreature();
+ // Did player provide a GUID?
+ if (!guid_str)
+ {
+ sLog.outDebug("DEBUG: HandleWpAddCommand - No GUID provided");
+
+ // No GUID provided
+ // -> Player must have selected a creature
+
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ if (target->GetEntry() == VISUAL_WAYPOINT )
+ {
+ sLog.outDebug("DEBUG: HandleWpAddCommand - target->GetEntry() == VISUAL_WAYPOINT (1) ");
+
+ QueryResult *result =
+ WorldDatabase.PQuery( "SELECT id, point FROM creature_movement WHERE wpguid = %u",
+ target->GetGUIDLow() );
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, target->GetGUIDLow());
+ // User selected a visual spawnpoint -> get the NPC
+ // Select NPC GUID
+ // Since we compare float values, we have to deal with
+ // some difficulties.
+ // Here we search for all waypoints that only differ in one from 1 thousand
+ // (0.001) - There is no other way to compare C++ floats with mySQL floats
+ // See also: http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html
+ const char* maxDIFF = "0.01";
+ result = WorldDatabase.PQuery( "SELECT id, point FROM creature_movement WHERE (abs(position_x - %f) <= %s ) and (abs(position_y - %f) <= %s ) and (abs(position_z - %f) <= %s )",
+ target->GetPositionX(), maxDIFF, target->GetPositionY(), maxDIFF, target->GetPositionZ(), maxDIFF);
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, target->GetGUIDLow());
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ do
+ {
+ Field *fields = result->Fetch();
+ lowguid = fields[0].GetUInt32();
+ point = fields[1].GetUInt32();
+ }while( result->NextRow() );
+ delete result;
+
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT));
+ if(!target)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ lowguid = target->GetDBTableGUIDLow();
+ }
+ }
+ else
+ {
+ sLog.outDebug("DEBUG: HandleWpAddCommand - GUID provided");
+
+ // GUID provided
+ // Warn if player also selected a creature
+ // -> Creature selection is ignored <-
+ if(target)
+ {
+ SendSysMessage(LANG_WAYPOINT_CREATSELECTED);
+ }
+ lowguid = atoi((char*)guid_str);
+
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT));
+ if(!target)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ // lowguid -> GUID of the NPC
+ // point -> number of the waypoint (if not 0)
+ sLog.outDebug("DEBUG: HandleWpAddCommand - danach");
+
+ sLog.outDebug("DEBUG: HandleWpAddCommand - point == 0");
+
+ Player* player = m_session->GetPlayer();
+ WaypointMgr.AddLastNode(lowguid, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), 0, 0);
+
+ // update movement type
+ if(target)
+ {
+ target->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
+ target->GetMotionMaster()->Initialize();
+ if(target->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ target->setDeathState(JUST_DIED);
+ target->Respawn();
+ }
+ target->SaveToDB();
+ }
+ else
+ WorldDatabase.PExecuteLog("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE,lowguid);
+
+ PSendSysMessage(LANG_WAYPOINT_ADDED, point, lowguid);
+
+ return true;
+} // HandleWpAddCommand
+
+/**
+ * .wp modify emote | spell | text | del | move | add
+ *
+ * add -> add a WP after the selected visual waypoint
+ * User must select a visual waypoint and then issue ".wp modify add"
+ *
+ * emote <emoteID>
+ * User has selected a visual waypoint before.
+ * <emoteID> is added to this waypoint. Everytime the
+ * NPC comes to this waypoint, the emote is called.
+ *
+ * emote <GUID> <WPNUM> <emoteID>
+ * User has not selected visual waypoint before.
+ * For the waypoint <WPNUM> for the NPC with <GUID>
+ * an emote <emoteID> is added.
+ * Everytime the NPC comes to this waypoint, the emote is called.
+ *
+ *
+ * info <GUID> <WPNUM> -> User did not select a visual waypoint and
+ */
+bool ChatHandler::HandleWpModifyCommand(const char* args)
+{
+ sLog.outDebug("DEBUG: HandleWpModifyCommand");
+
+ if(!*args)
+ return false;
+
+ // first arg: add del text emote spell waittime move
+ char* show_str = strtok((char*)args, " ");
+ if (!show_str)
+ {
+ return false;
+ }
+
+ std::string show = show_str;
+ // Check
+ // Remember: "show" must also be the name of a column!
+ if( (show != "emote") && (show != "spell") && (show != "text1") && (show != "text2")
+ && (show != "text3") && (show != "text4") && (show != "text5")
+ && (show != "waittime") && (show != "del") && (show != "move") && (show != "add")
+ && (show != "model1") && (show != "model2") && (show != "orientation"))
+ {
+ return false;
+ }
+
+ // Next arg is: <GUID> <WPNUM> <ARGUMENT>
+
+ // Did user provide a GUID
+ // or did the user select a creature?
+ // -> variable lowguid is filled with the GUID of the NPC
+ uint32 lowguid = 0;
+ uint32 point = 0;
+ uint32 wpGuid = 0;
+ Creature* target = getSelectedCreature();
+
+ if(target)
+ {
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - User did select an NPC");
+
+ // Did the user select a visual spawnpoint?
+ if (target->GetEntry() != VISUAL_WAYPOINT )
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_SELECT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ wpGuid = target->GetGUIDLow();
+
+ // The visual waypoint
+ QueryResult *result =
+ WorldDatabase.PQuery( "SELECT id, point FROM creature_movement WHERE wpguid = %u LIMIT 1",
+ target->GetGUIDLow() );
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, wpGuid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - After getting wpGuid");
+
+ Field *fields = result->Fetch();
+ lowguid = fields[0].GetUInt32();
+ point = fields[1].GetUInt32();
+
+ // Cleanup memory
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - Cleanup memory");
+ delete result;
+ }
+ else
+ {
+ // User did provide <GUID> <WPNUM>
+
+ char* guid_str = strtok((char*)NULL, " ");
+ if( !guid_str )
+ {
+ SendSysMessage(LANG_WAYPOINT_NOGUID);
+ return false;
+ }
+ lowguid = atoi((char*)guid_str);
+
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage("DEBUG: GUID provided: %d", lowguid);
+
+ char* point_str = strtok((char*)NULL, " ");
+ if( !point_str )
+ {
+ SendSysMessage(LANG_WAYPOINT_NOWAYPOINTGIVEN);
+ return false;
+ }
+ point = atoi((char*)point_str);
+
+ PSendSysMessage("DEBUG: wpNumber provided: %d", point);
+
+ // Now we need the GUID of the visual waypoint
+ // -> "del", "move", "add" command
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT wpguid FROM creature_movement WHERE id = '%u' AND point = '%u' LIMIT 1", lowguid, point);
+ if (!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, lowguid, point);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Field *fields = result->Fetch();
+ wpGuid = fields[0].GetUInt32();
+
+ // Free memory
+ delete result;
+ }
+
+ char* arg_str = NULL;
+ // Check for argument
+ if( (show.find("text") == std::string::npos ) && (show != "del") && (show != "move") && (show != "add"))
+ {
+ // Text is enclosed in "<>", all other arguments not
+ if( show.find("text") != std::string::npos )
+ arg_str = strtok((char*)NULL, "<>");
+ else
+ arg_str = strtok((char*)NULL, " ");
+
+ if( !arg_str)
+ {
+ PSendSysMessage(LANG_WAYPOINT_ARGUMENTREQ, show_str);
+ return false;
+ }
+ }
+
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - Parameters parsed - now execute the command");
+
+ // wpGuid -> GUID of the waypoint creature
+ // lowguid -> GUID of the NPC
+ // point -> waypoint number
+
+ // Special functions:
+ // add - move - del -> no args commands
+ // Add a waypoint after the selected visual
+ if(show == "add" && target)
+ {
+ PSendSysMessage("DEBUG: wp modify add, GUID: %u", lowguid);
+
+ // Get the creature for which we read the waypoint
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
+
+ if( !npcCreature )
+ {
+ PSendSysMessage(LANG_WAYPOINT_NPCNOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - add -- npcCreature");
+
+ // What to do:
+ // Add the visual spawnpoint (DB only)
+ // Adjust the waypoints
+ // Respawn the owner of the waypoints
+ sLog.outDebug("DEBUG: HandleWpModifyCommand - add");
+
+ Player* chr = m_session->GetPlayer();
+ Map *map = chr->GetMap();
+
+ if(npcCreature)
+ {
+ npcCreature->GetMotionMaster()->Initialize();
+ if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ npcCreature->setDeathState(JUST_DIED);
+ npcCreature->Respawn();
+ }
+ }
+
+ // create the waypoint creature
+ wpGuid = 0;
+ Creature* wpCreature = new Creature;
+ if (!wpCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map,VISUAL_WAYPOINT,0))
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
+ delete wpCreature;
+ }
+ else
+ {
+ wpCreature->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation());
+
+ if(!wpCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature->GetGUIDLow(),wpCreature->GetEntry(),wpCreature->GetPositionX(),wpCreature->GetPositionY());
+ delete wpCreature;
+ }
+ else
+ {
+ wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+ // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
+ wpCreature->LoadFromDB(wpCreature->GetDBTableGUIDLow(), map);
+ map->Add(wpCreature);
+ wpGuid = wpCreature->GetGUIDLow();
+ }
+ }
+
+ WaypointMgr.AddAfterNode(lowguid, point, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), 0, 0, wpGuid);
+
+ if(!wpGuid)
+ return false;
+
+ PSendSysMessage(LANG_WAYPOINT_ADDED_NO, point+1);
+ return true;
+ } // add
+
+ if(show == "del" && target)
+ {
+ PSendSysMessage("DEBUG: wp modify del, GUID: %u", lowguid);
+
+ // Get the creature for which we read the waypoint
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
+
+ // wpCreature
+ Creature* wpCreature = NULL;
+ if( wpGuid != 0 )
+ {
+ wpCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT));
+ wpCreature->DeleteFromDB();
+ wpCreature->CleanupsBeforeDelete();
+ wpCreature->AddObjectToRemoveList();
+ }
+
+ // What to do:
+ // Remove the visual spawnpoint
+ // Adjust the waypoints
+ // Respawn the owner of the waypoints
+
+ WaypointMgr.DeleteNode(lowguid, point);
+
+ if(npcCreature)
+ {
+ // Any waypoints left?
+ QueryResult *result2 = WorldDatabase.PQuery( "SELECT point FROM creature_movement WHERE id = '%u'",lowguid);
+ if(!result2)
+ {
+ npcCreature->SetDefaultMovementType(RANDOM_MOTION_TYPE);
+ }
+ else
+ {
+ npcCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
+ delete result2;
+ }
+ npcCreature->GetMotionMaster()->Initialize();
+ if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ npcCreature->setDeathState(JUST_DIED);
+ npcCreature->Respawn();
+ }
+ npcCreature->SaveToDB();
+ }
+
+ PSendSysMessage(LANG_WAYPOINT_REMOVED);
+ return true;
+ } // del
+
+ if(show == "move" && target)
+ {
+ PSendSysMessage("DEBUG: wp move, GUID: %u", lowguid);
+
+ Player *chr = m_session->GetPlayer();
+ Map *map = chr->GetMap();
+ {
+ // Get the creature for which we read the waypoint
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
+
+ // wpCreature
+ Creature* wpCreature = NULL;
+ // What to do:
+ // Move the visual spawnpoint
+ // Respawn the owner of the waypoints
+ if( wpGuid != 0 )
+ {
+ wpCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT));
+ wpCreature->DeleteFromDB();
+ wpCreature->CleanupsBeforeDelete();
+ wpCreature->AddObjectToRemoveList();
+ // re-create
+ Creature* wpCreature2 = new Creature;
+ if (!wpCreature2->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, VISUAL_WAYPOINT, 0))
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, VISUAL_WAYPOINT);
+ delete wpCreature2;
+ return false;
+ }
+
+ wpCreature2->Relocate(chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), chr->GetOrientation());
+
+ if(!wpCreature2->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature2->GetGUIDLow(),wpCreature2->GetEntry(),wpCreature2->GetPositionX(),wpCreature2->GetPositionY());
+ delete wpCreature2;
+ return false;
+ }
+
+ wpCreature2->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+ // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
+ wpCreature2->LoadFromDB(wpCreature2->GetDBTableGUIDLow(), map);
+ map->Add(wpCreature2);
+ //MapManager::Instance().GetMap(npcCreature->GetMapId())->Add(wpCreature2);
+ }
+
+ WaypointMgr.SetNodePosition(lowguid, point, chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ());
+
+ if(npcCreature)
+ {
+ npcCreature->GetMotionMaster()->Initialize();
+ if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ npcCreature->setDeathState(JUST_DIED);
+ npcCreature->Respawn();
+ }
+ }
+ PSendSysMessage(LANG_WAYPOINT_CHANGED);
+ }
+ return true;
+ } // move
+
+ // Create creature - npc that has the waypoint
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ WaypointMgr.SetNodeText(lowguid, point, show_str, arg_str);
+
+ Creature* npcCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), MAKE_NEW_GUID(lowguid, data->id, HIGHGUID_UNIT));
+ if(npcCreature)
+ {
+ npcCreature->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
+ npcCreature->GetMotionMaster()->Initialize();
+ if(npcCreature->isAlive()) // dead creature will reset movement generator at respawn
+ {
+ npcCreature->setDeathState(JUST_DIED);
+ npcCreature->Respawn();
+ }
+ }
+ PSendSysMessage(LANG_WAYPOINT_CHANGED_NO, show_str);
+
+ return true;
+}
+
+/**
+ * .wp show info | on | off
+ *
+ * info -> User has selected a visual waypoint before
+ *
+ * info <GUID> <WPNUM> -> User did not select a visual waypoint and
+ * provided the GUID of the NPC and the number of
+ * the waypoint.
+ *
+ * on -> User has selected an NPC; all visual waypoints for this
+ * NPC are added to the world
+ *
+ * on <GUID> -> User did not select an NPC - instead the GUID of the
+ * NPC is provided. All visual waypoints for this NPC
+ * are added from the world.
+ *
+ * off -> User has selected an NPC; all visual waypoints for this
+ * NPC are removed from the world.
+ *
+ * on <GUID> -> User did not select an NPC - instead the GUID of the
+ * NPC is provided. All visual waypoints for this NPC
+ * are removed from the world.
+ *
+ *
+ */
+bool ChatHandler::HandleWpShowCommand(const char* args)
+{
+ sLog.outDebug("DEBUG: HandleWpShowCommand");
+
+ if(!*args)
+ return false;
+
+ // first arg: on, off, first, last
+ char* show_str = strtok((char*)args, " ");
+ if (!show_str)
+ {
+ return false;
+ }
+ // second arg: GUID (optional, if a creature is selected)
+ char* guid_str = strtok((char*)NULL, " ");
+ sLog.outDebug("DEBUG: HandleWpShowCommand: show_str: %s guid_str: %s", show_str, guid_str);
+ //if (!guid_str) {
+ // return false;
+ //}
+
+ // Did user provide a GUID
+ // or did the user select a creature?
+ // -> variable lowguid is filled with the GUID
+ Creature* target = getSelectedCreature();
+ // Did player provide a GUID?
+ if (!guid_str)
+ {
+ sLog.outDebug("DEBUG: HandleWpShowCommand: !guid_str");
+ // No GUID provided
+ // -> Player must have selected a creature
+
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ sLog.outDebug("DEBUG: HandleWpShowCommand: GUID provided");
+ // GUID provided
+ // Warn if player also selected a creature
+ // -> Creature selection is ignored <-
+ if(target)
+ {
+ SendSysMessage(LANG_WAYPOINT_CREATSELECTED);
+ }
+
+ uint32 lowguid = atoi((char*)guid_str);
+
+ CreatureData const* data = objmgr.GetCreatureData(lowguid);
+ if(!data)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT));
+
+ if(!target)
+ {
+ PSendSysMessage(LANG_WAYPOINT_CREATNOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+
+ uint32 lowguid = target->GetDBTableGUIDLow();
+
+ std::string show = show_str;
+ uint32 Maxpoint;
+
+ sLog.outDebug("DEBUG: HandleWpShowCommand: lowguid: %u", lowguid);
+
+ sLog.outDebug("DEBUG: HandleWpShowCommand: Habe creature: %ld", target );
+
+ sLog.outDebug("DEBUG: HandleWpShowCommand: wpshow - show: %s", show_str);
+ //PSendSysMessage("wpshow - show: %s", show);
+
+ // Show info for the selected waypoint
+ if(show == "info")
+ {
+ PSendSysMessage("DEBUG: wp info, GUID: %u", lowguid);
+
+ // Check if the user did specify a visual waypoint
+ if( target->GetEntry() != VISUAL_WAYPOINT )
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_SELECT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ //PSendSysMessage("wp on, GUID: %u", lowguid);
+
+ //pCreature->GetPositionX();
+
+ QueryResult *result =
+ WorldDatabase.PQuery( "SELECT id, point, waittime, emote, spell, text1, text2, text3, text4, text5, model1, model2 FROM creature_movement WHERE wpguid = %u",
+ target->GetGUID() );
+ if(!result)
+ {
+ // Since we compare float values, we have to deal with
+ // some difficulties.
+ // Here we search for all waypoints that only differ in one from 1 thousand
+ // (0.001) - There is no other way to compare C++ floats with mySQL floats
+ // See also: http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html
+ const char* maxDIFF = "0.01";
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, target->GetGUID());
+
+ result = WorldDatabase.PQuery( "SELECT id, point, waittime, emote, spell, text1, text2, text3, text4, text5, model1, model2 FROM creature_movement WHERE (abs(position_x - %f) <= %s ) and (abs(position_y - %f) <= %s ) and (abs(position_z - %f) <= %s )",
+ target->GetPositionX(), maxDIFF, target->GetPositionY(), maxDIFF, target->GetPositionZ(), maxDIFF);
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 creGUID = fields[0].GetUInt32();
+ uint32 point = fields[1].GetUInt32();
+ int waittime = fields[2].GetUInt32();
+ uint32 emote = fields[3].GetUInt32();
+ uint32 spell = fields[4].GetUInt32();
+ const char * text1 = fields[5].GetString();
+ const char * text2 = fields[6].GetString();
+ const char * text3 = fields[7].GetString();
+ const char * text4 = fields[8].GetString();
+ const char * text5 = fields[9].GetString();
+ uint32 model1 = fields[10].GetUInt32();
+ uint32 model2 = fields[11].GetUInt32();
+
+ // Get the creature for which we read the waypoint
+ Creature* wpCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(creGUID,VISUAL_WAYPOINT,HIGHGUID_UNIT));
+
+ PSendSysMessage(LANG_WAYPOINT_INFO_TITLE, point, (wpCreature ? wpCreature->GetName() : "<not found>"), creGUID);
+ PSendSysMessage(LANG_WAYPOINT_INFO_WAITTIME, waittime);
+ PSendSysMessage(LANG_WAYPOINT_INFO_MODEL, 1, model1);
+ PSendSysMessage(LANG_WAYPOINT_INFO_MODEL, 2, model2);
+ PSendSysMessage(LANG_WAYPOINT_INFO_EMOTE, emote);
+ PSendSysMessage(LANG_WAYPOINT_INFO_SPELL, spell);
+ PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 1, text1);
+ PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 2, text2);
+ PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 3, text3);
+ PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 4, text4);
+ PSendSysMessage(LANG_WAYPOINT_INFO_TEXT, 5, text5);
+
+ }while( result->NextRow() );
+ // Cleanup memory
+ delete result;
+ return true;
+ }
+
+ if(show == "on")
+ {
+ PSendSysMessage("DEBUG: wp on, GUID: %u", lowguid);
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT point, position_x,position_y,position_z FROM creature_movement WHERE id = '%u'",lowguid);
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ // Delete all visuals for this NPC
+ QueryResult *result2 = WorldDatabase.PQuery( "SELECT wpguid FROM creature_movement WHERE id = '%u' and wpguid <> 0", lowguid);
+ if(result2)
+ {
+ bool hasError = false;
+ do
+ {
+ Field *fields = result2->Fetch();
+ uint32 wpguid = fields[0].GetUInt32();
+ Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(wpguid,VISUAL_WAYPOINT,HIGHGUID_UNIT));
+
+ if(!pCreature)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, wpguid);
+ hasError = true;
+ WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", wpguid);
+ }
+ else
+ {
+ pCreature->DeleteFromDB();
+ pCreature->CleanupsBeforeDelete();
+ pCreature->AddObjectToRemoveList();
+ }
+
+ }while( result2->NextRow() );
+ delete result2;
+ if( hasError )
+ {
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR1);
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR2);
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR3);
+ }
+ }
+
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 point = fields[0].GetUInt32();
+ float x = fields[1].GetFloat();
+ float y = fields[2].GetFloat();
+ float z = fields[3].GetFloat();
+
+ uint32 id = VISUAL_WAYPOINT;
+
+ Player *chr = m_session->GetPlayer();
+ Map *map = chr->GetMap();
+ float o = chr->GetOrientation();
+
+ Creature* wpCreature = new Creature;
+ if (!wpCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, 0))
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
+ delete wpCreature;
+ delete result;
+ return false;
+ }
+
+ wpCreature->Relocate(x, y, z, o);
+
+ if(!wpCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",wpCreature->GetGUIDLow(),wpCreature->GetEntry(),wpCreature->GetPositionX(),wpCreature->GetPositionY());
+ delete wpCreature;
+ delete result;
+ return false;
+ }
+
+ wpCreature->SetVisibility(VISIBILITY_OFF);
+ sLog.outDebug("DEBUG: UPDATE creature_movement SET wpguid = '%u");
+ // set "wpguid" column to the visual waypoint
+ WorldDatabase.PExecuteLog("UPDATE creature_movement SET wpguid = '%u' WHERE id = '%u' and point = '%u'", wpCreature->GetGUIDLow(), lowguid, point);
+
+ wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+ // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
+ wpCreature->LoadFromDB(wpCreature->GetDBTableGUIDLow(),map);
+ map->Add(wpCreature);
+ //MapManager::Instance().GetMap(wpCreature->GetMapId())->Add(wpCreature);
+ }while( result->NextRow() );
+
+ // Cleanup memory
+ delete result;
+ return true;
+ }
+
+ if(show == "first")
+ {
+ PSendSysMessage("DEBUG: wp first, GUID: %u", lowguid);
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT position_x,position_y,position_z FROM creature_movement WHERE point='1' AND id = '%u'",lowguid);
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUND, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Field *fields = result->Fetch();
+ float x = fields[0].GetFloat();
+ float y = fields[1].GetFloat();
+ float z = fields[2].GetFloat();
+ uint32 id = VISUAL_WAYPOINT;
+
+ Player *chr = m_session->GetPlayer();
+ float o = chr->GetOrientation();
+ Map *map = chr->GetMap();
+
+ Creature* pCreature = new Creature;
+ if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT),map, id, 0))
+ {
+ PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id);
+ delete pCreature;
+ delete result;
+ return false;
+ }
+
+ pCreature->Relocate(x, y, z, o);
+
+ if(!pCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
+ delete pCreature;
+ delete result;
+ return false;
+ }
+
+ pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+ pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map);
+ map->Add(pCreature);
+ //player->PlayerTalkClass->SendPointOfInterest(x, y, 6, 6, 0, "First Waypoint");
+
+ // Cleanup memory
+ delete result;
+ return true;
+ }
+
+ if(show == "last")
+ {
+ PSendSysMessage("DEBUG: wp last, GUID: %u", lowguid);
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'",lowguid);
+ if( result )
+ {
+ Maxpoint = (*result)[0].GetUInt32();
+
+ delete result;
+ }
+ else
+ Maxpoint = 0;
+
+ result = WorldDatabase.PQuery( "SELECT position_x,position_y,position_z FROM creature_movement WHERE point ='%u' AND id = '%u'",Maxpoint, lowguid);
+ if(!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDLAST, lowguid);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ Field *fields = result->Fetch();
+ float x = fields[0].GetFloat();
+ float y = fields[1].GetFloat();
+ float z = fields[2].GetFloat();
+ uint32 id = VISUAL_WAYPOINT;
+
+ Player *chr = m_session->GetPlayer();
+ float o = chr->GetOrientation();
+ Map *map = chr->GetMap();
+
+ Creature* pCreature = new Creature;
+ if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, id, 0))
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id);
+ delete pCreature;
+ delete result;
+ return false;
+ }
+
+ pCreature->Relocate(x, y, z, o);
+
+ if(!pCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Creature (guidlow %d, entry %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY());
+ delete pCreature;
+ delete result;
+ return false;
+ }
+
+ pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+ pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map);
+ map->Add(pCreature);
+ //player->PlayerTalkClass->SendPointOfInterest(x, y, 6, 6, 0, "Last Waypoint");
+ // Cleanup memory
+ delete result;
+ return true;
+ }
+
+ if(show == "off")
+ {
+ QueryResult *result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE id = '%d'", VISUAL_WAYPOINT);
+ if(!result)
+ {
+ SendSysMessage(LANG_WAYPOINT_VP_NOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ bool hasError = false;
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 guid = fields[0].GetUInt32();
+ Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(),MAKE_NEW_GUID(guid,VISUAL_WAYPOINT,HIGHGUID_UNIT));
+
+ //Creature* pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
+
+ if(!pCreature)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid);
+ hasError = true;
+ WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", guid);
+ }
+ else
+ {
+ pCreature->DeleteFromDB();
+ pCreature->CleanupsBeforeDelete();
+ pCreature->AddObjectToRemoveList();
+ }
+ }while(result->NextRow());
+ // set "wpguid" column to "empty" - no visual waypoint spawned
+ WorldDatabase.PExecuteLog("UPDATE creature_movement SET wpguid = '0'");
+
+ if( hasError )
+ {
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR1);
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR2);
+ PSendSysMessage(LANG_WAYPOINT_TOOFAR3);
+ }
+
+ SendSysMessage(LANG_WAYPOINT_VP_ALLREMOVED);
+ // Cleanup memory
+ delete result;
+
+ return true;
+ }
+
+ PSendSysMessage("DEBUG: wpshow - no valid command found");
+
+ return true;
+} // HandleWpShowCommand
+
+bool ChatHandler::HandleWpExportCommand(const char *args)
+{
+ if(!*args)
+ return false;
+
+ // Next arg is: <GUID> <ARGUMENT>
+
+ // Did user provide a GUID
+ // or did the user select a creature?
+ // -> variable lowguid is filled with the GUID of the NPC
+ uint32 lowguid = 0;
+ Creature* target = getSelectedCreature();
+ char* arg_str = NULL;
+ if (target)
+ {
+ if (target->GetEntry() != VISUAL_WAYPOINT)
+ lowguid = target->GetGUIDLow();
+ else
+ {
+ QueryResult *result = WorldDatabase.PQuery( "SELECT id FROM creature_movement WHERE wpguid = %u LIMIT 1", target->GetGUIDLow() );
+ if (!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM, target->GetGUIDLow());
+ return true;
+ }
+ Field *fields = result->Fetch();
+ lowguid = fields[0].GetUInt32();;
+ delete result;
+ }
+
+ arg_str = strtok((char*)args, " ");
+ }
+ else
+ {
+ // user provided <GUID>
+ char* guid_str = strtok((char*)args, " ");
+ if( !guid_str )
+ {
+ SendSysMessage(LANG_WAYPOINT_NOGUID);
+ return false;
+ }
+ lowguid = atoi((char*)guid_str);
+
+ arg_str = strtok((char*)NULL, " ");
+ }
+
+ if( !arg_str)
+ {
+ PSendSysMessage(LANG_WAYPOINT_ARGUMENTREQ, "export");
+ return false;
+ }
+
+ PSendSysMessage("DEBUG: wp export, GUID: %u", lowguid);
+
+ QueryResult *result = WorldDatabase.PQuery(
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ "SELECT point, position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5, id FROM creature_movement WHERE id = '%u' ORDER BY point", lowguid );
+
+ if (!result)
+ {
+ PSendSysMessage(LANG_WAYPOINT_NOTHINGTOEXPORT);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ std::ofstream outfile;
+ outfile.open (arg_str);
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ outfile << "INSERT INTO creature_movement ";
+ outfile << "( id, point, position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5 ) VALUES ";
+
+ outfile << "( ";
+ outfile << fields[15].GetUInt32(); // id
+ outfile << ", ";
+ outfile << fields[0].GetUInt32(); // point
+ outfile << ", ";
+ outfile << fields[1].GetFloat(); // position_x
+ outfile << ", ";
+ outfile << fields[2].GetFloat(); // position_y
+ outfile << ", ";
+ outfile << fields[3].GetUInt32(); // position_z
+ outfile << ", ";
+ outfile << fields[4].GetUInt32(); // orientation
+ outfile << ", ";
+ outfile << fields[5].GetUInt32(); // model1
+ outfile << ", ";
+ outfile << fields[6].GetUInt32(); // model2
+ outfile << ", ";
+ outfile << fields[7].GetUInt16(); // waittime
+ outfile << ", ";
+ outfile << fields[8].GetUInt32(); // emote
+ outfile << ", ";
+ outfile << fields[9].GetUInt32(); // spell
+ outfile << ", ";
+ const char *tmpChar = fields[10].GetString();
+ if( !tmpChar )
+ {
+ outfile << "NULL"; // text1
+ }
+ else
+ {
+ outfile << "'";
+ outfile << tmpChar; // text1
+ outfile << "'";
+ }
+ outfile << ", ";
+ tmpChar = fields[11].GetString();
+ if( !tmpChar )
+ {
+ outfile << "NULL"; // text2
+ }
+ else
+ {
+ outfile << "'";
+ outfile << tmpChar; // text2
+ outfile << "'";
+ }
+ outfile << ", ";
+ tmpChar = fields[12].GetString();
+ if( !tmpChar )
+ {
+ outfile << "NULL"; // text3
+ }
+ else
+ {
+ outfile << "'";
+ outfile << tmpChar; // text3
+ outfile << "'";
+ }
+ outfile << ", ";
+ tmpChar = fields[13].GetString();
+ if( !tmpChar )
+ {
+ outfile << "NULL"; // text4
+ }
+ else
+ {
+ outfile << "'";
+ outfile << tmpChar; // text4
+ outfile << "'";
+ }
+ outfile << ", ";
+ tmpChar = fields[14].GetString();
+ if( !tmpChar )
+ {
+ outfile << "NULL"; // text5
+ }
+ else
+ {
+ outfile << "'";
+ outfile << tmpChar; // text5
+ outfile << "'";
+ }
+ outfile << ");\n ";
+
+ } while( result->NextRow() );
+ delete result;
+
+ PSendSysMessage(LANG_WAYPOINT_EXPORTED);
+ outfile.close();
+
+ return true;
+}
+
+bool ChatHandler::HandleWpImportCommand(const char *args)
+{
+ if(!*args)
+ return false;
+
+ char* arg_str = strtok((char*)args, " ");
+ if (!arg_str)
+ return false;
+
+ std::string line;
+ std::ifstream infile (arg_str);
+ if (infile.is_open())
+ {
+ while (! infile.eof() )
+ {
+ getline (infile,line);
+ //cout << line << endl;
+ QueryResult *result = WorldDatabase.PQuery(line.c_str());
+ delete result;
+ }
+ infile.close();
+ }
+ PSendSysMessage(LANG_WAYPOINT_IMPORTED);
+
+ return true;
+}
+
+//rename characters
+bool ChatHandler::HandleRenameCommand(const char* args)
+{
+ Player* target = NULL;
+ uint64 targetGUID = 0;
+ std::string oldname;
+
+ char* px = strtok((char*)args, " ");
+
+ if(px)
+ {
+ oldname = px;
+
+ if(!normalizePlayerName(oldname))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target = objmgr.GetPlayer(oldname.c_str());
+
+ if (!target)
+ targetGUID = objmgr.GetPlayerGUIDByName(oldname);
+ }
+
+ if(!target && !targetGUID)
+ {
+ target = getSelectedPlayer();
+ }
+
+ if(!target && !targetGUID)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(target)
+ {
+ PSendSysMessage(LANG_RENAME_PLAYER, target->GetName());
+ target->SetAtLoginFlag(AT_LOGIN_RENAME);
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '1' WHERE guid = '%u'", target->GetGUIDLow());
+ }
+ else
+ {
+ PSendSysMessage(LANG_RENAME_PLAYER_GUID, oldname.c_str(), GUID_LOPART(targetGUID));
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '1' WHERE guid = '%u'", GUID_LOPART(targetGUID));
+ }
+
+ return true;
+}
+
+//spawn go
+bool ChatHandler::HandleGameObjectCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ char* pParam1 = strtok((char*)args, " ");
+ if (!pParam1)
+ return false;
+
+ uint32 id = atoi((char*)pParam1);
+ if(!id)
+ return false;
+
+ char* spawntimeSecs = strtok(NULL, " ");
+
+ const GameObjectInfo *goI = objmgr.GetGameObjectInfo(id);
+
+ if (!goI)
+ {
+ PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player *chr = m_session->GetPlayer();
+ float x = float(chr->GetPositionX());
+ float y = float(chr->GetPositionY());
+ float z = float(chr->GetPositionZ());
+ float o = float(chr->GetOrientation());
+ Map *map = chr->GetMap();
+
+ float rot2 = sin(o/2);
+ float rot3 = cos(o/2);
+
+ GameObject* pGameObj = new GameObject;
+ uint32 db_lowGUID = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
+
+ if(!pGameObj->Create(db_lowGUID, goI->id, map, x, y, z, o, 0, 0, rot2, rot3, 0, 1))
+ {
+ delete pGameObj;
+ return false;
+ }
+
+ if( spawntimeSecs )
+ {
+ uint32 value = atoi((char*)spawntimeSecs);
+ pGameObj->SetRespawnTime(value);
+ //sLog.outDebug("*** spawntimeSecs: %d", value);
+ }
+
+ // fill the gameobject data and save to the db
+ pGameObj->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()));
+
+ // this will generate a new guid if the object is in an instance
+ if(!pGameObj->LoadFromDB(db_lowGUID, map))
+ {
+ delete pGameObj;
+ return false;
+ }
+
+ sLog.outDebug(GetMangosString(LANG_GAMEOBJECT_CURRENT), goI->name, db_lowGUID, x, y, z, o);
+
+ map->Add(pGameObj);
+
+ // TODO: is it really necessary to add both the real and DB table guid here ?
+ objmgr.AddGameobjectToGrid(db_lowGUID, objmgr.GetGOData(db_lowGUID));
+
+ PSendSysMessage(LANG_GAMEOBJECT_ADD,id,goI->name,db_lowGUID,x,y,z);
+ return true;
+}
+
+//show animation
+bool ChatHandler::HandleAnimCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 anim_id = atoi((char*)args);
+ m_session->GetPlayer()->HandleEmoteCommand(anim_id);
+ return true;
+}
+
+//change standstate
+bool ChatHandler::HandleStandStateCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 anim_id = atoi((char*)args);
+ m_session->GetPlayer( )->SetUInt32Value( UNIT_NPC_EMOTESTATE , anim_id );
+
+ return true;
+}
+
+bool ChatHandler::HandleAddHonorCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ Player *target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 amount = (uint32)atoi(args);
+ target->RewardHonor(NULL, 1, amount);
+ return true;
+}
+
+bool ChatHandler::HandleHonorAddKillCommand(const char* /*args*/)
+{
+ Unit *target = getSelectedUnit();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ m_session->GetPlayer()->RewardHonor(target, 1);
+ return true;
+}
+
+bool ChatHandler::HandleUpdateHonorFieldsCommand(const char* /*args*/)
+{
+ Player *target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target->UpdateHonorFields();
+ return true;
+}
+
+bool ChatHandler::HandleLookupEventCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ // converting string that we try to find to lower case
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ wstrToLower(wnamepart);
+
+ uint32 counter = 0;
+
+ GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
+ GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+
+ for(uint32 id = 0; id < events.size(); ++id )
+ {
+ GameEventData const& eventData = events[id];
+
+ std::string descr = eventData.description;
+ if(descr.empty())
+ continue;
+
+ if (Utf8FitTo(descr, wnamepart))
+ {
+ char const* active = activeEvents.find(id) != activeEvents.end() ? GetMangosString(LANG_ACTIVE) : "";
+ PSendSysMessage(LANG_EVENT_ENTRY_LIST,id,id,descr.c_str(),active );
+ ++counter;
+ }
+ }
+
+ if (counter==0)
+ SendSysMessage(LANG_NOEVENTFOUND);
+
+ return true;
+}
+
+bool ChatHandler::HandleEventActiveListCommand(const char* args)
+{
+ uint32 counter = 0;
+
+ GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
+ GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+
+ char const* active = GetMangosString(LANG_ACTIVE);
+
+ for(GameEvent::ActiveEvents::const_iterator itr = activeEvents.begin(); itr != activeEvents.end(); ++itr )
+ {
+ uint32 event_id = *itr;
+ GameEventData const& eventData = events[event_id];
+
+ PSendSysMessage(LANG_EVENT_ENTRY_LIST,event_id,event_id,eventData.description.c_str(),active );
+ ++counter;
+ }
+
+ if (counter==0)
+ SendSysMessage(LANG_NOEVENTFOUND);
+
+ return true;
+}
+
+bool ChatHandler::HandleEventInfoCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameevent");
+ if(!cId)
+ return false;
+
+ uint32 event_id = atoi(cId);
+
+ GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
+
+ if(event_id >=events.size())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEventData const& eventData = events[event_id];
+ if(!eventData.isValid())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+ bool active = activeEvents.find(event_id) != activeEvents.end();
+ char const* activeStr = active ? GetMangosString(LANG_ACTIVE) : "";
+
+ std::string startTimeStr = TimeToTimestampStr(eventData.start);
+ std::string endTimeStr = TimeToTimestampStr(eventData.end);
+
+ uint32 delay = gameeventmgr.NextCheck(event_id);
+ time_t nextTime = time(NULL)+delay;
+ std::string nextStr = nextTime >= eventData.start && nextTime < eventData.end ? TimeToTimestampStr(time(NULL)+delay) : "-";
+
+ std::string occurenceStr = secsToTimeString(eventData.occurence * MINUTE);
+ std::string lengthStr = secsToTimeString(eventData.length * MINUTE);
+
+ PSendSysMessage(LANG_EVENT_INFO,event_id,eventData.description.c_str(),activeStr,
+ startTimeStr.c_str(),endTimeStr.c_str(),occurenceStr.c_str(),lengthStr.c_str(),
+ nextStr.c_str());
+ return true;
+}
+
+bool ChatHandler::HandleEventStartCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameevent");
+ if(!cId)
+ return false;
+
+ int32 event_id = atoi(cId);
+
+ GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
+
+ if(event_id < 1 || event_id >=events.size())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEventData const& eventData = events[event_id];
+ if(!eventData.isValid())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+ if(activeEvents.find(event_id) != activeEvents.end())
+ {
+ PSendSysMessage(LANG_EVENT_ALREADY_ACTIVE,event_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ gameeventmgr.StartEvent(event_id,true);
+ return true;
+}
+
+bool ChatHandler::HandleEventStopCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // id or [name] Shift-click form |color|Hgameevent:id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameevent");
+ if(!cId)
+ return false;
+
+ int32 event_id = atoi(cId);
+
+ GameEvent::GameEventDataMap const& events = gameeventmgr.GetEventMap();
+
+ if(event_id < 1 || event_id >=events.size())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEventData const& eventData = events[event_id];
+ if(!eventData.isValid())
+ {
+ SendSysMessage(LANG_EVENT_NOT_EXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameEvent::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+
+ if(activeEvents.find(event_id) == activeEvents.end())
+ {
+ PSendSysMessage(LANG_EVENT_NOT_ACTIVE,event_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ gameeventmgr.StopEvent(event_id,true);
+ return true;
+}
+
+bool ChatHandler::HandleCombatStopCommand(const char* args)
+{
+ Player *player;
+
+ if(*args)
+ {
+ std::string playername = args;
+
+ if(!normalizePlayerName(playername))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player = objmgr.GetPlayer(playername.c_str());
+
+ if(!player)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ else
+ {
+ player = getSelectedPlayer();
+
+ if (!player)
+ player = m_session->GetPlayer();
+ }
+
+ player->CombatStop();
+ player->getHostilRefManager().deleteReferences();
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllCraftsCommand(const char* /*args*/)
+{
+ uint32 classmask = m_session->GetPlayer()->getClassMask();
+
+ for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
+ {
+ SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(i);
+ if( !skillInfo )
+ continue;
+
+ if( skillInfo->categoryId == SKILL_CATEGORY_PROFESSION || skillInfo->categoryId == SKILL_CATEGORY_SECONDARY )
+ {
+ for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
+ {
+ SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j);
+ if( !skillLine )
+ continue;
+
+ // skip racial skills
+ if( skillLine->racemask != 0 )
+ continue;
+
+ // skip wrong class skills
+ if( skillLine->classmask && (skillLine->classmask & classmask) == 0)
+ continue;
+
+ if( skillLine->skillId != i || skillLine->forward_spellid )
+ continue;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
+ continue;
+
+ m_session->GetPlayer()->learnSpell(skillLine->spellId);
+ }
+ }
+ }
+
+ SendSysMessage(LANG_COMMAND_LEARN_ALL_CRAFT);
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllRecipesCommand(const char* args)
+{
+ // Learns all recipes of specified profession and sets skill to max
+ // Example: .learn all_recipes enchanting
+
+ Player* target = getSelectedPlayer();
+ if( !target )
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ return false;
+ }
+
+ if(!*args)
+ return false;
+
+ std::wstring wnamepart;
+
+ if(!Utf8toWStr(args,wnamepart))
+ return false;
+
+ uint32 counter = 0; // Counter for figure out that we found smth.
+
+ // converting string that we try to find to lower case
+ wstrToLower( wnamepart );
+
+ uint32 classmask = m_session->GetPlayer()->getClassMask();
+
+ for (uint32 i = 0; i < sSkillLineStore.GetNumRows(); ++i)
+ {
+ SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(i);
+ if( !skillInfo )
+ continue;
+
+ if( skillInfo->categoryId != SKILL_CATEGORY_PROFESSION &&
+ skillInfo->categoryId != SKILL_CATEGORY_SECONDARY )
+ continue;
+
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = skillInfo->name[loc];
+
+ if(Utf8FitTo(name, wnamepart))
+ {
+ for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
+ {
+ SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j);
+ if( !skillLine )
+ continue;
+
+ if( skillLine->skillId != i || skillLine->forward_spellid )
+ continue;
+
+ // skip racial skills
+ if( skillLine->racemask != 0 )
+ continue;
+
+ // skip wrong class skills
+ if( skillLine->classmask && (skillLine->classmask & classmask) == 0)
+ continue;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
+ continue;
+
+ if( !target->HasSpell(spellInfo->Id) )
+ m_session->GetPlayer()->learnSpell(skillLine->spellId);
+ }
+
+ uint16 maxLevel = target->GetPureMaxSkillValue(skillInfo->id);
+ target->SetSkill(skillInfo->id, maxLevel, maxLevel);
+ PSendSysMessage(LANG_COMMAND_LEARN_ALL_RECIPES, name.c_str());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ChatHandler::HandleLookupPlayerIpCommand(const char* args)
+{
+
+ if(!*args)
+ return false;
+
+ std::string ip = strtok((char*)args, " ");
+ char* limit_str = strtok(NULL, " ");
+ int32 limit = limit_str ? atoi(limit_str) : -1;
+
+ loginDatabase.escape_string(ip);
+
+ QueryResult* result = loginDatabase.PQuery("SELECT id,username FROM account WHERE last_ip = '%s'", ip.c_str());
+
+ return LookupPlayerSearchCommand(result,limit);
+}
+
+bool ChatHandler::HandleLookupPlayerAccountCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string account = strtok((char*)args, " ");
+ char* limit_str = strtok(NULL, " ");
+ int32 limit = limit_str ? atoi(limit_str) : -1;
+
+ if(!AccountMgr::normilizeString(account))
+ return false;
+
+ loginDatabase.escape_string(account);
+
+ QueryResult* result = loginDatabase.PQuery("SELECT id,username FROM account WHERE username = '%s'", account.c_str());
+
+ return LookupPlayerSearchCommand(result,limit);
+}
+
+bool ChatHandler::HandleLookupPlayerEmailCommand(const char* args)
+{
+
+ if(!*args)
+ return false;
+
+ std::string email = strtok((char*)args, " ");
+ char* limit_str = strtok(NULL, " ");
+ int32 limit = limit_str ? atoi(limit_str) : -1;
+
+ loginDatabase.escape_string(email);
+
+ QueryResult* result = loginDatabase.PQuery("SELECT id,username FROM account WHERE email = '%s'", email.c_str());
+
+ return LookupPlayerSearchCommand(result,limit);
+}
+
+bool ChatHandler::LookupPlayerSearchCommand(QueryResult* result, int32 limit)
+{
+ if(!result)
+ {
+ PSendSysMessage(LANG_NO_PLAYERS_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int i =0;
+ do
+ {
+ Field* fields = result->Fetch();
+ uint32 acc_id = fields[0].GetUInt32();
+ std::string acc_name = fields[1].GetCppString();
+
+ QueryResult* chars = CharacterDatabase.PQuery("SELECT guid,name FROM characters WHERE account = '%u'", acc_id);
+ if(chars)
+ {
+ PSendSysMessage(LANG_LOOKUP_PLAYER_ACCOUNT,acc_name.c_str(),acc_id);
+
+ uint64 guid = 0;
+ std::string name;
+
+ do
+ {
+ Field* charfields = chars->Fetch();
+ guid = charfields[0].GetUInt64();
+ name = charfields[1].GetCppString();
+
+ PSendSysMessage(LANG_LOOKUP_PLAYER_CHARACTER,name.c_str(),guid);
+ ++i;
+
+ } while( chars->NextRow() && ( limit == -1 || i < limit ) );
+
+ delete chars;
+ }
+ } while(result->NextRow());
+
+ delete result;
+
+ return true;
+}
diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp
index ff849f7c9d1..c79bd3db21e 100644
--- a/src/game/Level3.cpp
+++ b/src/game/Level3.cpp
@@ -1,5466 +1,5466 @@
-/*
- * 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 "World.h"
-#include "ObjectMgr.h"
-#include "PlayerDump.h"
-#include "SpellMgr.h"
-#include "Player.h"
-#include "Opcodes.h"
-#include "GameObject.h"
-#include "Chat.h"
-#include "Log.h"
-#include "Guild.h"
-#include "ObjectAccessor.h"
-#include "MapManager.h"
-#include "SpellAuras.h"
-#include "ScriptCalls.h"
-#include "Language.h"
-#include "GridNotifiersImpl.h"
-#include "CellImpl.h"
-#include "Weather.h"
-#include "PointMovementGenerator.h"
-#include "TargetedMovementGenerator.h"
-#include "SkillDiscovery.h"
-#include "SkillExtraItems.h"
-#include "SystemConfig.h"
-#include "Config/ConfigEnv.h"
-#include "Util.h"
-#include "ItemEnchantmentMgr.h"
-#include "BattleGroundMgr.h"
-#include "InstanceSaveMgr.h"
-#include "InstanceData.h"
-
-//reload commands
-bool ChatHandler::HandleReloadCommand(const char* arg)
-{
- // this is error catcher for wrong table name in .reload commands
- PSendSysMessage("Db table with name starting from '%s' not found and can't be reloaded.",arg);
- SetSentErrorMessage(true);
- return false;
-}
-
-bool ChatHandler::HandleReloadAllCommand(const char*)
-{
- HandleReloadAreaTriggerTeleportCommand("");
- HandleReloadSkillFishingBaseLevelCommand("");
-
- HandleReloadAllAreaCommand("");
- HandleReloadAllLootCommand("");
- HandleReloadAllNpcCommand("");
- HandleReloadAllQuestCommand("");
- HandleReloadAllSpellCommand("");
- HandleReloadAllItemCommand("");
-
- HandleReloadCommandCommand("");
- HandleReloadReservedNameCommand("");
- HandleReloadMangosStringCommand("");
- HandleReloadGameTeleCommand("");
- return true;
-}
-
-bool ChatHandler::HandleReloadAllAreaCommand(const char*)
-{
- //HandleReloadQuestAreaTriggersCommand(""); -- reloaded in HandleReloadAllQuestCommand
- HandleReloadAreaTriggerTeleportCommand("");
- HandleReloadAreaTriggerTavernCommand("");
- HandleReloadGameGraveyardZoneCommand("");
- return true;
-}
-
-bool ChatHandler::HandleReloadAllLootCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables..." );
- LoadLootTables();
- SendGlobalSysMessage("DB tables `*_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadAllNpcCommand(const char* /*args*/)
-{
- HandleReloadNpcGossipCommand("a");
- HandleReloadNpcTrainerCommand("a");
- HandleReloadNpcVendorCommand("a");
- return true;
-}
-
-bool ChatHandler::HandleReloadAllQuestCommand(const char* /*args*/)
-{
- HandleReloadQuestAreaTriggersCommand("a");
- HandleReloadQuestTemplateCommand("a");
-
- sLog.outString( "Re-Loading Quests Relations..." );
- objmgr.LoadQuestRelations();
- SendGlobalSysMessage("DB tables `*_questrelation` and `*_involvedrelation` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadAllScriptsCommand(const char*)
-{
- if(sWorld.IsScriptScheduled())
- {
- PSendSysMessage("DB scripts used currently, please attempt reload later.");
- SetSentErrorMessage(true);
- return false;
- }
-
- sLog.outString( "Re-Loading Scripts..." );
- HandleReloadGameObjectScriptsCommand("a");
- HandleReloadEventScriptsCommand("a");
- HandleReloadQuestEndScriptsCommand("a");
- HandleReloadQuestStartScriptsCommand("a");
- HandleReloadSpellScriptsCommand("a");
- SendGlobalSysMessage("DB tables `*_scripts` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadAllSpellCommand(const char*)
-{
- HandleReloadSkillDiscoveryTemplateCommand("a");
- HandleReloadSkillExtraItemTemplateCommand("a");
- HandleReloadSpellAffectCommand("a");
- HandleReloadSpellChainCommand("a");
- HandleReloadSpellElixirCommand("a");
- HandleReloadSpellLearnSpellCommand("a");
- HandleReloadSpellProcEventCommand("a");
- HandleReloadSpellScriptTargetCommand("a");
- HandleReloadSpellTargetPositionCommand("a");
- HandleReloadSpellThreatsCommand("a");
- HandleReloadSpellPetAurasCommand("a");
- return true;
-}
-
-bool ChatHandler::HandleReloadAllItemCommand(const char*)
-{
- HandleReloadPageTextsCommand("a");
- HandleReloadItemEnchantementsCommand("a");
- return true;
-}
-
-bool ChatHandler::HandleReloadConfigCommand(const char* arg)
-{
- sLog.outString( "Re-Loading config settings..." );
- sWorld.LoadConfigSettings(true);
- SendGlobalSysMessage("World config settings reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadAreaTriggerTavernCommand(const char*)
-{
- sLog.outString( "Re-Loading Tavern Area Triggers..." );
- objmgr.LoadTavernAreaTriggers();
- SendGlobalSysMessage("DB table `areatrigger_tavern` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadAreaTriggerTeleportCommand(const char*)
-{
- sLog.outString( "Re-Loading AreaTrigger teleport definitions..." );
- objmgr.LoadAreaTriggerTeleports();
- SendGlobalSysMessage("DB table `areatrigger_teleport` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadCommandCommand(const char*)
-{
- load_command_table = true;
- SendGlobalSysMessage("DB table `command` will be reloaded at next chat command use.");
- return true;
-}
-
-bool ChatHandler::HandleReloadCreatureQuestRelationsCommand(const char*)
-{
- sLog.outString( "Loading Quests Relations... (`creature_questrelation`)" );
- objmgr.LoadCreatureQuestRelations();
- SendGlobalSysMessage("DB table `creature_questrelation` (creature quest givers) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadCreatureQuestInvRelationsCommand(const char*)
-{
- sLog.outString( "Loading Quests Relations... (`creature_involvedrelation`)" );
- objmgr.LoadCreatureInvolvedRelations();
- SendGlobalSysMessage("DB table `creature_involvedrelation` (creature quest takers) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadGOQuestRelationsCommand(const char*)
-{
- sLog.outString( "Loading Quests Relations... (`gameobject_questrelation`)" );
- objmgr.LoadGameobjectQuestRelations();
- SendGlobalSysMessage("DB table `gameobject_questrelation` (gameobject quest givers) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadGOQuestInvRelationsCommand(const char*)
-{
- sLog.outString( "Loading Quests Relations... (`gameobject_involvedrelation`)" );
- objmgr.LoadGameobjectInvolvedRelations();
- SendGlobalSysMessage("DB table `gameobject_involvedrelation` (gameobject quest takers) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadQuestAreaTriggersCommand(const char*)
-{
- sLog.outString( "Re-Loading Quest Area Triggers..." );
- objmgr.LoadQuestAreaTriggers();
- SendGlobalSysMessage("DB table `areatrigger_involvedrelation` (quest area triggers) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadQuestTemplateCommand(const char*)
-{
- sLog.outString( "Re-Loading Quest Templates..." );
- objmgr.LoadQuests();
- SendGlobalSysMessage("DB table `quest_template` (quest definitions) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadLootTemplatesCreatureCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables... (`creature_loot_template`)" );
- LoadLootTemplates_Creature();
- LootTemplates_Creature.CheckLootRefs();
- SendGlobalSysMessage("DB table `creature_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadLootTemplatesDisenchantCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables... (`disenchant_loot_template`)" );
- LoadLootTemplates_Disenchant();
- LootTemplates_Disenchant.CheckLootRefs();
- SendGlobalSysMessage("DB table `disenchant_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadLootTemplatesFishingCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables... (`fishing_loot_template`)" );
- LoadLootTemplates_Fishing();
- LootTemplates_Fishing.CheckLootRefs();
- SendGlobalSysMessage("DB table `fishing_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadLootTemplatesGameobjectCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables... (`gameobject_loot_template`)" );
- LoadLootTemplates_Gameobject();
- LootTemplates_Gameobject.CheckLootRefs();
- SendGlobalSysMessage("DB table `gameobject_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadLootTemplatesItemCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables... (`item_loot_template`)" );
- LoadLootTemplates_Item();
- LootTemplates_Item.CheckLootRefs();
- SendGlobalSysMessage("DB table `item_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadLootTemplatesPickpocketingCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables... (`pickpocketing_loot_template`)" );
- LoadLootTemplates_Pickpocketing();
- LootTemplates_Pickpocketing.CheckLootRefs();
- SendGlobalSysMessage("DB table `pickpocketing_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadLootTemplatesProspectingCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables... (`prospecting_loot_template`)" );
- LoadLootTemplates_Prospecting();
- LootTemplates_Prospecting.CheckLootRefs();
- SendGlobalSysMessage("DB table `prospecting_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadLootTemplatesQuestMailCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables... (`quest_mail_loot_template`)" );
- LoadLootTemplates_QuestMail();
- LootTemplates_QuestMail.CheckLootRefs();
- SendGlobalSysMessage("DB table `quest_mail_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadLootTemplatesReferenceCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables... (`reference_loot_template`)" );
- LoadLootTemplates_Reference();
- SendGlobalSysMessage("DB table `reference_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadLootTemplatesSkinningCommand(const char*)
-{
- sLog.outString( "Re-Loading Loot Tables... (`skinning_loot_template`)" );
- LoadLootTemplates_Skinning();
- LootTemplates_Skinning.CheckLootRefs();
- SendGlobalSysMessage("DB table `skinning_loot_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadMangosStringCommand(const char*)
-{
- sLog.outString( "Re-Loading mangos_string Table!" );
- objmgr.LoadMangosStrings();
- SendGlobalSysMessage("DB table `mangos_string` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadNpcGossipCommand(const char*)
-{
- sLog.outString( "Re-Loading `npc_gossip` Table!" );
- objmgr.LoadNpcTextId();
- SendGlobalSysMessage("DB table `npc_gossip` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadNpcTrainerCommand(const char*)
-{
- sLog.outString( "Re-Loading `npc_trainer` Table!" );
- objmgr.LoadTrainerSpell();
- SendGlobalSysMessage("DB table `npc_trainer` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadNpcVendorCommand(const char*)
-{
- sLog.outString( "Re-Loading `npc_vendor` Table!" );
- objmgr.LoadVendors();
- SendGlobalSysMessage("DB table `npc_vendor` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadReservedNameCommand(const char*)
-{
- sLog.outString( "Loading ReservedNames... (`reserved_name`)" );
- objmgr.LoadReservedPlayersNames();
- SendGlobalSysMessage("DB table `reserved_name` (player reserved names) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSkillDiscoveryTemplateCommand(const char* /*args*/)
-{
- sLog.outString( "Re-Loading Skill Discovery Table..." );
- LoadSkillDiscoveryTable();
- SendGlobalSysMessage("DB table `skill_discovery_template` (recipes discovered at crafting) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSkillExtraItemTemplateCommand(const char* /*args*/)
-{
- sLog.outString( "Re-Loading Skill Extra Item Table..." );
- LoadSkillExtraItemTable();
- SendGlobalSysMessage("DB table `skill_extra_item_template` (extra item creation when crafting) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSkillFishingBaseLevelCommand(const char* /*args*/)
-{
- sLog.outString( "Re-Loading Skill Fishing base level requirements..." );
- objmgr.LoadFishingBaseSkillLevel();
- SendGlobalSysMessage("DB table `skill_fishing_base_level` (fishing base level for zone/subzone) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSpellAffectCommand(const char*)
-{
- sLog.outString( "Re-Loading SpellAffect definitions..." );
- spellmgr.LoadSpellAffects();
- SendGlobalSysMessage("DB table `spell_affect` (spell mods apply requirements) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSpellChainCommand(const char*)
-{
- sLog.outString( "Re-Loading Spell Chain Data... " );
- spellmgr.LoadSpellChains();
- SendGlobalSysMessage("DB table `spell_chain` (spell ranks) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSpellElixirCommand(const char*)
-{
- sLog.outString( "Re-Loading Spell Elixir types..." );
- spellmgr.LoadSpellElixirs();
- SendGlobalSysMessage("DB table `spell_elixir` (spell exlixir types) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSpellLearnSpellCommand(const char*)
-{
- sLog.outString( "Re-Loading Spell Learn Spells..." );
- spellmgr.LoadSpellLearnSpells();
- SendGlobalSysMessage("DB table `spell_learn_spell` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSpellProcEventCommand(const char*)
-{
- sLog.outString( "Re-Loading Spell Proc Event conditions..." );
- spellmgr.LoadSpellProcEvents();
- SendGlobalSysMessage("DB table `spell_proc_event` (spell proc trigger requirements) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSpellScriptTargetCommand(const char*)
-{
- sLog.outString( "Re-Loading SpellsScriptTarget..." );
- spellmgr.LoadSpellScriptTarget();
- SendGlobalSysMessage("DB table `spell_script_target` (spell targets selection in case specific creature/GO requirements) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSpellTargetPositionCommand(const char*)
-{
- sLog.outString( "Re-Loading Spell target coordinates..." );
- spellmgr.LoadSpellTargetPositions();
- SendGlobalSysMessage("DB table `spell_target_position` (destination coordinates for spell targets) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSpellThreatsCommand(const char*)
-{
- sLog.outString( "Re-Loading Aggro Spells Definitions...");
- spellmgr.LoadSpellThreats();
- SendGlobalSysMessage("DB table `spell_threat` (spell aggro definitions) reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadSpellPetAurasCommand(const char*)
-{
- sLog.outString( "Re-Loading Spell pet auras...");
- spellmgr.LoadSpellPetAuras();
- SendGlobalSysMessage("DB table `spell_pet_auras` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadPageTextsCommand(const char*)
-{
- sLog.outString( "Re-Loading Page Texts..." );
- objmgr.LoadPageTexts();
- SendGlobalSysMessage("DB table `page_texts` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadItemEnchantementsCommand(const char*)
-{
- sLog.outString( "Re-Loading Item Random Enchantments Table..." );
- LoadRandomEnchantmentsTable();
- SendGlobalSysMessage("DB table `item_enchantment_template` reloaded.");
- return true;
-}
-
-bool ChatHandler::HandleReloadGameObjectScriptsCommand(const char* arg)
-{
- if(sWorld.IsScriptScheduled())
- {
- SendSysMessage("DB scripts used currently, please attempt reload later.");
- SetSentErrorMessage(true);
- return false;
- }
-
- if(*arg!='a')
- sLog.outString( "Re-Loading Scripts from `gameobject_scripts`...");
-
- objmgr.LoadGameObjectScripts();
-
- if(*arg!='a')
- SendGlobalSysMessage("DB table `gameobject_scripts` reloaded.");
-
- return true;
-}
-
-bool ChatHandler::HandleReloadEventScriptsCommand(const char* arg)
-{
- if(sWorld.IsScriptScheduled())
- {
- SendSysMessage("DB scripts used currently, please attempt reload later.");
- SetSentErrorMessage(true);
- return false;
- }
-
- if(*arg!='a')
- sLog.outString( "Re-Loading Scripts from `event_scripts`...");
-
- objmgr.LoadEventScripts();
-
- if(*arg!='a')
- SendGlobalSysMessage("DB table `event_scripts` reloaded.");
-
- return true;
-}
-
-bool ChatHandler::HandleReloadQuestEndScriptsCommand(const char* arg)
-{
- if(sWorld.IsScriptScheduled())
- {
- SendSysMessage("DB scripts used currently, please attempt reload later.");
- SetSentErrorMessage(true);
- return false;
- }
-
- if(*arg!='a')
- sLog.outString( "Re-Loading Scripts from `quest_end_scripts`...");
-
- objmgr.LoadQuestEndScripts();
-
- if(*arg!='a')
- SendGlobalSysMessage("DB table `quest_end_scripts` reloaded.");
-
- return true;
-}
-
-bool ChatHandler::HandleReloadQuestStartScriptsCommand(const char* arg)
-{
- if(sWorld.IsScriptScheduled())
- {
- SendSysMessage("DB scripts used currently, please attempt reload later.");
- SetSentErrorMessage(true);
- return false;
- }
-
- if(*arg!='a')
- sLog.outString( "Re-Loading Scripts from `quest_start_scripts`...");
-
- objmgr.LoadQuestStartScripts();
-
- if(*arg!='a')
- SendGlobalSysMessage("DB table `quest_start_scripts` reloaded.");
-
- return true;
-}
-
-bool ChatHandler::HandleReloadSpellScriptsCommand(const char* arg)
-{
- if(sWorld.IsScriptScheduled())
- {
- SendSysMessage("DB scripts used currently, please attempt reload later.");
- SetSentErrorMessage(true);
- return false;
- }
-
- if(*arg!='a')
- sLog.outString( "Re-Loading Scripts from `spell_scripts`...");
-
- objmgr.LoadSpellScripts();
-
- if(*arg!='a')
- SendGlobalSysMessage("DB table `spell_scripts` reloaded.");
-
- return true;
-}
-
-bool ChatHandler::HandleReloadGameGraveyardZoneCommand(const char* /*arg*/)
-{
- sLog.outString( "Re-Loading Graveyard-zone links...");
-
- objmgr.LoadGraveyardZones();
-
- SendGlobalSysMessage("DB table `game_graveyard_zone` reloaded.");
-
- return true;
-}
-
-bool ChatHandler::HandleReloadGameTeleCommand(const char* /*arg*/)
-{
- sLog.outString( "Re-Loading Game Tele coordinates...");
-
- objmgr.LoadGameTele();
-
- SendGlobalSysMessage("DB table `game_tele` reloaded.");
-
- return true;
-}
-
-bool ChatHandler::HandleLoadScriptsCommand(const char* args)
-{
- if(!LoadScriptingModule(args)) return true;
-
- sWorld.SendWorldText(LANG_SCRIPTS_RELOADED);
- return true;
-}
-
-bool ChatHandler::HandleSecurityCommand(const char* args)
-{
- char* arg1 = strtok((char*)args, " ");
- if( !arg1 )
- return false;
-
- char* arg2 = 0;
-
- std::string targetName;
- uint32 targetAccountId = 0;
- uint32 targetSecurity = 0;
-
- Player* targetPlayer = getSelectedPlayer();
- if(targetPlayer)
- {
- targetName = targetPlayer->GetName();
- targetAccountId = targetPlayer->GetSession()->GetAccountId();
- targetSecurity = targetPlayer->GetSession()->GetSecurity();
- arg2 = arg1;
- }
- else
- {
- targetName = arg1;
- if(!normalizePlayerName(targetName))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- targetPlayer = ObjectAccessor::Instance().FindPlayerByName(targetName.c_str());
- if(targetPlayer)
- {
- targetAccountId = targetPlayer->GetSession()->GetAccountId();
- targetSecurity = targetPlayer->GetSession()->GetSecurity();
- }
- else
- {
- uint64 targetGUID = objmgr.GetPlayerGUIDByName(targetName.c_str());
- if(!targetGUID)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
- targetAccountId = objmgr.GetPlayerAccountIdByGUID(targetGUID);
- targetSecurity = objmgr.GetSecurityByAccount(targetAccountId);
- }
-
- arg2 = strtok(NULL, " ");
- }
-
- int32 gm = (int32)atoi(arg2);
- if ( gm < SEC_PLAYER || gm > SEC_ADMINISTRATOR )
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- // can set security level only for target with less security and to less security that we have
- if(targetSecurity >= m_session->GetSecurity() || uint32(gm) >= m_session->GetSecurity() )
- {
- SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(targetPlayer)
- {
- if( targetPlayer != m_session->GetPlayer() )
- ChatHandler(targetPlayer).PSendSysMessage(LANG_YOURS_SECURITY_CHANGED,m_session->GetPlayer()->GetName(), gm);
-
- targetPlayer->GetSession()->SetSecurity(gm);
- }
-
- PSendSysMessage(LANG_YOU_CHANGE_SECURITY, targetName.c_str(), gm);
- loginDatabase.PExecute("UPDATE account SET gmlevel = '%i' WHERE id = '%u'", gm, targetAccountId);
-
- return true;
-}
-
-bool ChatHandler::HandleAllowMovementCommand(const char* /*args*/)
-{
- if(sWorld.getAllowMovement())
- {
- sWorld.SetAllowMovement(false);
- SendSysMessage(LANG_CREATURE_MOVE_DISABLED);
- }
- else
- {
- sWorld.SetAllowMovement(true);
- SendSysMessage(LANG_CREATURE_MOVE_ENABLED);
- }
- return true;
-}
-
-bool ChatHandler::HandleMaxSkillCommand(const char* /*args*/)
-{
- Player* SelectedPlayer = getSelectedPlayer();
- if(!SelectedPlayer)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- // each skills that have max skill value dependent from level seted to current level max skill value
- SelectedPlayer->UpdateSkillsToMaxSkillsForLevel();
- return true;
-}
-
-bool ChatHandler::HandleSetSkillCommand(const char* args)
-{
- // number or [name] Shift-click form |color|Hskill:skill_id|h[name]|h|r
- char* skill_p = extractKeyFromLink((char*)args,"Hskill");
- if(!skill_p)
- return false;
-
- char *level_p = strtok (NULL, " ");
-
- if( !level_p)
- return false;
-
- char *max_p = strtok (NULL, " ");
-
- int32 skill = atoi(skill_p);
-
- if (skill <= 0)
- {
- PSendSysMessage(LANG_INVALID_SKILL_ID, skill);
- SetSentErrorMessage(true);
- return false;
- }
-
- int32 level = atol (level_p);
-
- Player * target = getSelectedPlayer();
- if(!target)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- SkillLineEntry const* sl = sSkillLineStore.LookupEntry(skill);
- if(!sl)
- {
- PSendSysMessage(LANG_INVALID_SKILL_ID, skill);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!target->GetSkillValue(skill))
- {
- PSendSysMessage(LANG_SET_SKILL_ERROR, target->GetName(), skill, sl->name[0]);
- SetSentErrorMessage(true);
- return false;
- }
-
- int32 max = max_p ? atol (max_p) : target->GetPureMaxSkillValue(skill);
-
- if( level <= 0 || level > max || max <= 0 )
- return false;
-
- target->SetSkill(skill, level, max);
- PSendSysMessage(LANG_SET_SKILL, skill, sl->name[0], target->GetName(), level, max);
-
- return true;
-}
-
-bool ChatHandler::HandleUnLearnCommand(const char* args)
-{
- if (!*args)
- return false;
-
-
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
- uint32 min_id = extractSpellIdFromLink((char*)args);
- if(!min_id)
- return false;
-
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
- char* tail = strtok(NULL,"");
-
- uint32 max_id = extractSpellIdFromLink(tail);
-
- if (!max_id)
- {
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
- max_id = min_id+1;
- }
- else
- {
- if (max_id < min_id)
- std::swap(min_id,max_id);
-
- max_id=max_id+1;
- }
-
- Player* target = getSelectedPlayer();
- if(!target)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- for(uint32 spell=min_id;spell<max_id;spell++)
- {
- if (target->HasSpell(spell))
- target->removeSpell(spell);
- else
- SendSysMessage(LANG_FORGET_SPELL);
- }
-
- return true;
-}
-
-bool ChatHandler::HandleCooldownCommand(const char* args)
-{
- Player* target = getSelectedPlayer();
- if(!target)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- if (!*args)
- {
- target->RemoveAllSpellCooldown();
- PSendSysMessage(LANG_REMOVEALL_COOLDOWN, target->GetName());
- }
- else
- {
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
- uint32 spell_id = extractSpellIdFromLink((char*)args);
- if(!spell_id)
- return false;
-
- if(!sSpellStore.LookupEntry(spell_id))
- {
- PSendSysMessage(LANG_UNKNOWN_SPELL, target==m_session->GetPlayer() ? GetMangosString(LANG_YOU) : target->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- WorldPacket data( SMSG_CLEAR_COOLDOWN, (4+8) );
- data << uint32(spell_id);
- data << uint64(target->GetGUID());
- target->GetSession()->SendPacket(&data);
- target->RemoveSpellCooldown(spell_id);
- PSendSysMessage(LANG_REMOVE_COOLDOWN, spell_id, target==m_session->GetPlayer() ? GetMangosString(LANG_YOU) : target->GetName());
- }
- return true;
-}
-
-bool ChatHandler::HandleLearnAllCommand(const char* /*args*/)
-{
- static const char *allSpellList[] =
- {
- "3365",
- "6233",
- "6247",
- "6246",
- "6477",
- "6478",
- "22810",
- "8386",
- "21651",
- "21652",
- "522",
- "7266",
- "8597",
- "2479",
- "22027",
- "6603",
- "5019",
- "133",
- "168",
- "227",
- "5009",
- "9078",
- "668",
- "203",
- "20599",
- "20600",
- "81",
- "20597",
- "20598",
- "20864",
- "1459",
- "5504",
- "587",
- "5143",
- "118",
- "5505",
- "597",
- "604",
- "1449",
- "1460",
- "2855",
- "1008",
- "475",
- "5506",
- "1463",
- "12824",
- "8437",
- "990",
- "5145",
- "8450",
- "1461",
- "759",
- "8494",
- "8455",
- "8438",
- "6127",
- "8416",
- "6129",
- "8451",
- "8495",
- "8439",
- "3552",
- "8417",
- "10138",
- "12825",
- "10169",
- "10156",
- "10144",
- "10191",
- "10201",
- "10211",
- "10053",
- "10173",
- "10139",
- "10145",
- "10192",
- "10170",
- "10202",
- "10054",
- "10174",
- "10193",
- "12826",
- "2136",
- "143",
- "145",
- "2137",
- "2120",
- "3140",
- "543",
- "2138",
- "2948",
- "8400",
- "2121",
- "8444",
- "8412",
- "8457",
- "8401",
- "8422",
- "8445",
- "8402",
- "8413",
- "8458",
- "8423",
- "8446",
- "10148",
- "10197",
- "10205",
- "10149",
- "10215",
- "10223",
- "10206",
- "10199",
- "10150",
- "10216",
- "10207",
- "10225",
- "10151",
- "116",
- "205",
- "7300",
- "122",
- "837",
- "10",
- "7301",
- "7322",
- "6143",
- "120",
- "865",
- "8406",
- "6141",
- "7302",
- "8461",
- "8407",
- "8492",
- "8427",
- "8408",
- "6131",
- "7320",
- "10159",
- "8462",
- "10185",
- "10179",
- "10160",
- "10180",
- "10219",
- "10186",
- "10177",
- "10230",
- "10181",
- "10161",
- "10187",
- "10220",
- "2018",
- "2663",
- "12260",
- "2660",
- "3115",
- "3326",
- "2665",
- "3116",
- "2738",
- "3293",
- "2661",
- "3319",
- "2662",
- "9983",
- "8880",
- "2737",
- "2739",
- "7408",
- "3320",
- "2666",
- "3323",
- "3324",
- "3294",
- "22723",
- "23219",
- "23220",
- "23221",
- "23228",
- "23338",
- "10788",
- "10790",
- "5611",
- "5016",
- "5609",
- "2060",
- "10963",
- "10964",
- "10965",
- "22593",
- "22594",
- "596",
- "996",
- "499",
- "768",
- "17002",
- "1448",
- "1082",
- "16979",
- "1079",
- "5215",
- "20484",
- "5221",
- "15590",
- "17007",
- "6795",
- "6807",
- "5487",
- "1446",
- "1066",
- "5421",
- "3139",
- "779",
- "6811",
- "6808",
- "1445",
- "5216",
- "1737",
- "5222",
- "5217",
- "1432",
- "6812",
- "9492",
- "5210",
- "3030",
- "1441",
- "783",
- "6801",
- "20739",
- "8944",
- "9491",
- "22569",
- "5226",
- "6786",
- "1433",
- "8973",
- "1828",
- "9495",
- "9006",
- "6794",
- "8993",
- "5203",
- "16914",
- "6784",
- "9635",
- "22830",
- "20722",
- "9748",
- "6790",
- "9753",
- "9493",
- "9752",
- "9831",
- "9825",
- "9822",
- "5204",
- "5401",
- "22831",
- "6793",
- "9845",
- "17401",
- "9882",
- "9868",
- "20749",
- "9893",
- "9899",
- "9895",
- "9832",
- "9902",
- "9909",
- "22832",
- "9828",
- "9851",
- "9883",
- "9869",
- "17406",
- "17402",
- "9914",
- "20750",
- "9897",
- "9848",
- "3127",
- "107",
- "204",
- "9116",
- "2457",
- "78",
- "18848",
- "331",
- "403",
- "2098",
- "1752",
- "11278",
- "11288",
- "11284",
- "6461",
- "2344",
- "2345",
- "6463",
- "2346",
- "2352",
- "775",
- "1434",
- "1612",
- "71",
- "2468",
- "2458",
- "2467",
- "7164",
- "7178",
- "7367",
- "7376",
- "7381",
- "21156",
- "5209",
- "3029",
- "5201",
- "9849",
- "9850",
- "20719",
- "22568",
- "22827",
- "22828",
- "22829",
- "6809",
- "8972",
- "9005",
- "9823",
- "9827",
- "6783",
- "9913",
- "6785",
- "6787",
- "9866",
- "9867",
- "9894",
- "9896",
- "6800",
- "8992",
- "9829",
- "9830",
- "780",
- "769",
- "6749",
- "6750",
- "9755",
- "9754",
- "9908",
- "20745",
- "20742",
- "20747",
- "20748",
- "9746",
- "9745",
- "9880",
- "9881",
- "5391",
- "842",
- "3025",
- "3031",
- "3287",
- "3329",
- "1945",
- "3559",
- "4933",
- "4934",
- "4935",
- "4936",
- "5142",
- "5390",
- "5392",
- "5404",
- "5420",
- "6405",
- "7293",
- "7965",
- "8041",
- "8153",
- "9033",
- "9034",
- //"9036", problems with ghost state
- "16421",
- "21653",
- "22660",
- "5225",
- "9846",
- "2426",
- "5916",
- "6634",
- //"6718", phasing stealth, annoing for learn all case.
- "6719",
- "8822",
- "9591",
- "9590",
- "10032",
- "17746",
- "17747",
- "8203",
- "11392",
- "12495",
- "16380",
- "23452",
- "4079",
- "4996",
- "4997",
- "4998",
- "4999",
- "5000",
- "6348",
- "6349",
- "6481",
- "6482",
- "6483",
- "6484",
- "11362",
- "11410",
- "11409",
- "12510",
- "12509",
- "12885",
- "13142",
- "21463",
- "23460",
- "11421",
- "11416",
- "11418",
- "1851",
- "10059",
- "11423",
- "11417",
- "11422",
- "11419",
- "11424",
- "11420",
- "27",
- "31",
- "33",
- "34",
- "35",
- "15125",
- "21127",
- "22950",
- "1180",
- "201",
- "12593",
- "12842",
- "16770",
- "6057",
- "12051",
- "18468",
- "12606",
- "12605",
- "18466",
- "12502",
- "12043",
- "15060",
- "12042",
- "12341",
- "12848",
- "12344",
- "12353",
- "18460",
- "11366",
- "12350",
- "12352",
- "13043",
- "11368",
- "11113",
- "12400",
- "11129",
- "16766",
- "12573",
- "15053",
- "12580",
- "12475",
- "12472",
- "12953",
- "12488",
- "11189",
- "12985",
- "12519",
- "16758",
- "11958",
- "12490",
- "11426",
- "3565",
- "3562",
- "18960",
- "3567",
- "3561",
- "3566",
- "3563",
- "1953",
- "2139",
- "12505",
- "13018",
- "12522",
- "12523",
- "5146",
- "5144",
- "5148",
- "8419",
- "8418",
- "10213",
- "10212",
- "10157",
- "12524",
- "13019",
- "12525",
- "13020",
- "12526",
- "13021",
- "18809",
- "13031",
- "13032",
- "13033",
- "4036",
- "3920",
- "3919",
- "3918",
- "7430",
- "3922",
- "3923",
- "7411",
- "7418",
- "7421",
- "13262",
- "7412",
- "7415",
- "7413",
- "7416",
- "13920",
- "13921",
- "7745",
- "7779",
- "7428",
- "7457",
- "7857",
- "7748",
- "7426",
- "13421",
- "7454",
- "13378",
- "7788",
- "14807",
- "14293",
- "7795",
- "6296",
- "20608",
- "755",
- "444",
- "427",
- "428",
- "442",
- "447",
- "3578",
- "3581",
- "19027",
- "3580",
- "665",
- "3579",
- "3577",
- "6755",
- "3576",
- "2575",
- "2577",
- "2578",
- "2579",
- "2580",
- "2656",
- "2657",
- "2576",
- "3564",
- "10248",
- "8388",
- "2659",
- "14891",
- "3308",
- "3307",
- "10097",
- "2658",
- "3569",
- "16153",
- "3304",
- "10098",
- "4037",
- "3929",
- "3931",
- "3926",
- "3924",
- "3930",
- "3977",
- "3925",
- "136",
- "228",
- "5487",
- "43",
- "202",
- "0"
- };
-
- int loop = 0;
- while(strcmp(allSpellList[loop], "0"))
- {
- uint32 spell = atol((char*)allSpellList[loop++]);
-
- if (m_session->GetPlayer()->HasSpell(spell))
- continue;
-
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
- if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
- {
- PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
- continue;
- }
-
- m_session->GetPlayer()->learnSpell(spell);
- }
-
- SendSysMessage(LANG_COMMAND_LEARN_MANY_SPELLS);
-
- return true;
-}
-
-bool ChatHandler::HandleLearnAllGMCommand(const char* /*args*/)
-{
- static const char *gmSpellList[] =
- {
- "24347", // Become A Fish, No Breath Bar
- "35132", // Visual Boom
- "38488", // Attack 4000-8000 AOE
- "38795", // Attack 2000 AOE + Slow Down 90%
- "15712", // Attack 200
- "1852", // GM Spell Silence
- "31899", // Kill
- "31924", // Kill
- "29878", // Kill My Self
- "26644", // More Kill
-
- "28550", //Invisible 24
- "23452", //Invisible + Target
- "0"
- };
-
- uint16 gmSpellIter = 0;
- while( strcmp(gmSpellList[gmSpellIter], "0") )
- {
- uint32 spell = atol((char*)gmSpellList[gmSpellIter++]);
-
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
- if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
- {
- PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
- continue;
- }
-
- m_session->GetPlayer()->learnSpell(spell);
- }
-
- SendSysMessage(LANG_LEARNING_GM_SKILLS);
- return true;
-}
-
-bool ChatHandler::HandleLearnAllMyClassCommand(const char* /*args*/)
-{
- HandleLearnAllMySpellsCommand("");
- HandleLearnAllMyTalentsCommand("");
- return true;
-}
-
-bool ChatHandler::HandleLearnAllMySpellsCommand(const char* /*args*/)
-{
- ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(m_session->GetPlayer()->getClass());
- if(!clsEntry)
- return true;
- uint32 family = clsEntry->spellfamily;
-
- for (uint32 i = 0; i < sSpellStore.GetNumRows(); i++)
- {
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(i);
- if(!spellInfo)
- continue;
-
- // skip wrong class/race skills
- if(!m_session->GetPlayer()->IsSpellFitByClassAndRace(spellInfo->Id))
- continue;
-
- // skip other spell families
- if( spellInfo->SpellFamilyName != family)
- continue;
-
- //TODO: skip triggered spells
-
- // skip spells with first rank learned as talent (and all talents then also)
- uint32 first_rank = spellmgr.GetFirstSpellInChain(spellInfo->Id);
- if(GetTalentSpellCost(first_rank) > 0 )
- continue;
-
- // skip broken spells
- if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
- continue;
-
- m_session->GetPlayer()->learnSpell(i);
- }
-
- SendSysMessage(LANG_COMMAND_LEARN_CLASS_SPELLS);
- return true;
-}
-
-static void learnAllHighRanks(Player* player, uint32 spellid)
-{
- SpellChainMapNext const& nextMap = spellmgr.GetSpellChainNext();
- for(SpellChainMapNext::const_iterator itr = nextMap.lower_bound(spellid); itr != nextMap.upper_bound(spellid); ++itr)
- {
- player->learnSpell(itr->second);
- learnAllHighRanks(player,itr->second);
- }
-}
-
-bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/)
-{
- Player* player = m_session->GetPlayer();
- uint32 classMask = player->getClassMask();
-
- for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++)
- {
- TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
- if(!talentInfo)
- continue;
-
- TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
- if(!talentTabInfo)
- continue;
-
- if( (classMask & talentTabInfo->ClassMask) == 0 )
- continue;
-
- // search highest talent rank
- uint32 spellid = 0;
- int rank = 4;
- for(; rank >= 0; --rank)
- {
- if(talentInfo->RankID[rank]!=0)
- {
- spellid = talentInfo->RankID[rank];
- break;
- }
- }
-
- if(!spellid) // ??? none spells in telent
- continue;
-
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid);
- if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
- continue;
-
- // learn highest rank of talent
- player->learnSpell(spellid);
-
- // and learn all non-talent spell ranks (recursive by tree)
- learnAllHighRanks(player,spellid);
- }
-
- SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS);
- return true;
-}
-
-bool ChatHandler::HandleLearnAllLangCommand(const char* /*args*/)
-{
- // skipping UNIVERSAL language (0)
- for(int i = 1; i < LANGUAGES_COUNT; ++i)
- m_session->GetPlayer()->learnSpell(lang_description[i].spell_id);
-
- SendSysMessage(LANG_COMMAND_LEARN_ALL_LANG);
- return true;
-}
-
-bool ChatHandler::HandleLearnAllDefaultCommand(const char* args)
-{
- char* pName = strtok((char*)args, "");
- Player *player = NULL;
- if (pName)
- {
- std::string name = pName;
-
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- player = objmgr.GetPlayer(name.c_str());
- }
- else
- player = getSelectedPlayer();
-
- if(!player)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- player->learnDefaultSpells();
- player->learnQuestRewardedSpells();
-
- PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST,player->GetName());
- return true;
-}
-
-bool ChatHandler::HandleLearnCommand(const char* args)
-{
- Player* targetPlayer = getSelectedPlayer();
-
- if(!targetPlayer)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
- uint32 spell = extractSpellIdFromLink((char*)args);
- if(!spell || !sSpellStore.LookupEntry(spell))
- return false;
-
- if (targetPlayer->HasSpell(spell))
- {
- if(targetPlayer == m_session->GetPlayer())
- SendSysMessage(LANG_YOU_KNOWN_SPELL);
- else
- PSendSysMessage(LANG_TARGET_KNOWN_SPELL,targetPlayer->GetName());
- SetSentErrorMessage(true);
- return false;
- }
-
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
- if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
- {
- PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
- SetSentErrorMessage(true);
- return false;
- }
-
- targetPlayer->learnSpell(spell);
-
- return true;
-}
-
-bool ChatHandler::HandleAddItemCommand(const char* args)
-{
- if (!*args)
- return false;
-
- uint32 itemId = 0;
-
- if(args[0]=='[') // [name] manual form
- {
- char* citemName = citemName = strtok((char*)args, "]");
-
- if(citemName && citemName[0])
- {
- std::string itemName = citemName+1;
- WorldDatabase.escape_string(itemName);
- QueryResult *result = WorldDatabase.PQuery("SELECT entry FROM item_template WHERE name = '%s'", itemName.c_str());
- if (!result)
- {
- PSendSysMessage(LANG_COMMAND_COULDNOTFIND, citemName+1);
- SetSentErrorMessage(true);
- return false;
- }
- itemId = result->Fetch()->GetUInt16();
- delete result;
- }
- else
- return false;
- }
- else // item_id or [name] Shift-click form |color|Hitem:item_id:0:0:0|h[name]|h|r
- {
- char* cId = extractKeyFromLink((char*)args,"Hitem");
- if(!cId)
- return false;
- itemId = atol(cId);
- }
-
- char* ccount = strtok(NULL, " ");
-
- int32 count = 1;
-
- if (ccount)
- count = strtol(ccount, NULL, 10);
-
- if (count == 0)
- count = 1;
-
- Player* pl = m_session->GetPlayer();
- Player* plTarget = getSelectedPlayer();
- if(!plTarget)
- plTarget = pl;
-
- sLog.outDetail(GetMangosString(LANG_ADDITEM), itemId, count);
-
- ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId);
- if(!pProto)
- {
- PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, itemId);
- SetSentErrorMessage(true);
- return false;
- }
-
- //Subtract
- if (count < 0)
- {
- plTarget->DestroyItemCount(itemId, -count, true, false);
- PSendSysMessage(LANG_REMOVEITEM, itemId, -count, plTarget->GetName());
- return true;
- }
-
- //Adding items
- uint32 noSpaceForCount = 0;
-
- // check space and find places
- ItemPosCountVec dest;
- uint8 msg = plTarget->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount );
- if( msg != EQUIP_ERR_OK ) // convert to possible store amount
- count -= noSpaceForCount;
-
- if( count == 0 || dest.empty()) // can't add any
- {
- PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, noSpaceForCount );
- SetSentErrorMessage(true);
- return false;
- }
-
- Item* item = plTarget->StoreNewItem( dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
-
- // remove binding (let GM give it to another player later)
- if(pl==plTarget)
- for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ++itr)
- if(Item* item1 = pl->GetItemByPos(itr->pos))
- item1->SetBinding( false );
-
- if(count > 0 && item)
- {
- pl->SendNewItem(item,count,false,true);
- if(pl!=plTarget)
- plTarget->SendNewItem(item,count,true,false);
- }
-
- if(noSpaceForCount > 0)
- PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, noSpaceForCount);
-
- return true;
-}
-
-bool ChatHandler::HandleAddItemSetCommand(const char* args)
-{
- if (!*args)
- return false;
-
- char* cId = extractKeyFromLink((char*)args,"Hitemset"); // number or [name] Shift-click form |color|Hitemset:itemset_id|h[name]|h|r
- if (!cId)
- return false;
-
- uint32 itemsetId = atol(cId);
-
- // prevent generation all items with itemset field value '0'
- if (itemsetId == 0)
- {
- PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND,itemsetId);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player* pl = m_session->GetPlayer();
- Player* plTarget = getSelectedPlayer();
- if(!plTarget)
- plTarget = pl;
-
- sLog.outDetail(GetMangosString(LANG_ADDITEMSET), itemsetId);
-
- QueryResult *result = WorldDatabase.PQuery("SELECT entry FROM item_template WHERE itemset = %u",itemsetId);
-
- if(!result)
- {
- PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND,itemsetId);
-
- SetSentErrorMessage(true);
- return false;
- }
-
- do
- {
- Field *fields = result->Fetch();
- uint32 itemId = fields[0].GetUInt32();
-
- ItemPosCountVec dest;
- uint8 msg = plTarget->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, 1 );
- if( msg == EQUIP_ERR_OK )
- {
- Item* item = plTarget->StoreNewItem( dest, itemId, true);
-
- // remove binding (let GM give it to another player later)
- if(pl==plTarget)
- item->SetBinding( false );
-
- pl->SendNewItem(item,1,false,true);
- if(pl!=plTarget)
- plTarget->SendNewItem(item,1,true,false);
- }
- else
- {
- pl->SendEquipError( msg, NULL, NULL );
- PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, 1);
- }
-
- }while( result->NextRow() );
-
- delete result;
-
- return true;
-}
-
-bool ChatHandler::HandleListItemCommand(const char* args)
-{
- if(!*args)
- return false;
-
- char* cId = extractKeyFromLink((char*)args,"Hitem");
- if(!cId)
- return false;
- uint32 item_id = atol(cId);
-
- ItemPrototype const* itemProto = item_id ? itemProto = objmgr.GetItemPrototype(item_id) : NULL;
-
- if(!itemProto)
- {
- PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, item_id);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* c_count = strtok(NULL, " ");
- int count = c_count ? atol(c_count) : 10;
-
- if(count < 0)
- return false;
-
- QueryResult *result;
-
- // inventory case
- uint32 inv_count = 0;
- result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM character_inventory WHERE item_template='%u'",item_id);
- if(result)
- {
- inv_count = (*result)[0].GetUInt32();
- delete result;
- }
-
- result=CharacterDatabase.PQuery(
- // 0 1 2 3 4 5
- "SELECT ci.item, cibag.slot AS bag, ci.slot, ci.guid, characters.account,characters.name "
- "FROM character_inventory AS ci LEFT JOIN character_inventory AS cibag ON (cibag.item=ci.bag),characters "
- "WHERE ci.item_template='%u' AND ci.guid = characters.guid LIMIT %u ",
- item_id,uint32(count));
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
- uint32 item_guid = fields[0].GetUInt32();
- uint32 item_bag = fields[1].GetUInt32();
- uint32 item_slot = fields[2].GetUInt32();
- uint32 owner_guid = fields[3].GetUInt32();
- uint32 owner_acc = fields[4].GetUInt32();
- std::string owner_name = fields[5].GetCppString();
-
- char const* item_pos = 0;
- if(Player::IsEquipmentPos(item_bag,item_slot))
- item_pos = "[equipped]";
- else if(Player::IsInventoryPos(item_bag,item_slot))
- item_pos = "[in inventory]";
- else if(Player::IsBankPos(item_bag,item_slot))
- item_pos = "[in bank]";
- else
- item_pos = "";
-
- PSendSysMessage(LANG_ITEMLIST_SLOT,
- item_guid,owner_name.c_str(),owner_guid,owner_acc,item_pos);
- } while (result->NextRow());
-
- int64 res_count = result->GetRowCount();
-
- delete result;
-
- if(count > res_count)
- count-=res_count;
- else if(count)
- count = 0;
- }
-
- // mail case
- uint32 mail_count = 0;
- result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM mail_items WHERE item_template='%u'", item_id);
- if(result)
- {
- mail_count = (*result)[0].GetUInt32();
- delete result;
- }
-
- if(count > 0)
- {
- result=CharacterDatabase.PQuery(
- // 0 1 2 3 4 5 6
- "SELECT mail_items.item_guid, mail.sender, mail.receiver, char_s.account, char_s.name, char_r.account, char_r.name "
- "FROM mail,mail_items,characters as char_s,characters as char_r "
- "WHERE mail_items.item_template='%u' AND char_s.guid = mail.sender AND char_r.guid = mail.receiver AND mail.id=mail_items.mail_id LIMIT %u",
- item_id,uint32(count));
- }
- else
- result = NULL;
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
- uint32 item_guid = fields[0].GetUInt32();
- uint32 item_s = fields[1].GetUInt32();
- uint32 item_r = fields[2].GetUInt32();
- uint32 item_s_acc = fields[3].GetUInt32();
- std::string item_s_name = fields[4].GetCppString();
- uint32 item_r_acc = fields[5].GetUInt32();
- std::string item_r_name = fields[6].GetCppString();
-
- char const* item_pos = "[in mail]";
-
- PSendSysMessage(LANG_ITEMLIST_MAIL,
- item_guid,item_s_name.c_str(),item_s,item_s_acc,item_r_name.c_str(),item_r,item_r_acc,item_pos);
- } while (result->NextRow());
-
- int64 res_count = result->GetRowCount();
-
- delete result;
-
- if(count > res_count)
- count-=res_count;
- else if(count)
- count = 0;
- }
-
- // auction case
- uint32 auc_count = 0;
- result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM auctionhouse WHERE item_template='%u'",item_id);
- if(result)
- {
- auc_count = (*result)[0].GetUInt32();
- delete result;
- }
-
- if(count > 0)
- {
- result=CharacterDatabase.PQuery(
- // 0 1 2 3
- "SELECT auctionhouse.itemguid, auctionhouse.itemowner, characters.account, characters.name "
- "FROM auctionhouse,characters WHERE auctionhouse.item_template='%u' AND characters.guid = auctionhouse.itemowner LIMIT %u",
- item_id,uint32(count));
- }
- else
- result = NULL;
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
- uint32 item_guid = fields[0].GetUInt32();
- uint32 owner = fields[1].GetUInt32();
- uint32 owner_acc = fields[2].GetUInt32();
- std::string owner_name = fields[3].GetCppString();
-
- char const* item_pos = "[in auction]";
-
- PSendSysMessage(LANG_ITEMLIST_AUCTION, item_guid, owner_name.c_str(), owner, owner_acc,item_pos);
- } while (result->NextRow());
-
- delete result;
- }
-
- if(inv_count+mail_count+auc_count == 0)
- {
- SendSysMessage(LANG_COMMAND_NOITEMFOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_COMMAND_LISTITEMMESSAGE,item_id,inv_count+mail_count+auc_count,inv_count,mail_count,auc_count);
-
- return true;
-}
-
-bool ChatHandler::HandleListObjectCommand(const char* args)
-{
- if(!*args)
- return false;
-
- // number or [name] Shift-click form |color|Hgameobject_entry:go_id|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hgameobject_entry");
- if(!cId)
- return false;
-
- uint32 go_id = atol(cId);
-
- GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(go_id);
-
- if(!go_id || !gInfo)
- {
- PSendSysMessage(LANG_COMMAND_LISTOBJINVALIDID, go_id);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* c_count = strtok(NULL, " ");
- int count = c_count ? atol(c_count) : 10;
-
- if(count < 0)
- return false;
-
- Player* pl = m_session->GetPlayer();
- QueryResult *result;
-
- uint32 obj_count = 0;
- result=WorldDatabase.PQuery("SELECT COUNT(guid) FROM gameobject WHERE id='%u'",go_id);
- if(result)
- {
- obj_count = (*result)[0].GetUInt32();
- delete result;
- }
-
- result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE id = '%u' ORDER BY order_ ASC LIMIT %u",
- pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),go_id,uint32(count));
-
- if (result)
- {
- do
- {
- Field *fields = result->Fetch();
- uint32 guid = fields[0].GetUInt32();
- float x = fields[1].GetFloat();
- float y = fields[2].GetFloat();
- float z = fields[3].GetFloat();
- int mapid = fields[4].GetUInt16();
-
- PSendSysMessage(LANG_GO_LIST, guid, guid, gInfo->name, x, y, z, mapid);
- } while (result->NextRow());
-
- delete result;
- }
-
- PSendSysMessage(LANG_COMMAND_LISTOBJMESSAGE,go_id,obj_count);
- return true;
-}
-
-bool ChatHandler::HandleNearObjectCommand(const char* args)
-{
- float distance = (!*args) ? 10 : atol(args);
- uint32 count = 0;
-
- Player* pl = m_session->GetPlayer();
- QueryResult *result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, map, "
- "(POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ "
- "FROM gameobject WHERE map='%u' AND (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) <= '%f' ORDER BY order_",
- pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),
- pl->GetMapId(),pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),distance*distance);
-
- if (result)
- {
- do
- {
- Field *fields = result->Fetch();
- uint32 guid = fields[0].GetUInt32();
- uint32 entry = fields[1].GetUInt32();
- float x = fields[2].GetFloat();
- float y = fields[3].GetFloat();
- float z = fields[4].GetFloat();
- int mapid = fields[5].GetUInt16();
-
- GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(entry);
-
- if(!gInfo)
- continue;
-
- PSendSysMessage(LANG_GO_LIST, guid, guid, gInfo->name, x, y, z, mapid);
-
- ++count;
- } while (result->NextRow());
-
- delete result;
- }
-
- PSendSysMessage(LANG_COMMAND_NEAROBJMESSAGE,distance,count);
- return true;
-}
-
-bool ChatHandler::HandleListCreatureCommand(const char* args)
-{
- if(!*args)
- return false;
-
- // number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hcreature_entry");
- if(!cId)
- return false;
-
- uint32 cr_id = atol(cId);
-
- CreatureInfo const* cInfo = objmgr.GetCreatureTemplate(cr_id);
-
- if(!cr_id || !cInfo)
- {
- PSendSysMessage(LANG_COMMAND_INVALIDCREATUREID, cr_id);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* c_count = strtok(NULL, " ");
- int count = c_count ? atol(c_count) : 10;
-
- if(count < 0)
- return false;
-
- Player* pl = m_session->GetPlayer();
- QueryResult *result;
-
- uint32 cr_count = 0;
- result=WorldDatabase.PQuery("SELECT COUNT(guid) FROM creature WHERE id='%u'",cr_id);
- if(result)
- {
- cr_count = (*result)[0].GetUInt32();
- delete result;
- }
-
- result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM creature WHERE id = '%u' ORDER BY order_ ASC LIMIT %u",
- pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), cr_id,uint32(count));
-
- if (result)
- {
- do
- {
- Field *fields = result->Fetch();
- uint32 guid = fields[0].GetUInt32();
- float x = fields[1].GetFloat();
- float y = fields[2].GetFloat();
- float z = fields[3].GetFloat();
- int mapid = fields[4].GetUInt16();
-
- PSendSysMessage(LANG_CREATURE_LIST, guid, guid, cInfo->Name, x, y, z, mapid);
- } while (result->NextRow());
-
- delete result;
- }
-
- PSendSysMessage(LANG_COMMAND_LISTCREATUREMESSAGE,cr_id,cr_count);
- return true;
-}
-
-bool ChatHandler::HandleLookupItemCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string namepart = args;
- std::wstring wnamepart;
-
- // converting string that we try to find to lower case
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- wstrToLower(wnamepart);
-
- uint32 counter = 0;
-
- // Search in `item_template`
- for (uint32 id = 0; id < sItemStorage.MaxEntry; id++)
- {
- ItemPrototype const *pProto = sItemStorage.LookupEntry<ItemPrototype >(id);
- if(!pProto)
- continue;
-
- int loc_idx = m_session->GetSessionDbLocaleIndex();
- if ( loc_idx >= 0 )
- {
- ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
- if (il)
- {
- if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
- {
- std::string name = il->Name[loc_idx];
-
- if (Utf8FitTo(name, wnamepart))
- {
- PSendSysMessage(LANG_ITEM_LIST, id, id, name.c_str());
- ++counter;
- continue;
- }
- }
- }
- }
-
- std::string name = pProto->Name1;
- if(name.empty())
- continue;
-
- if (Utf8FitTo(name, wnamepart))
- {
- PSendSysMessage(LANG_ITEM_LIST, id, id, name.c_str());
- ++counter;
- }
- }
-
- if (counter==0)
- SendSysMessage(LANG_COMMAND_NOITEMFOUND);
-
- return true;
-}
-
-bool ChatHandler::HandleLookupItemSetCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string namepart = args;
- std::wstring wnamepart;
-
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- // converting string that we try to find to lower case
- wstrToLower( wnamepart );
-
- uint32 counter = 0; // Counter for figure out that we found smth.
-
- // Search in ItemSet.dbc
- for (uint32 id = 0; id < sItemSetStore.GetNumRows(); id++)
- {
- ItemSetEntry const *set = sItemSetStore.LookupEntry(id);
- if(set)
- {
- int loc = m_session->GetSessionDbcLocale();
- std::string name = set->name[m_session->GetSessionDbcLocale()];
- if(name.empty())
- continue;
-
- if (!Utf8FitTo(name, wnamepart))
- {
- loc = 0;
- for(; loc < MAX_LOCALE; ++loc)
- {
- if(loc==m_session->GetSessionDbcLocale())
- continue;
-
- name = set->name[m_session->GetSessionDbcLocale()];
- if(name.empty())
- continue;
-
- if (Utf8FitTo(name, wnamepart))
- break;
- }
- }
-
- if(loc < MAX_LOCALE)
- {
- // send item set in "id - [namedlink locale]" format
- PSendSysMessage(LANG_ITEMSET_LIST,id,id,name.c_str(),localeNames[loc]);
- ++counter;
- }
- }
- }
- if (counter == 0) // if counter == 0 then we found nth
- SendSysMessage(LANG_COMMAND_NOITEMSETFOUND);
- return true;
-}
-
-bool ChatHandler::HandleLookupSkillCommand(const char* args)
-{
- Player* target = getSelectedPlayer();
- if(!target)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!*args)
- return false;
-
- std::string namepart = args;
- std::wstring wnamepart;
-
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- // converting string that we try to find to lower case
- wstrToLower( wnamepart );
-
- uint32 counter = 0; // Counter for figure out that we found smth.
-
- // Search in SkillLine.dbc
- for (uint32 id = 0; id < sSkillLineStore.GetNumRows(); id++)
- {
- SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(id);
- if(skillInfo)
- {
- int loc = m_session->GetSessionDbcLocale();
- std::string name = skillInfo->name[loc];
- if(name.empty())
- continue;
-
- if (!Utf8FitTo(name, wnamepart))
- {
- loc = 0;
- for(; loc < MAX_LOCALE; ++loc)
- {
- if(loc==m_session->GetSessionDbcLocale())
- continue;
-
- name = skillInfo->name[loc];
- if(name.empty())
- continue;
-
- if (Utf8FitTo(name, wnamepart))
- break;
- }
- }
-
- if(loc < MAX_LOCALE)
- {
- // send skill in "id - [namedlink locale]" format
- PSendSysMessage(LANG_SKILL_LIST,id,id,name.c_str(),localeNames[loc],(target->HasSkill(id) ? m_session->GetMangosString(LANG_KNOWN) : ""));
-
- ++counter;
- }
- }
- }
- if (counter == 0) // if counter == 0 then we found nth
- SendSysMessage(LANG_COMMAND_NOSKILLFOUND);
- return true;
-}
-
-bool ChatHandler::HandleLookupSpellCommand(const char* args)
-{
- Player* target = getSelectedPlayer();
- if( !target )
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!*args)
- return false;
-
- std::string namepart = args;
- std::wstring wnamepart;
-
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- // converting string that we try to find to lower case
- wstrToLower( wnamepart );
-
- uint32 counter = 0; // Counter for figure out that we found smth.
-
- // Search in Spell.dbc
- for (uint32 id = 0; id < sSpellStore.GetNumRows(); id++)
- {
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(id);
- if(spellInfo)
- {
- int loc = m_session->GetSessionDbcLocale();
- std::string name = spellInfo->SpellName[loc];
- if(name.empty())
- continue;
-
- if (!Utf8FitTo(name, wnamepart))
- {
- loc = 0;
- for(; loc < MAX_LOCALE; ++loc)
- {
- if(loc==m_session->GetSessionDbcLocale())
- continue;
-
- name = spellInfo->SpellName[loc];
- if(name.empty())
- continue;
-
- if (Utf8FitTo(name, wnamepart))
- break;
- }
- }
-
- if(loc < MAX_LOCALE)
- {
- bool known = target->HasSpell(id);
- bool learn = (spellInfo->Effect[0] == SPELL_EFFECT_LEARN_SPELL);
-
- uint32 telentCost = GetTalentSpellCost(id);
-
- bool talent = (telentCost > 0);
- bool passive = IsPassiveSpell(id);
- bool active = target->HasAura(id,0) || target->HasAura(id,1) || target->HasAura(id,2);
-
- // unit32 used to prevent interpreting uint8 as char at output
- // find rank of learned spell for learning spell, or talent rank
- uint32 rank = telentCost ? telentCost : spellmgr.GetSpellRank(learn ? spellInfo->EffectTriggerSpell[0] : id);
-
- // send spell in "id - [name, rank N] [talent] [passive] [learn] [known]" format
- std::ostringstream ss;
- ss << id << " - |cffffffff|Hspell:" << id << "|h[" << name;
-
- // include rank in link name
- if(rank)
- ss << GetMangosString(LANG_SPELL_RANK) << rank;
-
- ss << " " << localeNames[loc] << "]|h|r";
-
- if(talent)
- ss << GetMangosString(LANG_TALENT);
- if(passive)
- ss << GetMangosString(LANG_PASSIVE);
- if(learn)
- ss << GetMangosString(LANG_LEARN);
- if(known)
- ss << GetMangosString(LANG_KNOWN);
- if(active)
- ss << GetMangosString(LANG_ACTIVE);
-
- SendSysMessage(ss.str().c_str());
-
- ++counter;
- }
- }
- }
- if (counter == 0) // if counter == 0 then we found nth
- SendSysMessage(LANG_COMMAND_NOSPELLFOUND);
- return true;
-}
-
-bool ChatHandler::HandleLookupQuestCommand(const char* args)
-{
- Player* target = getSelectedPlayer();
- if( !target )
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!*args)
- return false;
-
- std::string namepart = args;
- std::wstring wnamepart;
-
- // converting string that we try to find to lower case
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- wstrToLower(wnamepart);
-
- uint32 counter = 0 ;
-
- ObjectMgr::QuestMap const& qTemplates = objmgr.GetQuestTemplates();
- for (ObjectMgr::QuestMap::const_iterator iter = qTemplates.begin(); iter != qTemplates.end(); ++iter)
- {
- Quest * qinfo = iter->second;
-
- int loc_idx = m_session->GetSessionDbLocaleIndex();
- if ( loc_idx >= 0 )
- {
- QuestLocale const *il = objmgr.GetQuestLocale(qinfo->GetQuestId());
- if (il)
- {
- if (il->Title.size() > loc_idx && !il->Title[loc_idx].empty())
- {
- std::string title = il->Title[loc_idx];
-
- if (Utf8FitTo(title, wnamepart))
- {
- QuestStatus status = target->GetQuestStatus(qinfo->GetQuestId());
-
- char const* statusStr = "";
- if(status == QUEST_STATUS_COMPLETE)
- {
- if(target->GetQuestRewardStatus(qinfo->GetQuestId()))
- statusStr = GetMangosString(LANG_COMMAND_QUEST_REWARDED);
- else
- statusStr = GetMangosString(LANG_COMMAND_QUEST_COMPLETE);
- }
- else if(status == QUEST_STATUS_INCOMPLETE)
- statusStr = GetMangosString(LANG_COMMAND_QUEST_ACTIVE);
-
- PSendSysMessage(LANG_QUEST_LIST,qinfo->GetQuestId(),qinfo->GetQuestId(),title.c_str(),(status == QUEST_STATUS_COMPLETE ? GetMangosString(LANG_COMPLETE) : (status == QUEST_STATUS_INCOMPLETE ? GetMangosString(LANG_ACTIVE) : "") ));
- ++counter;
- continue;
- }
- }
- }
- }
-
- std::string title = qinfo->GetTitle();
- if(title.empty())
- continue;
-
- if (Utf8FitTo(title, wnamepart))
- {
- QuestStatus status = target->GetQuestStatus(qinfo->GetQuestId());
-
- char const* statusStr = "";
- if(status == QUEST_STATUS_COMPLETE)
- {
- if(target->GetQuestRewardStatus(qinfo->GetQuestId()))
- statusStr = GetMangosString(LANG_COMMAND_QUEST_REWARDED);
- else
- statusStr = GetMangosString(LANG_COMMAND_QUEST_COMPLETE);
- }
- else if(status == QUEST_STATUS_INCOMPLETE)
- statusStr = GetMangosString(LANG_COMMAND_QUEST_ACTIVE);
-
- PSendSysMessage(LANG_QUEST_LIST,qinfo->GetQuestId(),qinfo->GetQuestId(), title.c_str(),(status == QUEST_STATUS_COMPLETE ? GetMangosString(LANG_COMPLETE) : (status == QUEST_STATUS_INCOMPLETE ? GetMangosString(LANG_ACTIVE) : "") ));
- ++counter;
- }
- }
-
- if (counter==0)
- SendSysMessage(LANG_COMMAND_NOQUESTFOUND);
-
- return true;
-}
-
-bool ChatHandler::HandleLookupCreatureCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string namepart = args;
- std::wstring wnamepart;
-
- // converting string that we try to find to lower case
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- wstrToLower(wnamepart);
-
- uint32 counter = 0;
-
- for (uint32 id = 0; id< sCreatureStorage.MaxEntry; id++ )
- {
- CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(id);
- if(!cInfo)
- continue;
-
- int loc_idx = m_session->GetSessionDbLocaleIndex();
- if ( loc_idx >= 0 )
- {
- CreatureLocale const *cl = objmgr.GetCreatureLocale(id);
- if (cl)
- {
- if (cl->Name.size() > loc_idx && !cl->Name[loc_idx].empty())
- {
- std::string name = cl->Name[loc_idx];
-
- if (Utf8FitTo(name, wnamepart))
- {
- PSendSysMessage(LANG_CREATURE_ENTRY_LIST, id, id, name.c_str());
- ++counter;
- continue;
- }
- }
- }
- }
-
- std::string name = cInfo->Name;
- if(name.empty())
- continue;
-
- if (Utf8FitTo(name, wnamepart))
- {
- PSendSysMessage(LANG_CREATURE_ENTRY_LIST,id,id,name.c_str());
- ++counter;
- }
- }
-
- if (counter==0)
- SendSysMessage(LANG_COMMAND_NOCREATUREFOUND);
-
- return true;
-}
-
-bool ChatHandler::HandleLookupObjectCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string namepart = args;
- std::wstring wnamepart;
-
- // converting string that we try to find to lower case
- if(!Utf8toWStr(namepart,wnamepart))
- return false;
-
- wstrToLower(wnamepart);
-
- uint32 counter = 0;
-
- for (uint32 id = 0; id< sGOStorage.MaxEntry; id++ )
- {
- GameObjectInfo const* gInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
- if(!gInfo)
- continue;
-
- int loc_idx = m_session->GetSessionDbLocaleIndex();
- if ( loc_idx >= 0 )
- {
- GameObjectLocale const *gl = objmgr.GetGameObjectLocale(id);
- if (gl)
- {
- if (gl->Name.size() > loc_idx && !gl->Name[loc_idx].empty())
- {
- std::string name = gl->Name[loc_idx];
-
- if (Utf8FitTo(name, wnamepart))
- {
- PSendSysMessage(LANG_GO_ENTRY_LIST, id, id, name.c_str());
- ++counter;
- continue;
- }
- }
- }
- }
-
- std::string name = gInfo->name;
- if(name.empty())
- continue;
-
- if(Utf8FitTo(name, wnamepart))
- {
- PSendSysMessage(LANG_GO_ENTRY_LIST, id, id, name.c_str());
- ++counter;
- }
- }
-
- if(counter==0)
- SendSysMessage(LANG_COMMAND_NOGAMEOBJECTFOUND);
-
- return true;
-}
-
-/** \brief GM command level 3 - Create a guild.
- *
- * This command allows a GM (level 3) to create a guild.
- *
- * The "args" parameter contains the name of the guild leader
- * and then the name of the guild.
- *
- */
-bool ChatHandler::HandleGuildCreateCommand(const char* args)
-{
-
- if (!*args)
- return false;
-
- Guild *guild;
- Player * player;
- char *lname,*gname;
- std::string guildname;
-
- lname = strtok((char*)args, " ");
- gname = strtok(NULL, "");
-
- if(!lname)
- return false;
- else if(!gname)
- {
- SendSysMessage(LANG_INSERT_GUILD_NAME);
- SetSentErrorMessage(true);
- return false;
- }
-
- guildname = gname;
- player = ObjectAccessor::Instance().FindPlayerByName(lname);
-
- if(!player)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!player->GetGuildId())
- {
- guild = new Guild;
- if(!guild->create(player->GetGUID(),guildname))
- {
- delete guild;
- SendSysMessage(LANG_GUILD_NOT_CREATED);
- SetSentErrorMessage(true);
- return false;
- }
-
- objmgr.AddGuild(guild);
- }
- else
- SendSysMessage(LANG_PLAYER_IN_GUILD);
-
- return true;
-}
-
-bool ChatHandler::HandleGuildInviteCommand(const char *args)
-{
- if(!*args)
- return false;
-
- char* par1 = strtok((char*)args, " ");
- char* par2 = strtok (NULL, "");
- if(!par1 || !par2)
- return false;
-
- std::string glName = par2;
- Guild* targetGuild = objmgr.GetGuildByName(glName);
- if(!targetGuild)
- return false;
-
- std::string plName = par1;
- if(!normalizePlayerName(plName))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 plGuid = 0;
- if(Player* targetPlayer = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()))
- plGuid = targetPlayer->GetGUID();
- else
- plGuid = objmgr.GetPlayerGUIDByName(plName.c_str());
-
- if(!plGuid)
- false;
-
- // players's guild membership checked in AddMember before add
- if(!targetGuild->AddMember(plGuid,targetGuild->GetLowestRank()))
- return false;
-
- return true;
-}
-
-bool ChatHandler::HandleGuildUninviteCommand(const char *args)
-{
- if(!*args)
- return false;
-
- char* par1 = strtok((char*)args, " ");
- if(!par1)
- return false;
- std::string plName = par1;
- if(!normalizePlayerName(plName))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 plGuid = 0;
- uint32 glId = 0;
- if(Player* targetPlayer = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()))
- {
- plGuid = targetPlayer->GetGUID();
- glId = targetPlayer->GetGuildId();
- }
- else
- {
- plGuid = objmgr.GetPlayerGUIDByName(plName.c_str());
- glId = Player::GetGuildIdFromDB(plGuid);
- }
-
- if(!plGuid || !glId)
- return false;
-
- Guild* targetGuild = objmgr.GetGuildById(glId);
- if(!targetGuild)
- return false;
-
- targetGuild->DelMember(plGuid);
-
- return true;
-}
-
-bool ChatHandler::HandleGuildRankCommand(const char *args)
-{
- if(!*args)
- return false;
-
- char* par1 = strtok((char*)args, " ");
- char* par2 = strtok(NULL, " ");
- if(!par1 || !par2)
- return false;
- std::string plName = par1;
- if(!normalizePlayerName(plName))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 plGuid = 0;
- uint32 glId = 0;
- if(Player* targetPlayer = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()))
- {
- plGuid = targetPlayer->GetGUID();
- glId = targetPlayer->GetGuildId();
- }
- else
- {
- plGuid = objmgr.GetPlayerGUIDByName(plName.c_str());
- glId = Player::GetGuildIdFromDB(plGuid);
- }
-
- if(!plGuid || !glId)
- return false;
-
- Guild* targetGuild = objmgr.GetGuildById(glId);
- if(!targetGuild)
- return false;
-
- uint32 newrank = uint32(atoi(par2));
- if(newrank > targetGuild->GetLowestRank())
- return false;
-
- targetGuild->ChangeRank(plGuid,newrank);
-
- return true;
-}
-
-bool ChatHandler::HandleGuildDeleteCommand(const char* args)
-{
- if(!*args)
- return false;
-
- char* par1 = strtok((char*)args, " ");
- if(!par1)
- return false;
-
- std::string gld = par1;
-
- Guild* targetGuild = objmgr.GetGuildByName(gld);
- if(!targetGuild)
- return false;
-
- targetGuild->Disband();
-
- return true;
-}
-
-bool ChatHandler::HandleGetDistanceCommand(const char* /*args*/)
-{
- Unit* pUnit = getSelectedUnit();
-
- if(!pUnit)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_DISTANCE, m_session->GetPlayer()->GetDistance(pUnit),m_session->GetPlayer()->GetDistance2d(pUnit));
-
- return true;
-}
-
-// FIX-ME!!!
-
-bool ChatHandler::HandleAddWeaponCommand(const char* /*args*/)
-{
- /*if (!*args)
- return false;
-
- uint64 guid = m_session->GetPlayer()->GetSelection();
- if (guid == 0)
- {
- SendSysMessage(LANG_NO_SELECTION);
- return true;
- }
-
- Creature *pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
-
- if(!pCreature)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- return true;
- }
-
- char* pSlotID = strtok((char*)args, " ");
- if (!pSlotID)
- return false;
-
- char* pItemID = strtok(NULL, " ");
- if (!pItemID)
- return false;
-
- uint32 ItemID = atoi(pItemID);
- uint32 SlotID = atoi(pSlotID);
-
- ItemPrototype* tmpItem = objmgr.GetItemPrototype(ItemID);
-
- bool added = false;
- if(tmpItem)
- {
- switch(SlotID)
- {
- case 1:
- pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, ItemID);
- added = true;
- break;
- case 2:
- pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_01, ItemID);
- added = true;
- break;
- case 3:
- pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_02, ItemID);
- added = true;
- break;
- default:
- PSendSysMessage(LANG_ITEM_SLOT_NOT_EXIST,SlotID);
- added = false;
- break;
- }
- if(added)
- {
- PSendSysMessage(LANG_ITEM_ADDED_TO_SLOT,ItemID,tmpItem->Name1,SlotID);
- }
- }
- else
- {
- PSendSysMessage(LANG_ITEM_NOT_FOUND,ItemID);
- return true;
- }
- */
- return true;
-}
-
-bool ChatHandler::HandleDieCommand(const char* /*args*/)
-{
- Unit* target = getSelectedUnit();
-
- if(!target || !m_session->GetPlayer()->GetSelection())
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- if( target->isAlive() )
- {
- m_session->GetPlayer()->DealDamage(target, target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
- }
-
- return true;
-}
-
-bool ChatHandler::HandleDamageCommand(const char * args)
-{
- if (!*args)
- return false;
-
- Unit* target = getSelectedUnit();
-
- if(!target || !m_session->GetPlayer()->GetSelection())
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- if( !target->isAlive() )
- return true;
-
- char* damageStr = strtok((char*)args, " ");
- if(!damageStr)
- return false;
-
- int32 damage = atoi((char*)damageStr);
- if(damage <=0)
- return true;
-
- char* schoolStr = strtok((char*)NULL, " ");
-
- // flat melee damage without resistence/etc reduction
- if(!schoolStr)
- {
- m_session->GetPlayer()->DealDamage(target, damage, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
- m_session->GetPlayer()->SendAttackStateUpdate (HITINFO_NORMALSWING2, target, 1, SPELL_SCHOOL_MASK_NORMAL, damage, 0, 0, VICTIMSTATE_NORMAL, 0);
- return true;
- }
-
- uint32 school = schoolStr ? atoi((char*)schoolStr) : SPELL_SCHOOL_NORMAL;
- if(school >= MAX_SPELL_SCHOOL)
- return false;
-
- SpellSchoolMask schoolmask = SpellSchoolMask(1 << school);
-
- if ( schoolmask & SPELL_SCHOOL_MASK_NORMAL )
- damage = m_session->GetPlayer()->CalcArmorReducedDamage(target, damage);
-
- char* spellStr = strtok((char*)NULL, " ");
-
- // melee damage by specific school
- if(!spellStr)
- {
- uint32 absorb = 0;
- uint32 resist = 0;
-
- m_session->GetPlayer()->CalcAbsorbResist(target,schoolmask, SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
-
- if (damage <= absorb + resist)
- return true;
-
- damage -= absorb + resist;
-
- m_session->GetPlayer()->DealDamage(target, damage, NULL, DIRECT_DAMAGE, schoolmask, NULL, false);
- m_session->GetPlayer()->SendAttackStateUpdate (HITINFO_NORMALSWING2, target, 1, schoolmask, damage, absorb, resist, VICTIMSTATE_NORMAL, 0);
- return true;
- }
-
- // non-melee damage
-
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
- uint32 spellid = extractSpellIdFromLink((char*)args);
- if(!spellid || !sSpellStore.LookupEntry(spellid))
- return false;
-
- m_session->GetPlayer()->SpellNonMeleeDamageLog(target, spellid, damage, false);
- return true;
-}
-
-bool ChatHandler::HandleModifyArenaCommand(const char * args)
-{
- if (!*args)
- return false;
-
- Player *target = getSelectedPlayer();
- if(!target)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- int32 amount = (uint32)atoi(args);
-
- target->ModifyArenaPoints(amount);
-
- PSendSysMessage(LANG_COMMAND_MODIFY_ARENA, target->GetName(), target->GetArenaPoints());
-
- return true;
-}
-
-bool ChatHandler::HandleReviveCommand(const char* args)
-{
- Player* SelectedPlayer = NULL;
-
- if (*args)
- {
- std::string name = args;
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- SelectedPlayer = objmgr.GetPlayer(name.c_str());
- }
- else
- SelectedPlayer = getSelectedPlayer();
-
- if(!SelectedPlayer)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- SelectedPlayer->ResurrectPlayer(0.5f);
- SelectedPlayer->SpawnCorpseBones();
- SelectedPlayer->SaveToDB();
- return true;
-}
-
-bool ChatHandler::HandleAuraCommand(const char* args)
-{
- char* px = strtok((char*)args, " ");
- if (!px)
- return false;
-
- Unit *target = getSelectedUnit();
- if(!target)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint32 spellID = (uint32)atoi(px);
- SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellID );
- if(spellInfo)
- {
- for(uint32 i = 0;i<3;i++)
- {
- uint8 eff = spellInfo->Effect[i];
- if (eff>=TOTAL_SPELL_EFFECTS)
- continue;
- if( IsAreaAuraEffect(eff) ||
- eff == SPELL_EFFECT_APPLY_AURA ||
- eff == SPELL_EFFECT_PERSISTENT_AREA_AURA )
- {
- Aura *Aur = CreateAura(spellInfo, i, NULL, target);
- target->AddAura(Aur);
- }
- }
- }
-
- return true;
-}
-
-bool ChatHandler::HandleUnAuraCommand(const char* args)
-{
- char* px = strtok((char*)args, " ");
- if (!px)
- return false;
-
- Unit *target = getSelectedUnit();
- if(!target)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- std::string argstr = args;
- if (argstr == "all")
- {
- target->RemoveAllAuras();
- return true;
- }
-
- uint32 spellID = (uint32)atoi(px);
- target->RemoveAurasDueToSpell(spellID);
-
- return true;
-}
-
-bool ChatHandler::HandleLinkGraveCommand(const char* args)
-{
- if(!*args)
- return false;
-
- char* px = strtok((char*)args, " ");
- if (!px)
- return false;
-
- uint32 g_id = (uint32)atoi(px);
-
- uint32 g_team;
-
- char* px2 = strtok(NULL, " ");
-
- if (!px2)
- g_team = 0;
- else if (strncmp(px2,"horde",6)==0)
- g_team = HORDE;
- else if (strncmp(px2,"alliance",9)==0)
- g_team = ALLIANCE;
- else
- return false;
-
- WorldSafeLocsEntry const* graveyard = sWorldSafeLocsStore.LookupEntry(g_id);
-
- if(!graveyard )
- {
- PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST, g_id);
- SetSentErrorMessage(true);
- return false;
- }
-
- Player* player = m_session->GetPlayer();
-
- uint32 zoneId = player->GetZoneId();
-
- AreaTableEntry const *areaEntry = GetAreaEntryByAreaID(zoneId);
- if(!areaEntry || areaEntry->zone !=0 )
- {
- PSendSysMessage(LANG_COMMAND_GRAVEYARDWRONGZONE, g_id,zoneId);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(graveyard->map_id != areaEntry->mapid && g_team != 0)
- {
- SendSysMessage(LANG_COMMAND_GRAVEYARDWRONGTEAM);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(objmgr.AddGraveYardLink(g_id,player->GetZoneId(),g_team))
- PSendSysMessage(LANG_COMMAND_GRAVEYARDLINKED, g_id,zoneId);
- else
- PSendSysMessage(LANG_COMMAND_GRAVEYARDALRLINKED, g_id,zoneId);
-
- return true;
-}
-
-bool ChatHandler::HandleNearGraveCommand(const char* args)
-{
- uint32 g_team;
-
- size_t argslen = strlen(args);
-
- if(!*args)
- g_team = 0;
- else if (strncmp((char*)args,"horde",argslen)==0)
- g_team = HORDE;
- else if (strncmp((char*)args,"alliance",argslen)==0)
- g_team = ALLIANCE;
- else
- return false;
-
- Player* player = m_session->GetPlayer();
-
- WorldSafeLocsEntry const* graveyard = objmgr.GetClosestGraveYard(
- player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(),player->GetMapId(),g_team);
-
- if(graveyard)
- {
- uint32 g_id = graveyard->ID;
-
- GraveYardData const* data = objmgr.FindGraveYardData(g_id,player->GetZoneId());
- if (!data)
- {
- PSendSysMessage(LANG_COMMAND_GRAVEYARDERROR,g_id);
- SetSentErrorMessage(true);
- return false;
- }
-
- g_team = data->team;
-
- std::string team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_NOTEAM);
-
- if(g_team == 0)
- team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ANY);
- else if(g_team == HORDE)
- team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_HORDE);
- else if(g_team == ALLIANCE)
- team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ALLIANCE);
-
- PSendSysMessage(LANG_COMMAND_GRAVEYARDNEAREST, g_id,team_name.c_str(),player->GetZoneId());
- }
- else
- {
- std::string team_name;
-
- if(g_team == 0)
- team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ANY);
- else if(g_team == HORDE)
- team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_HORDE);
- else if(g_team == ALLIANCE)
- team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ALLIANCE);
-
- if(g_team == ~uint32(0))
- PSendSysMessage(LANG_COMMAND_ZONENOGRAVEYARDS, player->GetZoneId());
- else
- PSendSysMessage(LANG_COMMAND_ZONENOGRAFACTION, player->GetZoneId(),team_name.c_str());
- }
-
- return true;
-}
-
-bool ChatHandler::HandleSpawnTransportCommand(const char* /*args*/)
-{
- return true;
-}
-
-//play npc emote
-bool ChatHandler::HandlePlayEmoteCommand(const char* args)
-{
- uint32 emote = atoi((char*)args);
-
- Creature* target = getSelectedCreature();
- if(!target)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- target->SetUInt32Value(UNIT_NPC_EMOTESTATE,emote);
-
- return true;
-}
-
-bool ChatHandler::HandleNpcInfoCommand(const char* /*args*/)
-{
- Creature* target = getSelectedCreature();
-
- if(!target)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint32 faction = target->getFaction();
- uint32 npcflags = target->GetUInt32Value(UNIT_NPC_FLAGS);
- uint32 displayid = target->GetDisplayId();
- uint32 nativeid = target->GetNativeDisplayId();
- uint32 Entry = target->GetEntry();
- CreatureInfo const* cInfo = target->GetCreatureInfo();
-
- int32 curRespawnDelay = target->GetRespawnTimeEx()-time(NULL);
- if(curRespawnDelay < 0)
- curRespawnDelay = 0;
- std::string curRespawnDelayStr = secsToTimeString(curRespawnDelay,true);
- std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(),true);
-
- PSendSysMessage(LANG_NPCINFO_CHAR, target->GetDBTableGUIDLow(), faction, npcflags, Entry, displayid, nativeid);
- PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel());
- PSendSysMessage(LANG_NPCINFO_HEALTH,target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth());
- PSendSysMessage(LANG_NPCINFO_FLAGS, target->GetUInt32Value(UNIT_FIELD_FLAGS), target->GetUInt32Value(UNIT_DYNAMIC_FLAGS), target->getFaction());
- PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(),curRespawnDelayStr.c_str());
- PSendSysMessage(LANG_NPCINFO_LOOT, cInfo->lootid,cInfo->pickpocketLootId,cInfo->SkinLootId);
- PSendSysMessage(LANG_NPCINFO_DUNGEON_ID, target->GetInstanceId());
- PSendSysMessage(LANG_NPCINFO_POSITION,float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ()));
-
- if ((npcflags & UNIT_NPC_FLAG_VENDOR) )
- {
- SendSysMessage(LANG_NPCINFO_VENDOR);
- }
- if ((npcflags & UNIT_NPC_FLAG_TRAINER) )
- {
- SendSysMessage(LANG_NPCINFO_TRAINER);
- }
-
- return true;
-}
-
-bool ChatHandler::HandleExploreCheatCommand(const char* args)
-{
- if (!*args)
- return false;
-
- int flag = atoi((char*)args);
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- if (flag != 0)
- {
- PSendSysMessage(LANG_YOU_SET_EXPLORE_ALL, chr->GetName());
- if(chr!=m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_ALL,m_session->GetPlayer()->GetName());
- }
- else
- {
- PSendSysMessage(LANG_YOU_SET_EXPLORE_NOTHING, chr->GetName());
- if(chr!=m_session->GetPlayer())
- ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_NOTHING,m_session->GetPlayer()->GetName());
- }
-
- for (uint8 i=0; i<128; i++)
- {
- if (flag != 0)
- {
- m_session->GetPlayer()->SetFlag(PLAYER_EXPLORED_ZONES_1+i,0xFFFFFFFF);
- }
- else
- {
- m_session->GetPlayer()->SetFlag(PLAYER_EXPLORED_ZONES_1+i,0);
- }
- }
-
- return true;
-}
-
-bool ChatHandler::HandleHoverCommand(const char* args)
-{
- char* px = strtok((char*)args, " ");
- uint32 flag;
- if (!px)
- flag = 1;
- else
- flag = atoi(px);
-
- m_session->GetPlayer()->SetHover(flag);
-
- if (flag)
- SendSysMessage(LANG_HOVER_ENABLED);
- else
- SendSysMessage(LANG_HOVER_DISABLED);
-
- return true;
-}
-
-bool ChatHandler::HandleLevelUpCommand(const char* args)
-{
- char* px = strtok((char*)args, " ");
- char* py = strtok((char*)NULL, " ");
-
- // command format parsing
- char* pname = (char*)NULL;
- int addlevel = 1;
-
- if(px && py) // .levelup name level
- {
- addlevel = atoi(py);
- pname = px;
- }
- else if(px && !py) // .levelup name OR .levelup level
- {
- if(isalpha(px[0])) // .levelup name
- pname = px;
- else // .levelup level
- addlevel = atoi(px);
- }
- // else .levelup - nothing do for prepering
-
- // player
- Player *chr = NULL;
- uint64 chr_guid = 0;
-
- std::string name;
-
- if(pname) // player by name
- {
- name = pname;
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- chr = objmgr.GetPlayer(name.c_str());
- if(!chr) // not in game
- {
- chr_guid = objmgr.GetPlayerGUIDByName(name);
- if (chr_guid == 0)
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
- }
- }
- else // player by selection
- {
- chr = getSelectedPlayer();
-
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- name = chr->GetName();
- }
-
- assert(chr || chr_guid);
-
- int32 oldlevel = chr ? chr->getLevel() : Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,chr_guid);
- int32 newlevel = oldlevel + addlevel;
- if(newlevel < 1)
- newlevel = 1;
- if(newlevel > 255) // hardcoded maximum level
- newlevel = 255;
-
- if(chr)
- {
- chr->GiveLevel(newlevel);
- chr->InitTalentForLevel();
- chr->SetUInt32Value(PLAYER_XP,0);
-
- if(oldlevel == newlevel)
- ChatHandler(chr).SendSysMessage(LANG_YOURS_LEVEL_PROGRESS_RESET);
- else
- if(oldlevel < newlevel)
- ChatHandler(chr).PSendSysMessage(LANG_YOURS_LEVEL_UP,newlevel-oldlevel);
- else
- if(oldlevel > newlevel)
- ChatHandler(chr).PSendSysMessage(LANG_YOURS_LEVEL_DOWN,newlevel-oldlevel);
- }
- else
- {
- // update levle and XP at level, all other will be updated at loading
- Tokens values;
- Player::LoadValuesArrayFromDB(values,chr_guid);
- Player::SetUInt32ValueInArray(values,UNIT_FIELD_LEVEL,newlevel);
- Player::SetUInt32ValueInArray(values,PLAYER_XP,0);
- Player::SaveValuesArrayInDB(values,chr_guid);
- }
-
- if(m_session->GetPlayer() != chr) // including chr==NULL
- PSendSysMessage(LANG_YOU_CHANGE_LVL,name.c_str(),newlevel);
- return true;
-}
-
-bool ChatHandler::HandleShowAreaCommand(const char* args)
-{
- if (!*args)
- return false;
-
- int area = atoi((char*)args);
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- int offset = area / 32;
- uint32 val = (uint32)(1 << (area % 32));
-
- if(offset >= 128)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint32 currFields = chr->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
- chr->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val));
-
- SendSysMessage(LANG_EXPLORE_AREA);
- return true;
-}
-
-bool ChatHandler::HandleHideAreaCommand(const char* args)
-{
- if (!*args)
- return false;
-
- int area = atoi((char*)args);
-
- Player *chr = getSelectedPlayer();
- if (chr == NULL)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- int offset = area / 32;
- uint32 val = (uint32)(1 << (area % 32));
-
- if(offset >= 128)
- {
- SendSysMessage(LANG_BAD_VALUE);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint32 currFields = chr->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
- chr->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields ^ val));
-
- SendSysMessage(LANG_UNEXPLORE_AREA);
- return true;
-}
-
-bool ChatHandler::HandleUpdate(const char* args)
-{
- if(!*args)
- return false;
-
- uint32 updateIndex;
- uint32 value;
-
- char* pUpdateIndex = strtok((char*)args, " ");
-
- Unit* chr = getSelectedUnit();
- if (chr == NULL)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!pUpdateIndex)
- {
- return true;
- }
- updateIndex = atoi(pUpdateIndex);
- //check updateIndex
- if(chr->GetTypeId() == TYPEID_PLAYER)
- {
- if (updateIndex>=PLAYER_END) return true;
- }
- else
- {
- if (updateIndex>=UNIT_END) return true;
- }
-
- char* pvalue = strtok(NULL, " ");
- if (!pvalue)
- {
- value=chr->GetUInt32Value(updateIndex);
-
- PSendSysMessage(LANG_UPDATE, chr->GetGUIDLow(),updateIndex,value);
- return true;
- }
-
- value=atoi(pvalue);
-
- PSendSysMessage(LANG_UPDATE_CHANGE, chr->GetGUIDLow(),updateIndex,value);
-
- chr->SetUInt32Value(updateIndex,value);
-
- return true;
-}
-
-bool ChatHandler::HandleBankCommand(const char* /*args*/)
-{
- m_session->SendShowBank( m_session->GetPlayer()->GetGUID() );
-
- return true;
-}
-
-bool ChatHandler::HandleChangeWeather(const char* args)
-{
- if(!*args)
- return false;
-
- //Weather is OFF
- if (!sWorld.getConfig(CONFIG_WEATHER))
- {
- SendSysMessage(LANG_WEATHER_DISABLED);
- SetSentErrorMessage(true);
- return false;
- }
-
- //*Change the weather of a cell
- char* px = strtok((char*)args, " ");
- char* py = strtok(NULL, " ");
-
- if (!px || !py)
- return false;
-
- uint32 type = (uint32)atoi(px); //0 to 3, 0: fine, 1: rain, 2: snow, 3: sand
- float grade = (float)atof(py); //0 to 1, sending -1 is instand good weather
-
- Player *player = m_session->GetPlayer();
- uint32 zoneid = player->GetZoneId();
-
- Weather* wth = sWorld.FindWeather(zoneid);
-
- if(!wth)
- wth = sWorld.AddWeather(zoneid);
- if(!wth)
- {
- SendSysMessage(LANG_NO_WEATHER);
- SetSentErrorMessage(true);
- return false;
- }
-
- wth->SetWeather(WeatherType(type), grade);
-
- return true;
-}
-
-bool ChatHandler::HandleSetValue(const char* args)
-{
- if(!*args)
- return false;
-
- char* px = strtok((char*)args, " ");
- char* py = strtok(NULL, " ");
- char* pz = strtok(NULL, " ");
-
- if (!px || !py)
- return false;
-
- Unit* target = getSelectedUnit();
- if(!target)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 guid = target->GetGUID();
-
- uint32 Opcode = (uint32)atoi(px);
- if(Opcode >= target->GetValuesCount())
- {
- PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, GUID_LOPART(guid), target->GetValuesCount());
- return false;
- }
- uint32 iValue;
- float fValue;
- bool isint32 = true;
- if(pz)
- isint32 = (bool)atoi(pz);
- if(isint32)
- {
- iValue = (uint32)atoi(py);
- sLog.outDebug(GetMangosString(LANG_SET_UINT), GUID_LOPART(guid), Opcode, iValue);
- target->SetUInt32Value( Opcode , iValue );
- PSendSysMessage(LANG_SET_UINT_FIELD, GUID_LOPART(guid), Opcode,iValue);
- }
- else
- {
- fValue = (float)atof(py);
- sLog.outDebug(GetMangosString(LANG_SET_FLOAT), GUID_LOPART(guid), Opcode, fValue);
- target->SetFloatValue( Opcode , fValue );
- PSendSysMessage(LANG_SET_FLOAT_FIELD, GUID_LOPART(guid), Opcode,fValue);
- }
-
- return true;
-}
-
-bool ChatHandler::HandleGetValue(const char* args)
-{
- if(!*args)
- return false;
-
- char* px = strtok((char*)args, " ");
- char* pz = strtok(NULL, " ");
-
- if (!px)
- return false;
-
- Unit* target = getSelectedUnit();
- if(!target)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 guid = target->GetGUID();
-
- uint32 Opcode = (uint32)atoi(px);
- if(Opcode >= target->GetValuesCount())
- {
- PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, GUID_LOPART(guid), target->GetValuesCount());
- return false;
- }
- uint32 iValue;
- float fValue;
- bool isint32 = true;
- if(pz)
- isint32 = (bool)atoi(pz);
-
- if(isint32)
- {
- iValue = target->GetUInt32Value( Opcode );
- sLog.outDebug(GetMangosString(LANG_GET_UINT), GUID_LOPART(guid), Opcode, iValue);
- PSendSysMessage(LANG_GET_UINT_FIELD, GUID_LOPART(guid), Opcode, iValue);
- }
- else
- {
- fValue = target->GetFloatValue( Opcode );
- sLog.outDebug(GetMangosString(LANG_GET_FLOAT), GUID_LOPART(guid), Opcode, fValue);
- PSendSysMessage(LANG_GET_FLOAT_FIELD, GUID_LOPART(guid), Opcode, fValue);
- }
-
- return true;
-}
-
-bool ChatHandler::HandleSet32Bit(const char* args)
-{
- if(!*args)
- return false;
-
- char* px = strtok((char*)args, " ");
- char* py = strtok(NULL, " ");
-
- if (!px || !py)
- return false;
-
- uint32 Opcode = (uint32)atoi(px);
- uint32 Value = (uint32)atoi(py);
- if (Value > 32) //uint32 = 32 bits
- return false;
-
- sLog.outDebug(GetMangosString(LANG_SET_32BIT), Opcode, Value);
-
- m_session->GetPlayer( )->SetUInt32Value( Opcode , 2^Value );
-
- PSendSysMessage(LANG_SET_32BIT_FIELD, Opcode,1);
- return true;
-}
-
-bool ChatHandler::HandleMod32Value(const char* args)
-{
- if(!*args)
- return false;
-
- char* px = strtok((char*)args, " ");
- char* py = strtok(NULL, " ");
-
- if (!px || !py)
- return false;
-
- uint32 Opcode = (uint32)atoi(px);
- int Value = atoi(py);
-
- if(Opcode >= m_session->GetPlayer()->GetValuesCount())
- {
- PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, m_session->GetPlayer()->GetGUIDLow(), m_session->GetPlayer( )->GetValuesCount());
- return false;
- }
-
- sLog.outDebug(GetMangosString(LANG_CHANGE_32BIT), Opcode, Value);
-
- int CurrentValue = (int)m_session->GetPlayer( )->GetUInt32Value( Opcode );
-
- CurrentValue += Value;
- m_session->GetPlayer( )->SetUInt32Value( Opcode , (uint32)CurrentValue );
-
- PSendSysMessage(LANG_CHANGE_32BIT_FIELD, Opcode,CurrentValue);
-
- return true;
-}
-
-bool ChatHandler::HandleAddTeleCommand(const char * args)
-{
- if(!*args)
- return false;
-
- Player *player=m_session->GetPlayer();
- if (!player)
- return false;
-
- std::string name = args;
-
- if(objmgr.GetGameTele(name))
- {
- SendSysMessage(LANG_COMMAND_TP_ALREADYEXIST);
- SetSentErrorMessage(true);
- return false;
- }
-
- GameTele tele;
- tele.position_x = player->GetPositionX();
- tele.position_y = player->GetPositionY();
- tele.position_z = player->GetPositionZ();
- tele.orientation = player->GetOrientation();
- tele.mapId = player->GetMapId();
- tele.name = name;
-
- if(objmgr.AddGameTele(tele))
- {
- SendSysMessage(LANG_COMMAND_TP_ADDED);
- }
- else
- {
- SendSysMessage(LANG_COMMAND_TP_ADDEDERR);
- SetSentErrorMessage(true);
- return false;
- }
-
- return true;
-}
-
-bool ChatHandler::HandleDelTeleCommand(const char * args)
-{
- if(!*args)
- return false;
-
- std::string name = args;
-
- if(!objmgr.DeleteGameTele(name))
- {
- SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- SendSysMessage(LANG_COMMAND_TP_DELETED);
- return true;
-}
-
-bool ChatHandler::HandleListAurasCommand (const char * /*args*/)
-{
- Unit *unit = getSelectedUnit();
- if(!unit)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- char const* talentStr = GetMangosString(LANG_TALENT);
- char const* passiveStr = GetMangosString(LANG_PASSIVE);
-
- Unit::AuraMap const& uAuras = unit->GetAuras();
- PSendSysMessage(LANG_COMMAND_TARGET_LISTAURAS, uAuras.size());
- for (Unit::AuraMap::const_iterator itr = uAuras.begin(); itr != uAuras.end(); ++itr)
- {
- bool talent = GetTalentSpellCost(itr->second->GetId()) > 0;
- PSendSysMessage(LANG_COMMAND_TARGET_AURADETAIL, itr->second->GetId(), itr->second->GetEffIndex(),
- itr->second->GetModifier()->m_auraname, itr->second->GetAuraDuration(), itr->second->GetAuraMaxDuration(),
- itr->second->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()],
- (itr->second->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""),
- IS_PLAYER_GUID(itr->second->GetCasterGUID()) ? "player" : "creature",GUID_LOPART(itr->second->GetCasterGUID()));
- }
- for (int i = 0; i < TOTAL_AURAS; i++)
- {
- Unit::AuraList const& uAuraList = unit->GetAurasByType(AuraType(i));
- if (uAuraList.empty()) continue;
- PSendSysMessage(LANG_COMMAND_TARGET_LISTAURATYPE, uAuraList.size(), i);
- for (Unit::AuraList::const_iterator itr = uAuraList.begin(); itr != uAuraList.end(); ++itr)
- {
- bool talent = GetTalentSpellCost((*itr)->GetId()) > 0;
- PSendSysMessage(LANG_COMMAND_TARGET_AURASIMPLE, (*itr)->GetId(), (*itr)->GetEffIndex(),
- (*itr)->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()],((*itr)->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""),
- IS_PLAYER_GUID((*itr)->GetCasterGUID()) ? "player" : "creature",GUID_LOPART((*itr)->GetCasterGUID()));
- }
- }
- return true;
-}
-
-bool ChatHandler::HandleResetHonorCommand (const char * args)
-{
- char* pName = strtok((char*)args, "");
- Player *player = NULL;
- if (pName)
- {
- std::string name = pName;
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str());
- player = objmgr.GetPlayer(guid);
- }
- else
- player = getSelectedPlayer();
-
- if(!player)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- return true;
- }
-
- player->SetUInt32Value(PLAYER_FIELD_KILLS, 0);
- player->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0);
- player->SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, 0);
- player->SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0);
- player->SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0);
-
- return true;
-}
-
-static bool HandleResetStatsOrLevelHelper(Player* player)
-{
- PlayerInfo const *info = objmgr.GetPlayerInfo(player->getRace(), player->getClass());
- if(!info) return false;
-
- ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(player->getClass());
- if(!cEntry)
- {
- sLog.outError("Class %u not found in DBC (Wrong DBC files?)",player->getClass());
- return false;
- }
-
- uint8 powertype = cEntry->powerType;
-
- uint32 unitfield;
- if(powertype == POWER_RAGE)
- unitfield = 0x1100EE00;
- else if(powertype == POWER_ENERGY)
- unitfield = 0x00000000;
- else if(powertype == POWER_MANA)
- unitfield = 0x0000EE00;
- else
- {
- sLog.outError("Invalid default powertype %u for player (class %u)",powertype,player->getClass());
- return false;
- }
-
- // reset m_form if no aura
- if(!player->HasAuraType(SPELL_AURA_MOD_SHAPESHIFT))
- player->m_form = FORM_NONE;
-
- player->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE );
- player->SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f );
-
- player->setFactionForRace(player->getRace());
-
- player->SetUInt32Value(UNIT_FIELD_BYTES_0, ( ( player->getRace() ) | ( player->getClass() << 8 ) | ( player->getGender() << 16 ) | ( powertype << 24 ) ) );
-
- // reset only if player not in some form;
- if(player->m_form==FORM_NONE)
- {
- switch(player->getGender())
- {
- case GENDER_FEMALE:
- player->SetDisplayId(info->displayId_f);
- player->SetNativeDisplayId(info->displayId_f);
- break;
- case GENDER_MALE:
- player->SetDisplayId(info->displayId_m);
- player->SetNativeDisplayId(info->displayId_m);
- break;
- default:
- break;
- }
- }
-
- // set UNIT_FIELD_BYTES_1 to init state but preserve m_form value
- player->SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield);
- player->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_UNK5 );
- player->SetByteValue(UNIT_FIELD_BYTES_2, 3, player->m_form);
-
- player->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
-
- //-1 is default value
- player->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1));
-
- //player->SetUInt32Value(PLAYER_FIELD_BYTES, 0xEEE00000 );
- return true;
-}
-
-bool ChatHandler::HandleResetLevelCommand(const char * args)
-{
- char* pName = strtok((char*)args, "");
- Player *player = NULL;
- if (pName)
- {
- std::string name = pName;
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str());
- player = objmgr.GetPlayer(guid);
- }
- else
- player = getSelectedPlayer();
-
- if(!player)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!HandleResetStatsOrLevelHelper(player))
- return false;
-
- player->SetLevel(1);
- player->InitStatsForLevel(true);
- player->InitTaxiNodesForLevel();
- player->InitTalentForLevel();
- player->SetUInt32Value(PLAYER_XP,0);
-
- // reset level to summoned pet
- Pet* pet = player->GetPet();
- if(pet && pet->getPetType()==SUMMON_PET)
- pet->InitStatsForLevel(1);
-
- return true;
-}
-
-bool ChatHandler::HandleResetStatsCommand(const char * args)
-{
- char* pName = strtok((char*)args, "");
- Player *player = NULL;
- if (pName)
- {
- std::string name = pName;
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str());
- player = objmgr.GetPlayer(guid);
- }
- else
- player = getSelectedPlayer();
-
- if(!player)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!HandleResetStatsOrLevelHelper(player))
- return false;
-
- player->InitStatsForLevel(true);
- player->InitTaxiNodesForLevel();
- player->InitTalentForLevel();
-
- return true;
-}
-
-bool ChatHandler::HandleResetSpellsCommand(const char * args)
-{
- char* pName = strtok((char*)args, "");
- Player *player = NULL;
- uint64 playerGUID = 0;
- if (pName)
- {
- std::string name = pName;
-
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- player = objmgr.GetPlayer(name.c_str());
- if(!player)
- playerGUID = objmgr.GetPlayerGUIDByName(name.c_str());
- }
- else
- player = getSelectedPlayer();
-
- if(!player && !playerGUID)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(player)
- {
- player->resetSpells();
-
- ChatHandler(player).SendSysMessage(LANG_RESET_SPELLS);
-
- if(m_session->GetPlayer()!=player)
- PSendSysMessage(LANG_RESET_SPELLS_ONLINE,player->GetName());
- }
- else
- {
- CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_SPELLS), GUID_LOPART(playerGUID));
- PSendSysMessage(LANG_RESET_SPELLS_OFFLINE,pName);
- }
-
- return true;
-}
-
-bool ChatHandler::HandleResetTalentsCommand(const char * args)
-{
- char* pName = strtok((char*)args, "");
- Player *player = NULL;
- uint64 playerGUID = 0;
- if (pName)
- {
- std::string name = pName;
- if(!normalizePlayerName(name))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- player = objmgr.GetPlayer(name.c_str());
- if(!player)
- playerGUID = objmgr.GetPlayerGUIDByName(name.c_str());
- }
- else
- player = getSelectedPlayer();
-
- if(!player && !playerGUID)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(player)
- {
- player->resetTalents(true);
-
- ChatHandler(player).SendSysMessage(LANG_RESET_TALENTS);
-
- if(m_session->GetPlayer()!=player)
- PSendSysMessage(LANG_RESET_TALENTS_ONLINE,player->GetName());
- }
- else
- {
- CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_TALENTS), GUID_LOPART(playerGUID) );
- PSendSysMessage(LANG_RESET_TALENTS_OFFLINE,pName);
- }
-
- return true;
-}
-
-bool ChatHandler::HandleResetAllCommand(const char * args)
-{
- if(!*args)
- return false;
-
- std::string casename = args;
-
- AtLoginFlags atLogin;
-
- // Command specially created as single command to prevent using short case names
- if(casename=="spells")
- {
- atLogin = AT_LOGIN_RESET_SPELLS;
- sWorld.SendWorldText(LANG_RESETALL_SPELLS);
- }
- else if(casename=="talents")
- {
- atLogin = AT_LOGIN_RESET_TALENTS;
- sWorld.SendWorldText(LANG_RESETALL_TALENTS);
- }
- else
- {
- PSendSysMessage(LANG_RESETALL_UNKNOWN_CASE,args);
- SetSentErrorMessage(true);
- return false;
- }
-
- CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u'",atLogin);
- HashMapHolder<Player>::MapType const& plist = ObjectAccessor::Instance().GetPlayers();
- for(HashMapHolder<Player>::MapType::const_iterator itr = plist.begin(); itr != plist.end(); ++itr)
- itr->second->SetAtLoginFlag(atLogin);
-
- return true;
-}
-
-bool ChatHandler::HandleShutDownCommand(const char* args)
-{
- if(!*args)
- return false;
-
- if(std::string(args)=="cancel")
- {
- sWorld.ShutdownCancel();
- }
- else
- {
- int32 time = atoi(args);
-
- ///- Prevent interpret wrong arg value as 0 secs shutdown time
- if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
- return false;
-
- sWorld.ShutdownServ(time);
- }
- return true;
-}
-
-bool ChatHandler::HandleRestartCommand(const char* args)
-{
- if(!*args)
- return false;
-
- if(std::string(args)=="cancel")
- {
- sWorld.ShutdownCancel();
- }
- else
- {
- int32 time = atoi(args);
-
- ///- Prevent interpret wrong arg value as 0 secs shutdown time
- if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
- return false;
-
- sWorld.ShutdownServ(time, SHUTDOWN_MASK_RESTART);
- }
- return true;
-}
-
-bool ChatHandler::HandleIdleRestartCommand(const char* args)
-{
- if(!*args)
- return false;
-
- if(std::string(args)=="cancel")
- {
- sWorld.ShutdownCancel();
- }
- else
- {
- int32 time = atoi(args);
-
- ///- Prevent interpret wrong arg value as 0 secs shutdown time
- if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
- return false;
-
- sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART+SHUTDOWN_MASK_IDLE);
- }
- return true;
-}
-
-bool ChatHandler::HandleIdleShutDownCommand(const char* args)
-{
- if(!*args)
- return false;
-
- if(std::string(args)=="cancel")
- {
- sWorld.ShutdownCancel();
- }
- else
- {
- int32 time = atoi(args);
-
- ///- Prevent interpret wrong arg value as 0 secs shutdown time
- if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
- return false;
-
- sWorld.ShutdownServ(time,SHUTDOWN_MASK_IDLE);
- }
- return true;
-}
-
-bool ChatHandler::HandleAddQuest(const char* args)
-{
- Player* player = getSelectedPlayer();
- if(!player)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- // .addquest #entry'
- // number or [name] Shift-click form |color|Hquest:quest_id|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hquest");
- if(!cId)
- return false;
-
- uint32 entry = atol(cId);
-
- Quest const* pQuest = objmgr.GetQuestTemplate(entry);
-
- if(!pQuest)
- {
- PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND,entry);
- SetSentErrorMessage(true);
- return false;
- }
-
- // check item starting quest (it can work incorrectly if added without item in inventory)
- QueryResult *result = WorldDatabase.PQuery("SELECT entry FROM item_template WHERE startquest = '%u' LIMIT 1",entry);
- if(result)
- {
- Field* fields = result->Fetch();
- uint32 item_id = fields[0].GetUInt32();
- delete result;
-
- PSendSysMessage(LANG_COMMAND_QUEST_STARTFROMITEM, entry,item_id);
- SetSentErrorMessage(true);
- return false;
- }
-
- // ok, normal (creature/GO starting) quest
- if( player->CanAddQuest( pQuest, true ) )
- {
- player->AddQuest( pQuest, NULL );
-
- if ( player->CanCompleteQuest( entry ) )
- player->CompleteQuest( entry );
- }
-
- return true;
-}
-
-bool ChatHandler::HandleRemoveQuest(const char* args)
-{
- Player* player = getSelectedPlayer();
- if(!player)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- // .removequest #entry'
- // number or [name] Shift-click form |color|Hquest:quest_id|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hquest");
- if(!cId)
- return false;
-
- uint32 entry = atol(cId);
-
- Quest const* pQuest = objmgr.GetQuestTemplate(entry);
-
- if(!pQuest)
- {
- PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry);
- SetSentErrorMessage(true);
- return false;
- }
-
- // remove all quest entries for 'entry' from quest log
- for(uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot )
- {
- uint32 quest = player->GetQuestSlotQuestId(slot);
- if(quest==entry)
- {
- player->SetQuestSlot(slot,0);
-
- // we ignore unequippable quest items in this case, its' still be equipped
- player->TakeQuestSourceItem( quest, false );
- }
- }
-
- // set quest status to not started (will updated in DB at next save)
- player->SetQuestStatus( entry, QUEST_STATUS_NONE);
-
- // reset rewarded for restart repeatable quest
- player->getQuestStatusMap()[entry].m_rewarded = false;
-
- SendSysMessage(LANG_COMMAND_QUEST_REMOVED);
- return true;
-}
-
-bool ChatHandler::HandleCompleteQuest(const char* args)
-{
- Player* player = getSelectedPlayer();
- if(!player)
- {
- SendSysMessage(LANG_NO_CHAR_SELECTED);
- SetSentErrorMessage(true);
- return false;
- }
-
- // .quest complete #entry
- // number or [name] Shift-click form |color|Hquest:quest_id|h[name]|h|r
- char* cId = extractKeyFromLink((char*)args,"Hquest");
- if(!cId)
- return false;
-
- uint32 entry = atol(cId);
-
- Quest const* pQuest = objmgr.GetQuestTemplate(entry);
-
- // If player doesn't have the quest
- if(!pQuest || player->GetQuestStatus(entry) == QUEST_STATUS_NONE)
- {
- PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry);
- SetSentErrorMessage(true);
- return false;
- }
-
- // Add quest items for quests that require items
- for(uint8 x = 0; x < QUEST_OBJECTIVES_COUNT; ++x)
- {
- uint32 id = pQuest->ReqItemId[x];
- uint32 count = pQuest->ReqItemCount[x];
- if(!id || !count)
- continue;
-
- uint32 curItemCount = player->GetItemCount(id,true);
-
- ItemPosCountVec dest;
- uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, id, count-curItemCount );
- if( msg == EQUIP_ERR_OK )
- {
- Item* item = player->StoreNewItem( dest, id, true);
- player->SendNewItem(item,count-curItemCount,true,false);
- }
- }
-
- // All creature/GO slain/casted (not required, but otherwise it will display "Creature slain 0/10")
- for(uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
- {
- uint32 creature = pQuest->ReqCreatureOrGOId[i];
- uint32 creaturecount = pQuest->ReqCreatureOrGOCount[i];
-
- if(uint32 spell_id = pQuest->ReqSpell[i])
- {
- for(uint16 z = 0; z < creaturecount; ++z)
- player->CastedCreatureOrGO(creature,0,spell_id);
- }
- else if(creature > 0)
- {
- for(uint16 z = 0; z < creaturecount; ++z)
- player->KilledMonster(creature,0);
- }
- else if(creature < 0)
- {
- for(uint16 z = 0; z < creaturecount; ++z)
- player->CastedCreatureOrGO(creature,0,0);
- }
- }
-
- // If the quest requires reputation to complete
- if(uint32 repFaction = pQuest->GetRepObjectiveFaction())
- {
- uint32 repValue = pQuest->GetRepObjectiveValue();
- uint32 curRep = player->GetReputation(repFaction);
- if(curRep < repValue)
- {
- FactionEntry const *factionEntry = sFactionStore.LookupEntry(repFaction);
- player->SetFactionReputation(factionEntry,repValue);
- }
- }
-
- // If the quest requires money
- int32 ReqOrRewMoney = pQuest->GetRewOrReqMoney();
- if(ReqOrRewMoney < 0)
- player->ModifyMoney(-ReqOrRewMoney);
-
- player->CompleteQuest(entry);
- return true;
-}
-
-bool ChatHandler::HandleBanCommand(const char* args)
-{
- if(!args)
- return false;
-
- char* type = strtok((char*)args, " ");
-
- if(!type)
- return false;
- char* nameOrIP = strtok(NULL, " ");
-
- if(!nameOrIP)
- return false;
-
- char* duration = strtok(NULL," ");
- if(!duration || !atoi(duration))
- return false;
-
- char* reason = strtok(NULL,"");
- if(!reason)
- return false;
-
- switch(sWorld.BanAccount(type, nameOrIP, duration, reason,m_session->GetPlayerName()))
- {
- case BAN_SUCCESS:
- if(atoi(duration)>0)
- PSendSysMessage(LANG_BAN_YOUBANNED,nameOrIP,secsToTimeString(TimeStringToSecs(duration),true).c_str(),reason);
- else
- PSendSysMessage(LANG_BAN_YOUPERMBANNED,nameOrIP,reason);
- break;
- case BAN_SYNTAX_ERROR:
- return false;
- case BAN_NOTFOUND:
- PSendSysMessage(LANG_BAN_NOTFOUND,type,nameOrIP);
- break;
- }
-
- return true;
-}
-
-bool ChatHandler::HandleUnBanCommand(const char* args)
-{
- if(!args)
- return false;
- char* type = strtok((char*)args, " ");
- if(!type)
- return false;
- char* nameOrIP = strtok(NULL, " ");
-
- if(!nameOrIP)
- return false;
-
- if(sWorld.RemoveBanAccount(type,nameOrIP))
- PSendSysMessage(LANG_UNBAN_UNBANNED,nameOrIP);
- else
- PSendSysMessage(LANG_UNBAN_ERROR,nameOrIP);
-
- return true;
-}
-
-bool ChatHandler::HandleBanInfoCommand(const char* args)
-{
- if(!args)
- return false;
-
- char* cType = strtok((char*)args, " ");
- char* cnameOrIP = strtok(NULL, "");
- if(!cType || !cnameOrIP)
- return false;
-
- std::string nameOrIP = cnameOrIP;
- std::string type = cType;
- if (!IsIPAddress(cnameOrIP) && type=="ip")
- return false;
-
- Field *fields;
- if(type != "ip")
- {
- //look the accountid up
- uint32 accountid;
- std::string accountname;
- if(type == "account")
- {
- loginDatabase.escape_string(nameOrIP);
- QueryResult *result = loginDatabase.PQuery("SELECT id, username FROM account WHERE username = '%s'",nameOrIP.c_str());
- if (!result)
- {
- PSendSysMessage(LANG_BANINFO_NOACCOUNT);
- return true;
- }
- fields = result->Fetch();
- accountid = fields[0].GetUInt32();
- accountname = fields[1].GetCppString();
- delete result;
- }
- else if(type == "character")
- {
- if(!normalizePlayerName(nameOrIP))
- {
- SendSysMessage(LANG_PLAYER_NOT_FOUND);
- SetSentErrorMessage(true);
- return false;
- }
-
- loginDatabase.escape_string(nameOrIP);
- QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'", nameOrIP.c_str());
- if (!result)
- {
- PSendSysMessage(LANG_BANINFO_NOCHARACTER);
- return true;
- }
- fields = result->Fetch();
- accountid = fields[0].GetUInt32();
- delete result;
- result = loginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", accountid);
- if (!result)
- {
- PSendSysMessage(LANG_BANINFO_NOCHARACTER);
- return true;
- }
- fields = result->Fetch();
- accountname = fields[0].GetCppString();
- delete result;
- }
- else
- return false;
-
- QueryResult *result = loginDatabase.PQuery("SELECT FROM_UNIXTIME(bandate), unbandate-bandate, active, unbandate,banreason,bannedby FROM account_banned WHERE id = '%u' ORDER BY bandate ASC",accountid);
- if(!result)
- {
- PSendSysMessage(LANG_BANINFO_NOACCOUNTBAN, accountname.c_str());
- return true;
- }
-
- PSendSysMessage(LANG_BANINFO_BANHISTORY,accountname.c_str());
- do
- {
- fields = result->Fetch();
-
- time_t unbandate = time_t(fields[3].GetUInt64());
- bool active = false;
- if(fields[2].GetBool() && (fields[1].GetUInt64() == (uint64)0 ||unbandate >= time(NULL)) )
- active = true;
- bool permanent = (fields[1].GetUInt64() == (uint64)0);
- std::string bantime = permanent?GetMangosString(LANG_BANINFO_INFINITE):secsToTimeString(fields[1].GetUInt64(), true);
- PSendSysMessage(LANG_BANINFO_HISTORYENTRY,
- fields[0].GetString(), bantime.c_str(), active ? GetMangosString(LANG_BANINFO_YES):GetMangosString(LANG_BANINFO_NO), fields[4].GetString(), fields[5].GetString());
- }while (result->NextRow());
-
- delete result;
- }
- else
- {
- loginDatabase.escape_string(nameOrIP);
- QueryResult *result = loginDatabase.PQuery("SELECT ip, FROM_UNIXTIME(bandate), FROM_UNIXTIME(unbandate), unbandate-UNIX_TIMESTAMP(), banreason,bannedby,unbandate-bandate FROM ip_banned WHERE ip = '%s'",nameOrIP.c_str());
- if(!result)
- {
- PSendSysMessage(LANG_BANINFO_NOIP);
- return true;
- }
- fields = result->Fetch();
- bool permanent = (fields[6].GetUInt64()==(uint64)0);
- PSendSysMessage(LANG_BANINFO_IPENTRY,
- fields[0].GetString(), fields[1].GetString(), permanent ? GetMangosString(LANG_BANINFO_NEVER):fields[2].GetString(),
- permanent ? GetMangosString(LANG_BANINFO_INFINITE):secsToTimeString(fields[3].GetUInt64(), true).c_str(), fields[4].GetString(), fields[5].GetString());
- delete result;
- }
- return true;
-}
-
-bool ChatHandler::HandleBanListCommand(const char* args)
-{
- loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
- if(!*args)
- return false;
- char* cType = strtok((char*)args, " ");
- char* cFilter = strtok(NULL, "");
- if(!cType || !cFilter)
- return false;
- std::string Filter = cFilter;
- std::string Type = cType;
- loginDatabase.escape_string(Filter);
-
- QueryResult* result = NULL;
- Field *fields = NULL;
- if(Type == "ip")
- {
- result = loginDatabase.PQuery("SELECT ip FROM ip_banned WHERE ip "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'"),Filter.c_str());
- if(!result)
- {
- PSendSysMessage(LANG_BANLIST_NOIP);
- return true;
- }
- PSendSysMessage(LANG_BANLIST_MATCHINGIP);
- do
- {
- fields = result->Fetch();
- PSendSysMessage("%s",fields[0].GetString());
- } while (result->NextRow());
-
- delete result;
- return true;
- }
- //lookup accountid
- if(Type == "account")
- {
- result = loginDatabase.PQuery("SELECT id FROM account WHERE username "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'"),Filter.c_str());
- if (!result)
- {
- PSendSysMessage(LANG_BANLIST_NOACCOUNT);
- return true;
- }
- //do not delete result
- }
- else if(Type == "characters")
- {
- result = CharacterDatabase.PQuery("SELECT account FROM characters, WHERE name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'"),Filter.c_str());
- if (!result)
- {
- PSendSysMessage(LANG_BANLIST_NOCHARACTER);
- return true;
- }
- }
- else
- return false;
-
- PSendSysMessage(LANG_BANLIST_MATCHINGACCOUNT);
- do
- {
- fields = result->Fetch();
- uint32 accountid = fields[0].GetUInt32();
- QueryResult* banresult = loginDatabase.PQuery("SELECT account.username FROM account,account_banned WHERE account_banned.id='%u' AND account_banned.active = '1' AND account_banned.id=account.id",accountid);
- if(banresult)
- {
- Field* fields2 = banresult->Fetch();
- PSendSysMessage("%s",fields2[0].GetString());
- delete banresult;
- }
- } while (result->NextRow());
-
- delete result;
- return true;
-}
-
-bool ChatHandler::HandleRespawnCommand(const char* /*args*/)
-{
- Player* pl = m_session->GetPlayer();
-
- CellPair p(MaNGOS::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- MaNGOS::RespawnDo u_do;
- MaNGOS::WorldObjectWorker<MaNGOS::RespawnDo> worker(u_do);
-
- TypeContainerVisitor<MaNGOS::WorldObjectWorker<MaNGOS::RespawnDo>, GridTypeMapContainer > obj_worker(worker);
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, obj_worker, *MapManager::Instance().GetMap(pl->GetMapId(), pl));
-
- return true;
-}
-
-bool ChatHandler::HandleFlyModeCommand(const char* args)
-{
- if(!args)
- return false;
-
- Unit *unit = getSelectedUnit();
- if (!unit || (unit->GetTypeId() != TYPEID_PLAYER))
- unit = m_session->GetPlayer();
-
- WorldPacket data(12);
- if (strncmp(args, "on", 3) == 0)
- data.SetOpcode(SMSG_MOVE_SET_CAN_FLY);
- else if (strncmp(args, "off", 4) == 0)
- data.SetOpcode(SMSG_MOVE_UNSET_CAN_FLY);
- else
- {
- SendSysMessage(LANG_USE_BOL);
- return false;
- }
- data.append(unit->GetPackGUID());
- data << uint32(0); // unknown
- unit->SendMessageToSet(&data, true);
- PSendSysMessage(LANG_COMMAND_FLYMODE_STATUS, unit->GetName(), args);
- return true;
-}
-
-bool ChatHandler::HandleLoadPDumpCommand(const char *args)
-{
- if(!args)
- return false;
-
- char * file = strtok((char*)args, " "); if(!file) return false;
- char * acc = strtok(NULL, " "); if(!acc) return false;
- if(!file || !acc)
- return false;
-
- uint32 account_id = objmgr.GetAccountByAccountName(acc);
- if(!account_id)
- {
- account_id = atoi(acc);
- if(account_id)
- {
- std::string acc_name;
- if(!objmgr.GetAccountNameByAccount(account_id,acc_name))
- return false;
- }
- else
- return false;
- }
-
- char * name = strtok(NULL, " ");
- char * guid_str = name ? strtok(NULL, " ") : NULL;
-
- uint32 guid = guid_str ? atoi(guid_str) : 0;
-
- if(PlayerDumpReader().LoadDump(file, account_id, name ? name : "", guid))
- PSendSysMessage(LANG_COMMAND_IMPORT_SUCCESS);
- else
- PSendSysMessage(LANG_COMMAND_IMPORT_FAILED);
-
- return true;
-}
-
-bool ChatHandler::HandleChangeEntryCommand(const char *args)
-{
- if(!args)
- return false;
-
- uint32 newEntryNum = atoi(args);
- if(!newEntryNum)
- return false;
-
- Unit* unit = getSelectedUnit();
- if(!unit || unit->GetTypeId() != TYPEID_UNIT)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
- Creature* creature = (Creature*)unit;
- if(creature->UpdateEntry(newEntryNum))
- SendSysMessage(LANG_DONE);
- else
- SendSysMessage(LANG_ERROR);
- return true;
-}
-
-bool ChatHandler::HandleWritePDumpCommand(const char *args)
-{
- if(!args)
- return false;
-
- char* file = strtok((char*)args, " ");
- char* p2 = strtok(NULL, " ");
-
- if(!file || !p2)
- return false;
-
- uint32 guid = objmgr.GetPlayerGUIDByName(p2);
- if(!guid)
- guid = atoi(p2);
-
- if (PlayerDumpWriter().WriteDump(file, guid))
- PSendSysMessage(LANG_COMMAND_EXPORT_SUCCESS);
- else
- PSendSysMessage(LANG_COMMAND_EXPORT_FAILED);
-
- return true;
-}
-
-bool ChatHandler::HandleMovegensCommand(const char* /*args*/)
-{
- Unit* unit = getSelectedUnit();
- if(!unit)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- PSendSysMessage(LANG_MOVEGENS_LIST,(unit->GetTypeId()==TYPEID_PLAYER ? "Player" : "Creature" ),unit->GetGUIDLow());
-
- MotionMaster* mm = unit->GetMotionMaster();
- for(MotionMaster::const_iterator itr = mm->begin(); itr != mm->end(); ++itr)
- {
- switch((*itr)->GetMovementGeneratorType())
- {
- case IDLE_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_IDLE); break;
- case RANDOM_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_RANDOM); break;
- case WAYPOINT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_WAYPOINT); break;
- case ANIMAL_RANDOM_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_ANIMAL_RANDOM); break;
- case CONFUSED_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_CONFUSED); break;
- case TARGETED_MOTION_TYPE:
- {
- if(unit->GetTypeId()==TYPEID_PLAYER)
- {
- TargetedMovementGenerator<Player> const* mgen = static_cast<TargetedMovementGenerator<Player> const*>(*itr);
- Unit* target = mgen->GetTarget();
- if(target)
- PSendSysMessage(LANG_MOVEGENS_TARGETED_PLAYER,target->GetName(),target->GetGUIDLow());
- else
- SendSysMessage(LANG_MOVEGENS_TARGETED_NULL);
- }
- else
- {
- TargetedMovementGenerator<Creature> const* mgen = static_cast<TargetedMovementGenerator<Creature> const*>(*itr);
- Unit* target = mgen->GetTarget();
- if(target)
- PSendSysMessage(LANG_MOVEGENS_TARGETED_CREATURE,target->GetName(),target->GetGUIDLow());
- else
- SendSysMessage(LANG_MOVEGENS_TARGETED_NULL);
- }
- break;
- }
- case HOME_MOTION_TYPE:
- if(unit->GetTypeId()==TYPEID_UNIT)
- {
- float x,y,z;
- (*itr)->GetDestination(x,y,z);
- PSendSysMessage(LANG_MOVEGENS_HOME_CREATURE,x,y,z);
- }
- else
- SendSysMessage(LANG_MOVEGENS_HOME_PLAYER);
- break;
- case FLIGHT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_FLIGHT); break;
- case POINT_MOTION_TYPE:
- {
- float x,y,z;
- (*itr)->GetDestination(x,y,z);
- PSendSysMessage(LANG_MOVEGENS_POINT,x,y,z);
- break;
- }
- case FLEEING_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_FEAR); break;
- case DISTRACT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_DISTRACT); break;
- default:
- PSendSysMessage(LANG_MOVEGENS_UNKNOWN,(*itr)->GetMovementGeneratorType());
- break;
- }
- }
- return true;
-}
-
-bool ChatHandler::HandlePLimitCommand(const char *args)
-{
- if(*args)
- {
- char* param = strtok((char*)args, " ");
- if(!param)
- return false;
-
- int l = strlen(param);
-
- if( strncmp(param,"player",l) == 0 )
- sWorld.SetPlayerLimit(-SEC_PLAYER);
- else if(strncmp(param,"moderator",l) == 0 )
- sWorld.SetPlayerLimit(-SEC_MODERATOR);
- else if(strncmp(param,"gamemaster",l) == 0 )
- sWorld.SetPlayerLimit(-SEC_GAMEMASTER);
- else if(strncmp(param,"administrator",l) == 0 )
- sWorld.SetPlayerLimit(-SEC_ADMINISTRATOR);
- else if(strncmp(param,"reset",l) == 0 )
- sWorld.SetPlayerLimit( sConfig.GetIntDefault("PlayerLimit", DEFAULT_PLAYER_LIMIT) );
- else
- {
- int val = atoi(param);
- if(val < -SEC_ADMINISTRATOR) val = -SEC_ADMINISTRATOR;
-
- sWorld.SetPlayerLimit(val);
- }
-
- // kick all low security level players
- if(sWorld.GetPlayerAmountLimit() > SEC_PLAYER)
- sWorld.KickAllLess(sWorld.GetPlayerSecurityLimit());
- }
-
- uint32 pLimit = sWorld.GetPlayerAmountLimit();
- AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit();
- char const* secName = "";
- switch(allowedAccountType)
- {
- case SEC_PLAYER: secName = "Player"; break;
- case SEC_MODERATOR: secName = "Moderator"; break;
- case SEC_GAMEMASTER: secName = "Gamemaster"; break;
- case SEC_ADMINISTRATOR: secName = "Administrator"; break;
- default: secName = "<unknown>"; break;
- }
-
- PSendSysMessage("Player limits: amount %u, min. security level %s.",pLimit,secName);
-
- return true;
-}
-
-bool ChatHandler::HandleCastCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Unit* target = getSelectedUnit();
-
- if(!target)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
- uint32 spell = extractSpellIdFromLink((char*)args);
- if(!spell)
- return false;
-
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
- if(!spellInfo)
- return false;
-
- if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
- {
- PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* trig_str = strtok(NULL, " ");
- if(trig_str)
- {
- int l = strlen(trig_str);
- if(strncmp(trig_str,"triggered",l) != 0 )
- return false;
- }
-
- bool triggered = (trig_str != NULL);
-
- m_session->GetPlayer()->CastSpell(target,spell,triggered);
-
- return true;
-}
-
-bool ChatHandler::HandleCastBackCommand(const char* args)
-{
- Creature* caster = getSelectedCreature();
-
- if(!caster)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
- uint32 spell = extractSpellIdFromLink((char*)args);
- if(!spell || !sSpellStore.LookupEntry(spell))
- return false;
-
- char* trig_str = strtok(NULL, " ");
- if(trig_str)
- {
- int l = strlen(trig_str);
- if(strncmp(trig_str,"triggered",l) != 0 )
- return false;
- }
-
- bool triggered = (trig_str != NULL);
-
- // update orientation at server
- caster->SetOrientation(caster->GetAngle(m_session->GetPlayer()));
-
- // and client
- WorldPacket data;
- caster->BuildHeartBeatMsg(&data);
- caster->SendMessageToSet(&data,true);
-
- caster->CastSpell(m_session->GetPlayer(),spell,false);
-
- return true;
-}
-
-bool ChatHandler::HandleCastDistCommand(const char* args)
-{
- if(!*args)
- return false;
-
-
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
- uint32 spell = extractSpellIdFromLink((char*)args);
- if(!spell)
- return false;
-
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
- if(!spellInfo)
- return false;
-
- if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
- {
- PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
- SetSentErrorMessage(true);
- return false;
- }
-
- char *distStr = strtok(NULL, " ");
-
- float dist = 0;
-
- if(distStr)
- sscanf(distStr, "%f", &dist);
-
- char* trig_str = strtok(NULL, " ");
- if(trig_str)
- {
- int l = strlen(trig_str);
- if(strncmp(trig_str,"triggered",l) != 0 )
- return false;
- }
-
- bool triggered = (trig_str != NULL);
-
- float x,y,z;
- m_session->GetPlayer()->GetClosePoint(x,y,z,dist);
-
- m_session->GetPlayer()->CastSpell(x,y,z,spell,triggered);
- return true;
-}
-
-bool ChatHandler::HandleCastTargetCommand(const char* args)
-{
- Creature* caster = getSelectedCreature();
-
- if(!caster)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(!caster->getVictim())
- {
- SendSysMessage(LANG_SELECTED_TARGET_NOT_HAVE_VICTIM);
- SetSentErrorMessage(true);
- return false;
- }
-
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
- uint32 spell = extractSpellIdFromLink((char*)args);
- if(!spell || !sSpellStore.LookupEntry(spell))
- return false;
-
- char* trig_str = strtok(NULL, " ");
- if(trig_str)
- {
- int l = strlen(trig_str);
- if(strncmp(trig_str,"triggered",l) != 0 )
- return false;
- }
-
- bool triggered = (trig_str != NULL);
-
- // update orientation at server
- caster->SetOrientation(caster->GetAngle(m_session->GetPlayer()));
-
- // and client
- WorldPacket data;
- caster->BuildHeartBeatMsg(&data);
- caster->SendMessageToSet(&data,true);
-
- caster->CastSpell(caster->getVictim(),spell,false);
-
- return true;
-}
-
-/*
-ComeToMe command REQUIRED for 3rd party scripting library to have access to PointMovementGenerator
-Without this function 3rd party scripting library will get linking errors (unresolved external)
-when attempting to use the PointMovementGenerator
-*/
-bool ChatHandler::HandleComeToMeCommand(const char *args)
-{
- Creature* caster = getSelectedCreature();
-
- if(!caster)
- {
- SendSysMessage(LANG_SELECT_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- char* newFlagStr = strtok((char*)args, " ");
-
- if(!newFlagStr)
- return false;
-
- uint32 newFlags = atoi(newFlagStr);
-
- caster->SetUnitMovementFlags(newFlags);
-
- Player* pl = m_session->GetPlayer();
-
- caster->GetMotionMaster()->MovePoint(0, pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ());
- return true;
-}
-
-bool ChatHandler::HandleCastSelfCommand(const char* args)
-{
- if(!*args)
- return false;
-
- Unit* target = getSelectedUnit();
-
- if(!target)
- {
- SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
- SetSentErrorMessage(true);
- return false;
- }
-
- // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
- uint32 spell = extractSpellIdFromLink((char*)args);
- if(!spell)
- return false;
-
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
- if(!spellInfo)
- return false;
-
- if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
- {
- PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
- SetSentErrorMessage(true);
- return false;
- }
-
- target->CastSpell(target,spell,false);
-
- return true;
-}
-
-std::string GetTimeString(uint32 time)
-{
- uint16 days = time / DAY, hours = (time % DAY) / HOUR, minute = (time % HOUR) / MINUTE;
- std::ostringstream ss;
- if(days) ss << days << "d ";
- if(hours) ss << hours << "h ";
- ss << minute << "m";
- return ss.str();
-}
-
-bool ChatHandler::HandleInstanceListBindsCommand(const char* /*args*/)
-{
- Player* player = getSelectedPlayer();
- if (!player) player = m_session->GetPlayer();
- uint32 counter = 0;
- for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
- {
- Player::BoundInstancesMap &binds = player->GetBoundInstances(i);
- for(Player::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end(); ++itr)
- {
- InstanceSave *save = itr->second.save;
- std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL));
- PSendSysMessage("map: %d inst: %d perm: %s diff: %s canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty() == DIFFICULTY_NORMAL ? "normal" : "heroic", save->CanReset() ? "yes" : "no", timeleft.c_str());
- counter++;
- }
- }
- PSendSysMessage("player binds: %d", counter);
- counter = 0;
- Group *group = player->GetGroup();
- if(group)
- {
- for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
- {
- Group::BoundInstancesMap &binds = group->GetBoundInstances(i);
- for(Group::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end(); ++itr)
- {
- InstanceSave *save = itr->second.save;
- std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL));
- PSendSysMessage("map: %d inst: %d perm: %s diff: %s canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty() == DIFFICULTY_NORMAL ? "normal" : "heroic", save->CanReset() ? "yes" : "no", timeleft.c_str());
- counter++;
- }
- }
- }
- PSendSysMessage("group binds: %d", counter);
-
- return true;
-}
-
-bool ChatHandler::HandleInstanceUnbindCommand(const char* args)
-{
- if(!*args)
- return false;
-
- std::string cmd = args;
- if(cmd == "all")
- {
- Player* player = getSelectedPlayer();
- if (!player) player = m_session->GetPlayer();
- uint32 counter = 0;
- for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
- {
- Player::BoundInstancesMap &binds = player->GetBoundInstances(i);
- for(Player::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end();)
- {
- if(itr->first != player->GetMapId())
- {
- InstanceSave *save = itr->second.save;
- std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL));
- PSendSysMessage("unbinding map: %d inst: %d perm: %s diff: %s canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty() == DIFFICULTY_NORMAL ? "normal" : "heroic", save->CanReset() ? "yes" : "no", timeleft.c_str());
- player->UnbindInstance(itr, i);
- counter++;
- }
- else
- ++itr;
- }
- }
- PSendSysMessage("instances unbound: %d", counter);
- }
- return true;
-}
-
-bool ChatHandler::HandleInstanceStatsCommand(const char* /*args*/)
-{
- PSendSysMessage("instances loaded: %d", MapManager::Instance().GetNumInstances());
- PSendSysMessage("players in instances: %d", MapManager::Instance().GetNumPlayersInInstances());
- PSendSysMessage("instance saves: %d", sInstanceSaveManager.GetNumInstanceSaves());
- PSendSysMessage("players bound: %d", sInstanceSaveManager.GetNumBoundPlayersTotal());
- PSendSysMessage("groups bound: %d", sInstanceSaveManager.GetNumBoundGroupsTotal());
- return true;
-}
-
-bool ChatHandler::HandleInstanceSaveDataCommand(const char * /*args*/)
-{
- Player* pl = m_session->GetPlayer();
-
- Map* map = pl->GetMap();
- if (!map->IsDungeon())
- {
- PSendSysMessage("Map is not a dungeon.");
- SetSentErrorMessage(true);
- return false;
- }
-
- if (!((InstanceMap*)map)->GetInstanceData())
- {
- PSendSysMessage("Map has no instance data.");
- SetSentErrorMessage(true);
- return false;
- }
-
- ((InstanceMap*)map)->GetInstanceData()->SaveToDB();
- return true;
-}
-
-bool ChatHandler::HandleFlushArenaPointsCommand(const char * /*args*/)
-{
- sBattleGroundMgr.DistributeArenaPoints();
- 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "PlayerDump.h"
+#include "SpellMgr.h"
+#include "Player.h"
+#include "Opcodes.h"
+#include "GameObject.h"
+#include "Chat.h"
+#include "Log.h"
+#include "Guild.h"
+#include "ObjectAccessor.h"
+#include "MapManager.h"
+#include "SpellAuras.h"
+#include "ScriptCalls.h"
+#include "Language.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "Weather.h"
+#include "PointMovementGenerator.h"
+#include "TargetedMovementGenerator.h"
+#include "SkillDiscovery.h"
+#include "SkillExtraItems.h"
+#include "SystemConfig.h"
+#include "Config/ConfigEnv.h"
+#include "Util.h"
+#include "ItemEnchantmentMgr.h"
+#include "BattleGroundMgr.h"
+#include "InstanceSaveMgr.h"
+#include "InstanceData.h"
+
+//reload commands
+bool ChatHandler::HandleReloadCommand(const char* arg)
+{
+ // this is error catcher for wrong table name in .reload commands
+ PSendSysMessage("Db table with name starting from '%s' not found and can't be reloaded.",arg);
+ SetSentErrorMessage(true);
+ return false;
+}
+
+bool ChatHandler::HandleReloadAllCommand(const char*)
+{
+ HandleReloadAreaTriggerTeleportCommand("");
+ HandleReloadSkillFishingBaseLevelCommand("");
+
+ HandleReloadAllAreaCommand("");
+ HandleReloadAllLootCommand("");
+ HandleReloadAllNpcCommand("");
+ HandleReloadAllQuestCommand("");
+ HandleReloadAllSpellCommand("");
+ HandleReloadAllItemCommand("");
+
+ HandleReloadCommandCommand("");
+ HandleReloadReservedNameCommand("");
+ HandleReloadMangosStringCommand("");
+ HandleReloadGameTeleCommand("");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllAreaCommand(const char*)
+{
+ //HandleReloadQuestAreaTriggersCommand(""); -- reloaded in HandleReloadAllQuestCommand
+ HandleReloadAreaTriggerTeleportCommand("");
+ HandleReloadAreaTriggerTavernCommand("");
+ HandleReloadGameGraveyardZoneCommand("");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllLootCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables..." );
+ LoadLootTables();
+ SendGlobalSysMessage("DB tables `*_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllNpcCommand(const char* /*args*/)
+{
+ HandleReloadNpcGossipCommand("a");
+ HandleReloadNpcTrainerCommand("a");
+ HandleReloadNpcVendorCommand("a");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllQuestCommand(const char* /*args*/)
+{
+ HandleReloadQuestAreaTriggersCommand("a");
+ HandleReloadQuestTemplateCommand("a");
+
+ sLog.outString( "Re-Loading Quests Relations..." );
+ objmgr.LoadQuestRelations();
+ SendGlobalSysMessage("DB tables `*_questrelation` and `*_involvedrelation` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllScriptsCommand(const char*)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ PSendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ sLog.outString( "Re-Loading Scripts..." );
+ HandleReloadGameObjectScriptsCommand("a");
+ HandleReloadEventScriptsCommand("a");
+ HandleReloadQuestEndScriptsCommand("a");
+ HandleReloadQuestStartScriptsCommand("a");
+ HandleReloadSpellScriptsCommand("a");
+ SendGlobalSysMessage("DB tables `*_scripts` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllSpellCommand(const char*)
+{
+ HandleReloadSkillDiscoveryTemplateCommand("a");
+ HandleReloadSkillExtraItemTemplateCommand("a");
+ HandleReloadSpellAffectCommand("a");
+ HandleReloadSpellChainCommand("a");
+ HandleReloadSpellElixirCommand("a");
+ HandleReloadSpellLearnSpellCommand("a");
+ HandleReloadSpellProcEventCommand("a");
+ HandleReloadSpellScriptTargetCommand("a");
+ HandleReloadSpellTargetPositionCommand("a");
+ HandleReloadSpellThreatsCommand("a");
+ HandleReloadSpellPetAurasCommand("a");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAllItemCommand(const char*)
+{
+ HandleReloadPageTextsCommand("a");
+ HandleReloadItemEnchantementsCommand("a");
+ return true;
+}
+
+bool ChatHandler::HandleReloadConfigCommand(const char* arg)
+{
+ sLog.outString( "Re-Loading config settings..." );
+ sWorld.LoadConfigSettings(true);
+ SendGlobalSysMessage("World config settings reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAreaTriggerTavernCommand(const char*)
+{
+ sLog.outString( "Re-Loading Tavern Area Triggers..." );
+ objmgr.LoadTavernAreaTriggers();
+ SendGlobalSysMessage("DB table `areatrigger_tavern` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadAreaTriggerTeleportCommand(const char*)
+{
+ sLog.outString( "Re-Loading AreaTrigger teleport definitions..." );
+ objmgr.LoadAreaTriggerTeleports();
+ SendGlobalSysMessage("DB table `areatrigger_teleport` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadCommandCommand(const char*)
+{
+ load_command_table = true;
+ SendGlobalSysMessage("DB table `command` will be reloaded at next chat command use.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadCreatureQuestRelationsCommand(const char*)
+{
+ sLog.outString( "Loading Quests Relations... (`creature_questrelation`)" );
+ objmgr.LoadCreatureQuestRelations();
+ SendGlobalSysMessage("DB table `creature_questrelation` (creature quest givers) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadCreatureQuestInvRelationsCommand(const char*)
+{
+ sLog.outString( "Loading Quests Relations... (`creature_involvedrelation`)" );
+ objmgr.LoadCreatureInvolvedRelations();
+ SendGlobalSysMessage("DB table `creature_involvedrelation` (creature quest takers) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadGOQuestRelationsCommand(const char*)
+{
+ sLog.outString( "Loading Quests Relations... (`gameobject_questrelation`)" );
+ objmgr.LoadGameobjectQuestRelations();
+ SendGlobalSysMessage("DB table `gameobject_questrelation` (gameobject quest givers) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadGOQuestInvRelationsCommand(const char*)
+{
+ sLog.outString( "Loading Quests Relations... (`gameobject_involvedrelation`)" );
+ objmgr.LoadGameobjectInvolvedRelations();
+ SendGlobalSysMessage("DB table `gameobject_involvedrelation` (gameobject quest takers) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadQuestAreaTriggersCommand(const char*)
+{
+ sLog.outString( "Re-Loading Quest Area Triggers..." );
+ objmgr.LoadQuestAreaTriggers();
+ SendGlobalSysMessage("DB table `areatrigger_involvedrelation` (quest area triggers) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadQuestTemplateCommand(const char*)
+{
+ sLog.outString( "Re-Loading Quest Templates..." );
+ objmgr.LoadQuests();
+ SendGlobalSysMessage("DB table `quest_template` (quest definitions) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesCreatureCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`creature_loot_template`)" );
+ LoadLootTemplates_Creature();
+ LootTemplates_Creature.CheckLootRefs();
+ SendGlobalSysMessage("DB table `creature_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesDisenchantCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`disenchant_loot_template`)" );
+ LoadLootTemplates_Disenchant();
+ LootTemplates_Disenchant.CheckLootRefs();
+ SendGlobalSysMessage("DB table `disenchant_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesFishingCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`fishing_loot_template`)" );
+ LoadLootTemplates_Fishing();
+ LootTemplates_Fishing.CheckLootRefs();
+ SendGlobalSysMessage("DB table `fishing_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesGameobjectCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`gameobject_loot_template`)" );
+ LoadLootTemplates_Gameobject();
+ LootTemplates_Gameobject.CheckLootRefs();
+ SendGlobalSysMessage("DB table `gameobject_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesItemCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`item_loot_template`)" );
+ LoadLootTemplates_Item();
+ LootTemplates_Item.CheckLootRefs();
+ SendGlobalSysMessage("DB table `item_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesPickpocketingCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`pickpocketing_loot_template`)" );
+ LoadLootTemplates_Pickpocketing();
+ LootTemplates_Pickpocketing.CheckLootRefs();
+ SendGlobalSysMessage("DB table `pickpocketing_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesProspectingCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`prospecting_loot_template`)" );
+ LoadLootTemplates_Prospecting();
+ LootTemplates_Prospecting.CheckLootRefs();
+ SendGlobalSysMessage("DB table `prospecting_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesQuestMailCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`quest_mail_loot_template`)" );
+ LoadLootTemplates_QuestMail();
+ LootTemplates_QuestMail.CheckLootRefs();
+ SendGlobalSysMessage("DB table `quest_mail_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesReferenceCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`reference_loot_template`)" );
+ LoadLootTemplates_Reference();
+ SendGlobalSysMessage("DB table `reference_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadLootTemplatesSkinningCommand(const char*)
+{
+ sLog.outString( "Re-Loading Loot Tables... (`skinning_loot_template`)" );
+ LoadLootTemplates_Skinning();
+ LootTemplates_Skinning.CheckLootRefs();
+ SendGlobalSysMessage("DB table `skinning_loot_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadMangosStringCommand(const char*)
+{
+ sLog.outString( "Re-Loading mangos_string Table!" );
+ objmgr.LoadMangosStrings();
+ SendGlobalSysMessage("DB table `mangos_string` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadNpcGossipCommand(const char*)
+{
+ sLog.outString( "Re-Loading `npc_gossip` Table!" );
+ objmgr.LoadNpcTextId();
+ SendGlobalSysMessage("DB table `npc_gossip` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadNpcTrainerCommand(const char*)
+{
+ sLog.outString( "Re-Loading `npc_trainer` Table!" );
+ objmgr.LoadTrainerSpell();
+ SendGlobalSysMessage("DB table `npc_trainer` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadNpcVendorCommand(const char*)
+{
+ sLog.outString( "Re-Loading `npc_vendor` Table!" );
+ objmgr.LoadVendors();
+ SendGlobalSysMessage("DB table `npc_vendor` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadReservedNameCommand(const char*)
+{
+ sLog.outString( "Loading ReservedNames... (`reserved_name`)" );
+ objmgr.LoadReservedPlayersNames();
+ SendGlobalSysMessage("DB table `reserved_name` (player reserved names) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSkillDiscoveryTemplateCommand(const char* /*args*/)
+{
+ sLog.outString( "Re-Loading Skill Discovery Table..." );
+ LoadSkillDiscoveryTable();
+ SendGlobalSysMessage("DB table `skill_discovery_template` (recipes discovered at crafting) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSkillExtraItemTemplateCommand(const char* /*args*/)
+{
+ sLog.outString( "Re-Loading Skill Extra Item Table..." );
+ LoadSkillExtraItemTable();
+ SendGlobalSysMessage("DB table `skill_extra_item_template` (extra item creation when crafting) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSkillFishingBaseLevelCommand(const char* /*args*/)
+{
+ sLog.outString( "Re-Loading Skill Fishing base level requirements..." );
+ objmgr.LoadFishingBaseSkillLevel();
+ SendGlobalSysMessage("DB table `skill_fishing_base_level` (fishing base level for zone/subzone) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellAffectCommand(const char*)
+{
+ sLog.outString( "Re-Loading SpellAffect definitions..." );
+ spellmgr.LoadSpellAffects();
+ SendGlobalSysMessage("DB table `spell_affect` (spell mods apply requirements) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellChainCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell Chain Data... " );
+ spellmgr.LoadSpellChains();
+ SendGlobalSysMessage("DB table `spell_chain` (spell ranks) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellElixirCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell Elixir types..." );
+ spellmgr.LoadSpellElixirs();
+ SendGlobalSysMessage("DB table `spell_elixir` (spell exlixir types) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellLearnSpellCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell Learn Spells..." );
+ spellmgr.LoadSpellLearnSpells();
+ SendGlobalSysMessage("DB table `spell_learn_spell` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellProcEventCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell Proc Event conditions..." );
+ spellmgr.LoadSpellProcEvents();
+ SendGlobalSysMessage("DB table `spell_proc_event` (spell proc trigger requirements) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellScriptTargetCommand(const char*)
+{
+ sLog.outString( "Re-Loading SpellsScriptTarget..." );
+ spellmgr.LoadSpellScriptTarget();
+ SendGlobalSysMessage("DB table `spell_script_target` (spell targets selection in case specific creature/GO requirements) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellTargetPositionCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell target coordinates..." );
+ spellmgr.LoadSpellTargetPositions();
+ SendGlobalSysMessage("DB table `spell_target_position` (destination coordinates for spell targets) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellThreatsCommand(const char*)
+{
+ sLog.outString( "Re-Loading Aggro Spells Definitions...");
+ spellmgr.LoadSpellThreats();
+ SendGlobalSysMessage("DB table `spell_threat` (spell aggro definitions) reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellPetAurasCommand(const char*)
+{
+ sLog.outString( "Re-Loading Spell pet auras...");
+ spellmgr.LoadSpellPetAuras();
+ SendGlobalSysMessage("DB table `spell_pet_auras` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadPageTextsCommand(const char*)
+{
+ sLog.outString( "Re-Loading Page Texts..." );
+ objmgr.LoadPageTexts();
+ SendGlobalSysMessage("DB table `page_texts` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadItemEnchantementsCommand(const char*)
+{
+ sLog.outString( "Re-Loading Item Random Enchantments Table..." );
+ LoadRandomEnchantmentsTable();
+ SendGlobalSysMessage("DB table `item_enchantment_template` reloaded.");
+ return true;
+}
+
+bool ChatHandler::HandleReloadGameObjectScriptsCommand(const char* arg)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ SendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(*arg!='a')
+ sLog.outString( "Re-Loading Scripts from `gameobject_scripts`...");
+
+ objmgr.LoadGameObjectScripts();
+
+ if(*arg!='a')
+ SendGlobalSysMessage("DB table `gameobject_scripts` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadEventScriptsCommand(const char* arg)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ SendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(*arg!='a')
+ sLog.outString( "Re-Loading Scripts from `event_scripts`...");
+
+ objmgr.LoadEventScripts();
+
+ if(*arg!='a')
+ SendGlobalSysMessage("DB table `event_scripts` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadQuestEndScriptsCommand(const char* arg)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ SendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(*arg!='a')
+ sLog.outString( "Re-Loading Scripts from `quest_end_scripts`...");
+
+ objmgr.LoadQuestEndScripts();
+
+ if(*arg!='a')
+ SendGlobalSysMessage("DB table `quest_end_scripts` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadQuestStartScriptsCommand(const char* arg)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ SendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(*arg!='a')
+ sLog.outString( "Re-Loading Scripts from `quest_start_scripts`...");
+
+ objmgr.LoadQuestStartScripts();
+
+ if(*arg!='a')
+ SendGlobalSysMessage("DB table `quest_start_scripts` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadSpellScriptsCommand(const char* arg)
+{
+ if(sWorld.IsScriptScheduled())
+ {
+ SendSysMessage("DB scripts used currently, please attempt reload later.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(*arg!='a')
+ sLog.outString( "Re-Loading Scripts from `spell_scripts`...");
+
+ objmgr.LoadSpellScripts();
+
+ if(*arg!='a')
+ SendGlobalSysMessage("DB table `spell_scripts` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadGameGraveyardZoneCommand(const char* /*arg*/)
+{
+ sLog.outString( "Re-Loading Graveyard-zone links...");
+
+ objmgr.LoadGraveyardZones();
+
+ SendGlobalSysMessage("DB table `game_graveyard_zone` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleReloadGameTeleCommand(const char* /*arg*/)
+{
+ sLog.outString( "Re-Loading Game Tele coordinates...");
+
+ objmgr.LoadGameTele();
+
+ SendGlobalSysMessage("DB table `game_tele` reloaded.");
+
+ return true;
+}
+
+bool ChatHandler::HandleLoadScriptsCommand(const char* args)
+{
+ if(!LoadScriptingModule(args)) return true;
+
+ sWorld.SendWorldText(LANG_SCRIPTS_RELOADED);
+ return true;
+}
+
+bool ChatHandler::HandleSecurityCommand(const char* args)
+{
+ char* arg1 = strtok((char*)args, " ");
+ if( !arg1 )
+ return false;
+
+ char* arg2 = 0;
+
+ std::string targetName;
+ uint32 targetAccountId = 0;
+ uint32 targetSecurity = 0;
+
+ Player* targetPlayer = getSelectedPlayer();
+ if(targetPlayer)
+ {
+ targetName = targetPlayer->GetName();
+ targetAccountId = targetPlayer->GetSession()->GetAccountId();
+ targetSecurity = targetPlayer->GetSession()->GetSecurity();
+ arg2 = arg1;
+ }
+ else
+ {
+ targetName = arg1;
+ if(!normalizePlayerName(targetName))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ targetPlayer = ObjectAccessor::Instance().FindPlayerByName(targetName.c_str());
+ if(targetPlayer)
+ {
+ targetAccountId = targetPlayer->GetSession()->GetAccountId();
+ targetSecurity = targetPlayer->GetSession()->GetSecurity();
+ }
+ else
+ {
+ uint64 targetGUID = objmgr.GetPlayerGUIDByName(targetName.c_str());
+ if(!targetGUID)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ targetAccountId = objmgr.GetPlayerAccountIdByGUID(targetGUID);
+ targetSecurity = objmgr.GetSecurityByAccount(targetAccountId);
+ }
+
+ arg2 = strtok(NULL, " ");
+ }
+
+ int32 gm = (int32)atoi(arg2);
+ if ( gm < SEC_PLAYER || gm > SEC_ADMINISTRATOR )
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // can set security level only for target with less security and to less security that we have
+ if(targetSecurity >= m_session->GetSecurity() || uint32(gm) >= m_session->GetSecurity() )
+ {
+ SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(targetPlayer)
+ {
+ if( targetPlayer != m_session->GetPlayer() )
+ ChatHandler(targetPlayer).PSendSysMessage(LANG_YOURS_SECURITY_CHANGED,m_session->GetPlayer()->GetName(), gm);
+
+ targetPlayer->GetSession()->SetSecurity(gm);
+ }
+
+ PSendSysMessage(LANG_YOU_CHANGE_SECURITY, targetName.c_str(), gm);
+ loginDatabase.PExecute("UPDATE account SET gmlevel = '%i' WHERE id = '%u'", gm, targetAccountId);
+
+ return true;
+}
+
+bool ChatHandler::HandleAllowMovementCommand(const char* /*args*/)
+{
+ if(sWorld.getAllowMovement())
+ {
+ sWorld.SetAllowMovement(false);
+ SendSysMessage(LANG_CREATURE_MOVE_DISABLED);
+ }
+ else
+ {
+ sWorld.SetAllowMovement(true);
+ SendSysMessage(LANG_CREATURE_MOVE_ENABLED);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleMaxSkillCommand(const char* /*args*/)
+{
+ Player* SelectedPlayer = getSelectedPlayer();
+ if(!SelectedPlayer)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // each skills that have max skill value dependent from level seted to current level max skill value
+ SelectedPlayer->UpdateSkillsToMaxSkillsForLevel();
+ return true;
+}
+
+bool ChatHandler::HandleSetSkillCommand(const char* args)
+{
+ // number or [name] Shift-click form |color|Hskill:skill_id|h[name]|h|r
+ char* skill_p = extractKeyFromLink((char*)args,"Hskill");
+ if(!skill_p)
+ return false;
+
+ char *level_p = strtok (NULL, " ");
+
+ if( !level_p)
+ return false;
+
+ char *max_p = strtok (NULL, " ");
+
+ int32 skill = atoi(skill_p);
+
+ if (skill <= 0)
+ {
+ PSendSysMessage(LANG_INVALID_SKILL_ID, skill);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 level = atol (level_p);
+
+ Player * target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ SkillLineEntry const* sl = sSkillLineStore.LookupEntry(skill);
+ if(!sl)
+ {
+ PSendSysMessage(LANG_INVALID_SKILL_ID, skill);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!target->GetSkillValue(skill))
+ {
+ PSendSysMessage(LANG_SET_SKILL_ERROR, target->GetName(), skill, sl->name[0]);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 max = max_p ? atol (max_p) : target->GetPureMaxSkillValue(skill);
+
+ if( level <= 0 || level > max || max <= 0 )
+ return false;
+
+ target->SetSkill(skill, level, max);
+ PSendSysMessage(LANG_SET_SKILL, skill, sl->name[0], target->GetName(), level, max);
+
+ return true;
+}
+
+bool ChatHandler::HandleUnLearnCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
+ uint32 min_id = extractSpellIdFromLink((char*)args);
+ if(!min_id)
+ return false;
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
+ char* tail = strtok(NULL,"");
+
+ uint32 max_id = extractSpellIdFromLink(tail);
+
+ if (!max_id)
+ {
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
+ max_id = min_id+1;
+ }
+ else
+ {
+ if (max_id < min_id)
+ std::swap(min_id,max_id);
+
+ max_id=max_id+1;
+ }
+
+ Player* target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ for(uint32 spell=min_id;spell<max_id;spell++)
+ {
+ if (target->HasSpell(spell))
+ target->removeSpell(spell);
+ else
+ SendSysMessage(LANG_FORGET_SPELL);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleCooldownCommand(const char* args)
+{
+ Player* target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (!*args)
+ {
+ target->RemoveAllSpellCooldown();
+ PSendSysMessage(LANG_REMOVEALL_COOLDOWN, target->GetName());
+ }
+ else
+ {
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell_id = extractSpellIdFromLink((char*)args);
+ if(!spell_id)
+ return false;
+
+ if(!sSpellStore.LookupEntry(spell_id))
+ {
+ PSendSysMessage(LANG_UNKNOWN_SPELL, target==m_session->GetPlayer() ? GetMangosString(LANG_YOU) : target->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ WorldPacket data( SMSG_CLEAR_COOLDOWN, (4+8) );
+ data << uint32(spell_id);
+ data << uint64(target->GetGUID());
+ target->GetSession()->SendPacket(&data);
+ target->RemoveSpellCooldown(spell_id);
+ PSendSysMessage(LANG_REMOVE_COOLDOWN, spell_id, target==m_session->GetPlayer() ? GetMangosString(LANG_YOU) : target->GetName());
+ }
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllCommand(const char* /*args*/)
+{
+ static const char *allSpellList[] =
+ {
+ "3365",
+ "6233",
+ "6247",
+ "6246",
+ "6477",
+ "6478",
+ "22810",
+ "8386",
+ "21651",
+ "21652",
+ "522",
+ "7266",
+ "8597",
+ "2479",
+ "22027",
+ "6603",
+ "5019",
+ "133",
+ "168",
+ "227",
+ "5009",
+ "9078",
+ "668",
+ "203",
+ "20599",
+ "20600",
+ "81",
+ "20597",
+ "20598",
+ "20864",
+ "1459",
+ "5504",
+ "587",
+ "5143",
+ "118",
+ "5505",
+ "597",
+ "604",
+ "1449",
+ "1460",
+ "2855",
+ "1008",
+ "475",
+ "5506",
+ "1463",
+ "12824",
+ "8437",
+ "990",
+ "5145",
+ "8450",
+ "1461",
+ "759",
+ "8494",
+ "8455",
+ "8438",
+ "6127",
+ "8416",
+ "6129",
+ "8451",
+ "8495",
+ "8439",
+ "3552",
+ "8417",
+ "10138",
+ "12825",
+ "10169",
+ "10156",
+ "10144",
+ "10191",
+ "10201",
+ "10211",
+ "10053",
+ "10173",
+ "10139",
+ "10145",
+ "10192",
+ "10170",
+ "10202",
+ "10054",
+ "10174",
+ "10193",
+ "12826",
+ "2136",
+ "143",
+ "145",
+ "2137",
+ "2120",
+ "3140",
+ "543",
+ "2138",
+ "2948",
+ "8400",
+ "2121",
+ "8444",
+ "8412",
+ "8457",
+ "8401",
+ "8422",
+ "8445",
+ "8402",
+ "8413",
+ "8458",
+ "8423",
+ "8446",
+ "10148",
+ "10197",
+ "10205",
+ "10149",
+ "10215",
+ "10223",
+ "10206",
+ "10199",
+ "10150",
+ "10216",
+ "10207",
+ "10225",
+ "10151",
+ "116",
+ "205",
+ "7300",
+ "122",
+ "837",
+ "10",
+ "7301",
+ "7322",
+ "6143",
+ "120",
+ "865",
+ "8406",
+ "6141",
+ "7302",
+ "8461",
+ "8407",
+ "8492",
+ "8427",
+ "8408",
+ "6131",
+ "7320",
+ "10159",
+ "8462",
+ "10185",
+ "10179",
+ "10160",
+ "10180",
+ "10219",
+ "10186",
+ "10177",
+ "10230",
+ "10181",
+ "10161",
+ "10187",
+ "10220",
+ "2018",
+ "2663",
+ "12260",
+ "2660",
+ "3115",
+ "3326",
+ "2665",
+ "3116",
+ "2738",
+ "3293",
+ "2661",
+ "3319",
+ "2662",
+ "9983",
+ "8880",
+ "2737",
+ "2739",
+ "7408",
+ "3320",
+ "2666",
+ "3323",
+ "3324",
+ "3294",
+ "22723",
+ "23219",
+ "23220",
+ "23221",
+ "23228",
+ "23338",
+ "10788",
+ "10790",
+ "5611",
+ "5016",
+ "5609",
+ "2060",
+ "10963",
+ "10964",
+ "10965",
+ "22593",
+ "22594",
+ "596",
+ "996",
+ "499",
+ "768",
+ "17002",
+ "1448",
+ "1082",
+ "16979",
+ "1079",
+ "5215",
+ "20484",
+ "5221",
+ "15590",
+ "17007",
+ "6795",
+ "6807",
+ "5487",
+ "1446",
+ "1066",
+ "5421",
+ "3139",
+ "779",
+ "6811",
+ "6808",
+ "1445",
+ "5216",
+ "1737",
+ "5222",
+ "5217",
+ "1432",
+ "6812",
+ "9492",
+ "5210",
+ "3030",
+ "1441",
+ "783",
+ "6801",
+ "20739",
+ "8944",
+ "9491",
+ "22569",
+ "5226",
+ "6786",
+ "1433",
+ "8973",
+ "1828",
+ "9495",
+ "9006",
+ "6794",
+ "8993",
+ "5203",
+ "16914",
+ "6784",
+ "9635",
+ "22830",
+ "20722",
+ "9748",
+ "6790",
+ "9753",
+ "9493",
+ "9752",
+ "9831",
+ "9825",
+ "9822",
+ "5204",
+ "5401",
+ "22831",
+ "6793",
+ "9845",
+ "17401",
+ "9882",
+ "9868",
+ "20749",
+ "9893",
+ "9899",
+ "9895",
+ "9832",
+ "9902",
+ "9909",
+ "22832",
+ "9828",
+ "9851",
+ "9883",
+ "9869",
+ "17406",
+ "17402",
+ "9914",
+ "20750",
+ "9897",
+ "9848",
+ "3127",
+ "107",
+ "204",
+ "9116",
+ "2457",
+ "78",
+ "18848",
+ "331",
+ "403",
+ "2098",
+ "1752",
+ "11278",
+ "11288",
+ "11284",
+ "6461",
+ "2344",
+ "2345",
+ "6463",
+ "2346",
+ "2352",
+ "775",
+ "1434",
+ "1612",
+ "71",
+ "2468",
+ "2458",
+ "2467",
+ "7164",
+ "7178",
+ "7367",
+ "7376",
+ "7381",
+ "21156",
+ "5209",
+ "3029",
+ "5201",
+ "9849",
+ "9850",
+ "20719",
+ "22568",
+ "22827",
+ "22828",
+ "22829",
+ "6809",
+ "8972",
+ "9005",
+ "9823",
+ "9827",
+ "6783",
+ "9913",
+ "6785",
+ "6787",
+ "9866",
+ "9867",
+ "9894",
+ "9896",
+ "6800",
+ "8992",
+ "9829",
+ "9830",
+ "780",
+ "769",
+ "6749",
+ "6750",
+ "9755",
+ "9754",
+ "9908",
+ "20745",
+ "20742",
+ "20747",
+ "20748",
+ "9746",
+ "9745",
+ "9880",
+ "9881",
+ "5391",
+ "842",
+ "3025",
+ "3031",
+ "3287",
+ "3329",
+ "1945",
+ "3559",
+ "4933",
+ "4934",
+ "4935",
+ "4936",
+ "5142",
+ "5390",
+ "5392",
+ "5404",
+ "5420",
+ "6405",
+ "7293",
+ "7965",
+ "8041",
+ "8153",
+ "9033",
+ "9034",
+ //"9036", problems with ghost state
+ "16421",
+ "21653",
+ "22660",
+ "5225",
+ "9846",
+ "2426",
+ "5916",
+ "6634",
+ //"6718", phasing stealth, annoing for learn all case.
+ "6719",
+ "8822",
+ "9591",
+ "9590",
+ "10032",
+ "17746",
+ "17747",
+ "8203",
+ "11392",
+ "12495",
+ "16380",
+ "23452",
+ "4079",
+ "4996",
+ "4997",
+ "4998",
+ "4999",
+ "5000",
+ "6348",
+ "6349",
+ "6481",
+ "6482",
+ "6483",
+ "6484",
+ "11362",
+ "11410",
+ "11409",
+ "12510",
+ "12509",
+ "12885",
+ "13142",
+ "21463",
+ "23460",
+ "11421",
+ "11416",
+ "11418",
+ "1851",
+ "10059",
+ "11423",
+ "11417",
+ "11422",
+ "11419",
+ "11424",
+ "11420",
+ "27",
+ "31",
+ "33",
+ "34",
+ "35",
+ "15125",
+ "21127",
+ "22950",
+ "1180",
+ "201",
+ "12593",
+ "12842",
+ "16770",
+ "6057",
+ "12051",
+ "18468",
+ "12606",
+ "12605",
+ "18466",
+ "12502",
+ "12043",
+ "15060",
+ "12042",
+ "12341",
+ "12848",
+ "12344",
+ "12353",
+ "18460",
+ "11366",
+ "12350",
+ "12352",
+ "13043",
+ "11368",
+ "11113",
+ "12400",
+ "11129",
+ "16766",
+ "12573",
+ "15053",
+ "12580",
+ "12475",
+ "12472",
+ "12953",
+ "12488",
+ "11189",
+ "12985",
+ "12519",
+ "16758",
+ "11958",
+ "12490",
+ "11426",
+ "3565",
+ "3562",
+ "18960",
+ "3567",
+ "3561",
+ "3566",
+ "3563",
+ "1953",
+ "2139",
+ "12505",
+ "13018",
+ "12522",
+ "12523",
+ "5146",
+ "5144",
+ "5148",
+ "8419",
+ "8418",
+ "10213",
+ "10212",
+ "10157",
+ "12524",
+ "13019",
+ "12525",
+ "13020",
+ "12526",
+ "13021",
+ "18809",
+ "13031",
+ "13032",
+ "13033",
+ "4036",
+ "3920",
+ "3919",
+ "3918",
+ "7430",
+ "3922",
+ "3923",
+ "7411",
+ "7418",
+ "7421",
+ "13262",
+ "7412",
+ "7415",
+ "7413",
+ "7416",
+ "13920",
+ "13921",
+ "7745",
+ "7779",
+ "7428",
+ "7457",
+ "7857",
+ "7748",
+ "7426",
+ "13421",
+ "7454",
+ "13378",
+ "7788",
+ "14807",
+ "14293",
+ "7795",
+ "6296",
+ "20608",
+ "755",
+ "444",
+ "427",
+ "428",
+ "442",
+ "447",
+ "3578",
+ "3581",
+ "19027",
+ "3580",
+ "665",
+ "3579",
+ "3577",
+ "6755",
+ "3576",
+ "2575",
+ "2577",
+ "2578",
+ "2579",
+ "2580",
+ "2656",
+ "2657",
+ "2576",
+ "3564",
+ "10248",
+ "8388",
+ "2659",
+ "14891",
+ "3308",
+ "3307",
+ "10097",
+ "2658",
+ "3569",
+ "16153",
+ "3304",
+ "10098",
+ "4037",
+ "3929",
+ "3931",
+ "3926",
+ "3924",
+ "3930",
+ "3977",
+ "3925",
+ "136",
+ "228",
+ "5487",
+ "43",
+ "202",
+ "0"
+ };
+
+ int loop = 0;
+ while(strcmp(allSpellList[loop], "0"))
+ {
+ uint32 spell = atol((char*)allSpellList[loop++]);
+
+ if (m_session->GetPlayer()->HasSpell(spell))
+ continue;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ continue;
+ }
+
+ m_session->GetPlayer()->learnSpell(spell);
+ }
+
+ SendSysMessage(LANG_COMMAND_LEARN_MANY_SPELLS);
+
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllGMCommand(const char* /*args*/)
+{
+ static const char *gmSpellList[] =
+ {
+ "24347", // Become A Fish, No Breath Bar
+ "35132", // Visual Boom
+ "38488", // Attack 4000-8000 AOE
+ "38795", // Attack 2000 AOE + Slow Down 90%
+ "15712", // Attack 200
+ "1852", // GM Spell Silence
+ "31899", // Kill
+ "31924", // Kill
+ "29878", // Kill My Self
+ "26644", // More Kill
+
+ "28550", //Invisible 24
+ "23452", //Invisible + Target
+ "0"
+ };
+
+ uint16 gmSpellIter = 0;
+ while( strcmp(gmSpellList[gmSpellIter], "0") )
+ {
+ uint32 spell = atol((char*)gmSpellList[gmSpellIter++]);
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ continue;
+ }
+
+ m_session->GetPlayer()->learnSpell(spell);
+ }
+
+ SendSysMessage(LANG_LEARNING_GM_SKILLS);
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllMyClassCommand(const char* /*args*/)
+{
+ HandleLearnAllMySpellsCommand("");
+ HandleLearnAllMyTalentsCommand("");
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllMySpellsCommand(const char* /*args*/)
+{
+ ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(m_session->GetPlayer()->getClass());
+ if(!clsEntry)
+ return true;
+ uint32 family = clsEntry->spellfamily;
+
+ for (uint32 i = 0; i < sSpellStore.GetNumRows(); i++)
+ {
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(i);
+ if(!spellInfo)
+ continue;
+
+ // skip wrong class/race skills
+ if(!m_session->GetPlayer()->IsSpellFitByClassAndRace(spellInfo->Id))
+ continue;
+
+ // skip other spell families
+ if( spellInfo->SpellFamilyName != family)
+ continue;
+
+ //TODO: skip triggered spells
+
+ // skip spells with first rank learned as talent (and all talents then also)
+ uint32 first_rank = spellmgr.GetFirstSpellInChain(spellInfo->Id);
+ if(GetTalentSpellCost(first_rank) > 0 )
+ continue;
+
+ // skip broken spells
+ if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
+ continue;
+
+ m_session->GetPlayer()->learnSpell(i);
+ }
+
+ SendSysMessage(LANG_COMMAND_LEARN_CLASS_SPELLS);
+ return true;
+}
+
+static void learnAllHighRanks(Player* player, uint32 spellid)
+{
+ SpellChainMapNext const& nextMap = spellmgr.GetSpellChainNext();
+ for(SpellChainMapNext::const_iterator itr = nextMap.lower_bound(spellid); itr != nextMap.upper_bound(spellid); ++itr)
+ {
+ player->learnSpell(itr->second);
+ learnAllHighRanks(player,itr->second);
+ }
+}
+
+bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/)
+{
+ Player* player = m_session->GetPlayer();
+ uint32 classMask = player->getClassMask();
+
+ for (uint32 i = 0; i < sTalentStore.GetNumRows(); i++)
+ {
+ TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
+ if(!talentInfo)
+ continue;
+
+ TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
+ if(!talentTabInfo)
+ continue;
+
+ if( (classMask & talentTabInfo->ClassMask) == 0 )
+ continue;
+
+ // search highest talent rank
+ uint32 spellid = 0;
+ int rank = 4;
+ for(; rank >= 0; --rank)
+ {
+ if(talentInfo->RankID[rank]!=0)
+ {
+ spellid = talentInfo->RankID[rank];
+ break;
+ }
+ }
+
+ if(!spellid) // ??? none spells in telent
+ continue;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
+ continue;
+
+ // learn highest rank of talent
+ player->learnSpell(spellid);
+
+ // and learn all non-talent spell ranks (recursive by tree)
+ learnAllHighRanks(player,spellid);
+ }
+
+ SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS);
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllLangCommand(const char* /*args*/)
+{
+ // skipping UNIVERSAL language (0)
+ for(int i = 1; i < LANGUAGES_COUNT; ++i)
+ m_session->GetPlayer()->learnSpell(lang_description[i].spell_id);
+
+ SendSysMessage(LANG_COMMAND_LEARN_ALL_LANG);
+ return true;
+}
+
+bool ChatHandler::HandleLearnAllDefaultCommand(const char* args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ if (pName)
+ {
+ std::string name = pName;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player = objmgr.GetPlayer(name.c_str());
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player->learnDefaultSpells();
+ player->learnQuestRewardedSpells();
+
+ PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST,player->GetName());
+ return true;
+}
+
+bool ChatHandler::HandleLearnCommand(const char* args)
+{
+ Player* targetPlayer = getSelectedPlayer();
+
+ if(!targetPlayer)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell || !sSpellStore.LookupEntry(spell))
+ return false;
+
+ if (targetPlayer->HasSpell(spell))
+ {
+ if(targetPlayer == m_session->GetPlayer())
+ SendSysMessage(LANG_YOU_KNOWN_SPELL);
+ else
+ PSendSysMessage(LANG_TARGET_KNOWN_SPELL,targetPlayer->GetName());
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ targetPlayer->learnSpell(spell);
+
+ return true;
+}
+
+bool ChatHandler::HandleAddItemCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ uint32 itemId = 0;
+
+ if(args[0]=='[') // [name] manual form
+ {
+ char* citemName = citemName = strtok((char*)args, "]");
+
+ if(citemName && citemName[0])
+ {
+ std::string itemName = citemName+1;
+ WorldDatabase.escape_string(itemName);
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry FROM item_template WHERE name = '%s'", itemName.c_str());
+ if (!result)
+ {
+ PSendSysMessage(LANG_COMMAND_COULDNOTFIND, citemName+1);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ itemId = result->Fetch()->GetUInt16();
+ delete result;
+ }
+ else
+ return false;
+ }
+ else // item_id or [name] Shift-click form |color|Hitem:item_id:0:0:0|h[name]|h|r
+ {
+ char* cId = extractKeyFromLink((char*)args,"Hitem");
+ if(!cId)
+ return false;
+ itemId = atol(cId);
+ }
+
+ char* ccount = strtok(NULL, " ");
+
+ int32 count = 1;
+
+ if (ccount)
+ count = strtol(ccount, NULL, 10);
+
+ if (count == 0)
+ count = 1;
+
+ Player* pl = m_session->GetPlayer();
+ Player* plTarget = getSelectedPlayer();
+ if(!plTarget)
+ plTarget = pl;
+
+ sLog.outDetail(GetMangosString(LANG_ADDITEM), itemId, count);
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId);
+ if(!pProto)
+ {
+ PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, itemId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ //Subtract
+ if (count < 0)
+ {
+ plTarget->DestroyItemCount(itemId, -count, true, false);
+ PSendSysMessage(LANG_REMOVEITEM, itemId, -count, plTarget->GetName());
+ return true;
+ }
+
+ //Adding items
+ uint32 noSpaceForCount = 0;
+
+ // check space and find places
+ ItemPosCountVec dest;
+ uint8 msg = plTarget->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount );
+ if( msg != EQUIP_ERR_OK ) // convert to possible store amount
+ count -= noSpaceForCount;
+
+ if( count == 0 || dest.empty()) // can't add any
+ {
+ PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, noSpaceForCount );
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Item* item = plTarget->StoreNewItem( dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
+
+ // remove binding (let GM give it to another player later)
+ if(pl==plTarget)
+ for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ++itr)
+ if(Item* item1 = pl->GetItemByPos(itr->pos))
+ item1->SetBinding( false );
+
+ if(count > 0 && item)
+ {
+ pl->SendNewItem(item,count,false,true);
+ if(pl!=plTarget)
+ plTarget->SendNewItem(item,count,true,false);
+ }
+
+ if(noSpaceForCount > 0)
+ PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, noSpaceForCount);
+
+ return true;
+}
+
+bool ChatHandler::HandleAddItemSetCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ char* cId = extractKeyFromLink((char*)args,"Hitemset"); // number or [name] Shift-click form |color|Hitemset:itemset_id|h[name]|h|r
+ if (!cId)
+ return false;
+
+ uint32 itemsetId = atol(cId);
+
+ // prevent generation all items with itemset field value '0'
+ if (itemsetId == 0)
+ {
+ PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND,itemsetId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player* pl = m_session->GetPlayer();
+ Player* plTarget = getSelectedPlayer();
+ if(!plTarget)
+ plTarget = pl;
+
+ sLog.outDetail(GetMangosString(LANG_ADDITEMSET), itemsetId);
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry FROM item_template WHERE itemset = %u",itemsetId);
+
+ if(!result)
+ {
+ PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND,itemsetId);
+
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 itemId = fields[0].GetUInt32();
+
+ ItemPosCountVec dest;
+ uint8 msg = plTarget->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, 1 );
+ if( msg == EQUIP_ERR_OK )
+ {
+ Item* item = plTarget->StoreNewItem( dest, itemId, true);
+
+ // remove binding (let GM give it to another player later)
+ if(pl==plTarget)
+ item->SetBinding( false );
+
+ pl->SendNewItem(item,1,false,true);
+ if(pl!=plTarget)
+ plTarget->SendNewItem(item,1,true,false);
+ }
+ else
+ {
+ pl->SendEquipError( msg, NULL, NULL );
+ PSendSysMessage(LANG_ITEM_CANNOT_CREATE, itemId, 1);
+ }
+
+ }while( result->NextRow() );
+
+ delete result;
+
+ return true;
+}
+
+bool ChatHandler::HandleListItemCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* cId = extractKeyFromLink((char*)args,"Hitem");
+ if(!cId)
+ return false;
+ uint32 item_id = atol(cId);
+
+ ItemPrototype const* itemProto = item_id ? itemProto = objmgr.GetItemPrototype(item_id) : NULL;
+
+ if(!itemProto)
+ {
+ PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, item_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* c_count = strtok(NULL, " ");
+ int count = c_count ? atol(c_count) : 10;
+
+ if(count < 0)
+ return false;
+
+ QueryResult *result;
+
+ // inventory case
+ uint32 inv_count = 0;
+ result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM character_inventory WHERE item_template='%u'",item_id);
+ if(result)
+ {
+ inv_count = (*result)[0].GetUInt32();
+ delete result;
+ }
+
+ result=CharacterDatabase.PQuery(
+ // 0 1 2 3 4 5
+ "SELECT ci.item, cibag.slot AS bag, ci.slot, ci.guid, characters.account,characters.name "
+ "FROM character_inventory AS ci LEFT JOIN character_inventory AS cibag ON (cibag.item=ci.bag),characters "
+ "WHERE ci.item_template='%u' AND ci.guid = characters.guid LIMIT %u ",
+ item_id,uint32(count));
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 item_guid = fields[0].GetUInt32();
+ uint32 item_bag = fields[1].GetUInt32();
+ uint32 item_slot = fields[2].GetUInt32();
+ uint32 owner_guid = fields[3].GetUInt32();
+ uint32 owner_acc = fields[4].GetUInt32();
+ std::string owner_name = fields[5].GetCppString();
+
+ char const* item_pos = 0;
+ if(Player::IsEquipmentPos(item_bag,item_slot))
+ item_pos = "[equipped]";
+ else if(Player::IsInventoryPos(item_bag,item_slot))
+ item_pos = "[in inventory]";
+ else if(Player::IsBankPos(item_bag,item_slot))
+ item_pos = "[in bank]";
+ else
+ item_pos = "";
+
+ PSendSysMessage(LANG_ITEMLIST_SLOT,
+ item_guid,owner_name.c_str(),owner_guid,owner_acc,item_pos);
+ } while (result->NextRow());
+
+ int64 res_count = result->GetRowCount();
+
+ delete result;
+
+ if(count > res_count)
+ count-=res_count;
+ else if(count)
+ count = 0;
+ }
+
+ // mail case
+ uint32 mail_count = 0;
+ result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM mail_items WHERE item_template='%u'", item_id);
+ if(result)
+ {
+ mail_count = (*result)[0].GetUInt32();
+ delete result;
+ }
+
+ if(count > 0)
+ {
+ result=CharacterDatabase.PQuery(
+ // 0 1 2 3 4 5 6
+ "SELECT mail_items.item_guid, mail.sender, mail.receiver, char_s.account, char_s.name, char_r.account, char_r.name "
+ "FROM mail,mail_items,characters as char_s,characters as char_r "
+ "WHERE mail_items.item_template='%u' AND char_s.guid = mail.sender AND char_r.guid = mail.receiver AND mail.id=mail_items.mail_id LIMIT %u",
+ item_id,uint32(count));
+ }
+ else
+ result = NULL;
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 item_guid = fields[0].GetUInt32();
+ uint32 item_s = fields[1].GetUInt32();
+ uint32 item_r = fields[2].GetUInt32();
+ uint32 item_s_acc = fields[3].GetUInt32();
+ std::string item_s_name = fields[4].GetCppString();
+ uint32 item_r_acc = fields[5].GetUInt32();
+ std::string item_r_name = fields[6].GetCppString();
+
+ char const* item_pos = "[in mail]";
+
+ PSendSysMessage(LANG_ITEMLIST_MAIL,
+ item_guid,item_s_name.c_str(),item_s,item_s_acc,item_r_name.c_str(),item_r,item_r_acc,item_pos);
+ } while (result->NextRow());
+
+ int64 res_count = result->GetRowCount();
+
+ delete result;
+
+ if(count > res_count)
+ count-=res_count;
+ else if(count)
+ count = 0;
+ }
+
+ // auction case
+ uint32 auc_count = 0;
+ result=CharacterDatabase.PQuery("SELECT COUNT(item_template) FROM auctionhouse WHERE item_template='%u'",item_id);
+ if(result)
+ {
+ auc_count = (*result)[0].GetUInt32();
+ delete result;
+ }
+
+ if(count > 0)
+ {
+ result=CharacterDatabase.PQuery(
+ // 0 1 2 3
+ "SELECT auctionhouse.itemguid, auctionhouse.itemowner, characters.account, characters.name "
+ "FROM auctionhouse,characters WHERE auctionhouse.item_template='%u' AND characters.guid = auctionhouse.itemowner LIMIT %u",
+ item_id,uint32(count));
+ }
+ else
+ result = NULL;
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 item_guid = fields[0].GetUInt32();
+ uint32 owner = fields[1].GetUInt32();
+ uint32 owner_acc = fields[2].GetUInt32();
+ std::string owner_name = fields[3].GetCppString();
+
+ char const* item_pos = "[in auction]";
+
+ PSendSysMessage(LANG_ITEMLIST_AUCTION, item_guid, owner_name.c_str(), owner, owner_acc,item_pos);
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ if(inv_count+mail_count+auc_count == 0)
+ {
+ SendSysMessage(LANG_COMMAND_NOITEMFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_COMMAND_LISTITEMMESSAGE,item_id,inv_count+mail_count+auc_count,inv_count,mail_count,auc_count);
+
+ return true;
+}
+
+bool ChatHandler::HandleListObjectCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // number or [name] Shift-click form |color|Hgameobject_entry:go_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hgameobject_entry");
+ if(!cId)
+ return false;
+
+ uint32 go_id = atol(cId);
+
+ GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(go_id);
+
+ if(!go_id || !gInfo)
+ {
+ PSendSysMessage(LANG_COMMAND_LISTOBJINVALIDID, go_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* c_count = strtok(NULL, " ");
+ int count = c_count ? atol(c_count) : 10;
+
+ if(count < 0)
+ return false;
+
+ Player* pl = m_session->GetPlayer();
+ QueryResult *result;
+
+ uint32 obj_count = 0;
+ result=WorldDatabase.PQuery("SELECT COUNT(guid) FROM gameobject WHERE id='%u'",go_id);
+ if(result)
+ {
+ obj_count = (*result)[0].GetUInt32();
+ delete result;
+ }
+
+ result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM gameobject WHERE id = '%u' ORDER BY order_ ASC LIMIT %u",
+ pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),go_id,uint32(count));
+
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 guid = fields[0].GetUInt32();
+ float x = fields[1].GetFloat();
+ float y = fields[2].GetFloat();
+ float z = fields[3].GetFloat();
+ int mapid = fields[4].GetUInt16();
+
+ PSendSysMessage(LANG_GO_LIST, guid, guid, gInfo->name, x, y, z, mapid);
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ PSendSysMessage(LANG_COMMAND_LISTOBJMESSAGE,go_id,obj_count);
+ return true;
+}
+
+bool ChatHandler::HandleNearObjectCommand(const char* args)
+{
+ float distance = (!*args) ? 10 : atol(args);
+ uint32 count = 0;
+
+ Player* pl = m_session->GetPlayer();
+ QueryResult *result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, map, "
+ "(POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ "
+ "FROM gameobject WHERE map='%u' AND (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) <= '%f' ORDER BY order_",
+ pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),
+ pl->GetMapId(),pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(),distance*distance);
+
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 guid = fields[0].GetUInt32();
+ uint32 entry = fields[1].GetUInt32();
+ float x = fields[2].GetFloat();
+ float y = fields[3].GetFloat();
+ float z = fields[4].GetFloat();
+ int mapid = fields[5].GetUInt16();
+
+ GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(entry);
+
+ if(!gInfo)
+ continue;
+
+ PSendSysMessage(LANG_GO_LIST, guid, guid, gInfo->name, x, y, z, mapid);
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ PSendSysMessage(LANG_COMMAND_NEAROBJMESSAGE,distance,count);
+ return true;
+}
+
+bool ChatHandler::HandleListCreatureCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ // number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hcreature_entry");
+ if(!cId)
+ return false;
+
+ uint32 cr_id = atol(cId);
+
+ CreatureInfo const* cInfo = objmgr.GetCreatureTemplate(cr_id);
+
+ if(!cr_id || !cInfo)
+ {
+ PSendSysMessage(LANG_COMMAND_INVALIDCREATUREID, cr_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* c_count = strtok(NULL, " ");
+ int count = c_count ? atol(c_count) : 10;
+
+ if(count < 0)
+ return false;
+
+ Player* pl = m_session->GetPlayer();
+ QueryResult *result;
+
+ uint32 cr_count = 0;
+ result=WorldDatabase.PQuery("SELECT COUNT(guid) FROM creature WHERE id='%u'",cr_id);
+ if(result)
+ {
+ cr_count = (*result)[0].GetUInt32();
+ delete result;
+ }
+
+ result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map, (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ FROM creature WHERE id = '%u' ORDER BY order_ ASC LIMIT %u",
+ pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), cr_id,uint32(count));
+
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 guid = fields[0].GetUInt32();
+ float x = fields[1].GetFloat();
+ float y = fields[2].GetFloat();
+ float z = fields[3].GetFloat();
+ int mapid = fields[4].GetUInt16();
+
+ PSendSysMessage(LANG_CREATURE_LIST, guid, guid, cInfo->Name, x, y, z, mapid);
+ } while (result->NextRow());
+
+ delete result;
+ }
+
+ PSendSysMessage(LANG_COMMAND_LISTCREATUREMESSAGE,cr_id,cr_count);
+ return true;
+}
+
+bool ChatHandler::HandleLookupItemCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ // converting string that we try to find to lower case
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ wstrToLower(wnamepart);
+
+ uint32 counter = 0;
+
+ // Search in `item_template`
+ for (uint32 id = 0; id < sItemStorage.MaxEntry; id++)
+ {
+ ItemPrototype const *pProto = sItemStorage.LookupEntry<ItemPrototype >(id);
+ if(!pProto)
+ continue;
+
+ int loc_idx = m_session->GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
+ if (il)
+ {
+ if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
+ {
+ std::string name = il->Name[loc_idx];
+
+ if (Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_ITEM_LIST, id, id, name.c_str());
+ ++counter;
+ continue;
+ }
+ }
+ }
+ }
+
+ std::string name = pProto->Name1;
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_ITEM_LIST, id, id, name.c_str());
+ ++counter;
+ }
+ }
+
+ if (counter==0)
+ SendSysMessage(LANG_COMMAND_NOITEMFOUND);
+
+ return true;
+}
+
+bool ChatHandler::HandleLookupItemSetCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ // converting string that we try to find to lower case
+ wstrToLower( wnamepart );
+
+ uint32 counter = 0; // Counter for figure out that we found smth.
+
+ // Search in ItemSet.dbc
+ for (uint32 id = 0; id < sItemSetStore.GetNumRows(); id++)
+ {
+ ItemSetEntry const *set = sItemSetStore.LookupEntry(id);
+ if(set)
+ {
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = set->name[m_session->GetSessionDbcLocale()];
+ if(name.empty())
+ continue;
+
+ if (!Utf8FitTo(name, wnamepart))
+ {
+ loc = 0;
+ for(; loc < MAX_LOCALE; ++loc)
+ {
+ if(loc==m_session->GetSessionDbcLocale())
+ continue;
+
+ name = set->name[m_session->GetSessionDbcLocale()];
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ break;
+ }
+ }
+
+ if(loc < MAX_LOCALE)
+ {
+ // send item set in "id - [namedlink locale]" format
+ PSendSysMessage(LANG_ITEMSET_LIST,id,id,name.c_str(),localeNames[loc]);
+ ++counter;
+ }
+ }
+ }
+ if (counter == 0) // if counter == 0 then we found nth
+ SendSysMessage(LANG_COMMAND_NOITEMSETFOUND);
+ return true;
+}
+
+bool ChatHandler::HandleLookupSkillCommand(const char* args)
+{
+ Player* target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ // converting string that we try to find to lower case
+ wstrToLower( wnamepart );
+
+ uint32 counter = 0; // Counter for figure out that we found smth.
+
+ // Search in SkillLine.dbc
+ for (uint32 id = 0; id < sSkillLineStore.GetNumRows(); id++)
+ {
+ SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(id);
+ if(skillInfo)
+ {
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = skillInfo->name[loc];
+ if(name.empty())
+ continue;
+
+ if (!Utf8FitTo(name, wnamepart))
+ {
+ loc = 0;
+ for(; loc < MAX_LOCALE; ++loc)
+ {
+ if(loc==m_session->GetSessionDbcLocale())
+ continue;
+
+ name = skillInfo->name[loc];
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ break;
+ }
+ }
+
+ if(loc < MAX_LOCALE)
+ {
+ // send skill in "id - [namedlink locale]" format
+ PSendSysMessage(LANG_SKILL_LIST,id,id,name.c_str(),localeNames[loc],(target->HasSkill(id) ? m_session->GetMangosString(LANG_KNOWN) : ""));
+
+ ++counter;
+ }
+ }
+ }
+ if (counter == 0) // if counter == 0 then we found nth
+ SendSysMessage(LANG_COMMAND_NOSKILLFOUND);
+ return true;
+}
+
+bool ChatHandler::HandleLookupSpellCommand(const char* args)
+{
+ Player* target = getSelectedPlayer();
+ if( !target )
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ // converting string that we try to find to lower case
+ wstrToLower( wnamepart );
+
+ uint32 counter = 0; // Counter for figure out that we found smth.
+
+ // Search in Spell.dbc
+ for (uint32 id = 0; id < sSpellStore.GetNumRows(); id++)
+ {
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(id);
+ if(spellInfo)
+ {
+ int loc = m_session->GetSessionDbcLocale();
+ std::string name = spellInfo->SpellName[loc];
+ if(name.empty())
+ continue;
+
+ if (!Utf8FitTo(name, wnamepart))
+ {
+ loc = 0;
+ for(; loc < MAX_LOCALE; ++loc)
+ {
+ if(loc==m_session->GetSessionDbcLocale())
+ continue;
+
+ name = spellInfo->SpellName[loc];
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ break;
+ }
+ }
+
+ if(loc < MAX_LOCALE)
+ {
+ bool known = target->HasSpell(id);
+ bool learn = (spellInfo->Effect[0] == SPELL_EFFECT_LEARN_SPELL);
+
+ uint32 telentCost = GetTalentSpellCost(id);
+
+ bool talent = (telentCost > 0);
+ bool passive = IsPassiveSpell(id);
+ bool active = target->HasAura(id,0) || target->HasAura(id,1) || target->HasAura(id,2);
+
+ // unit32 used to prevent interpreting uint8 as char at output
+ // find rank of learned spell for learning spell, or talent rank
+ uint32 rank = telentCost ? telentCost : spellmgr.GetSpellRank(learn ? spellInfo->EffectTriggerSpell[0] : id);
+
+ // send spell in "id - [name, rank N] [talent] [passive] [learn] [known]" format
+ std::ostringstream ss;
+ ss << id << " - |cffffffff|Hspell:" << id << "|h[" << name;
+
+ // include rank in link name
+ if(rank)
+ ss << GetMangosString(LANG_SPELL_RANK) << rank;
+
+ ss << " " << localeNames[loc] << "]|h|r";
+
+ if(talent)
+ ss << GetMangosString(LANG_TALENT);
+ if(passive)
+ ss << GetMangosString(LANG_PASSIVE);
+ if(learn)
+ ss << GetMangosString(LANG_LEARN);
+ if(known)
+ ss << GetMangosString(LANG_KNOWN);
+ if(active)
+ ss << GetMangosString(LANG_ACTIVE);
+
+ SendSysMessage(ss.str().c_str());
+
+ ++counter;
+ }
+ }
+ }
+ if (counter == 0) // if counter == 0 then we found nth
+ SendSysMessage(LANG_COMMAND_NOSPELLFOUND);
+ return true;
+}
+
+bool ChatHandler::HandleLookupQuestCommand(const char* args)
+{
+ Player* target = getSelectedPlayer();
+ if( !target )
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ // converting string that we try to find to lower case
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ wstrToLower(wnamepart);
+
+ uint32 counter = 0 ;
+
+ ObjectMgr::QuestMap const& qTemplates = objmgr.GetQuestTemplates();
+ for (ObjectMgr::QuestMap::const_iterator iter = qTemplates.begin(); iter != qTemplates.end(); ++iter)
+ {
+ Quest * qinfo = iter->second;
+
+ int loc_idx = m_session->GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ QuestLocale const *il = objmgr.GetQuestLocale(qinfo->GetQuestId());
+ if (il)
+ {
+ if (il->Title.size() > loc_idx && !il->Title[loc_idx].empty())
+ {
+ std::string title = il->Title[loc_idx];
+
+ if (Utf8FitTo(title, wnamepart))
+ {
+ QuestStatus status = target->GetQuestStatus(qinfo->GetQuestId());
+
+ char const* statusStr = "";
+ if(status == QUEST_STATUS_COMPLETE)
+ {
+ if(target->GetQuestRewardStatus(qinfo->GetQuestId()))
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_REWARDED);
+ else
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_COMPLETE);
+ }
+ else if(status == QUEST_STATUS_INCOMPLETE)
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_ACTIVE);
+
+ PSendSysMessage(LANG_QUEST_LIST,qinfo->GetQuestId(),qinfo->GetQuestId(),title.c_str(),(status == QUEST_STATUS_COMPLETE ? GetMangosString(LANG_COMPLETE) : (status == QUEST_STATUS_INCOMPLETE ? GetMangosString(LANG_ACTIVE) : "") ));
+ ++counter;
+ continue;
+ }
+ }
+ }
+ }
+
+ std::string title = qinfo->GetTitle();
+ if(title.empty())
+ continue;
+
+ if (Utf8FitTo(title, wnamepart))
+ {
+ QuestStatus status = target->GetQuestStatus(qinfo->GetQuestId());
+
+ char const* statusStr = "";
+ if(status == QUEST_STATUS_COMPLETE)
+ {
+ if(target->GetQuestRewardStatus(qinfo->GetQuestId()))
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_REWARDED);
+ else
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_COMPLETE);
+ }
+ else if(status == QUEST_STATUS_INCOMPLETE)
+ statusStr = GetMangosString(LANG_COMMAND_QUEST_ACTIVE);
+
+ PSendSysMessage(LANG_QUEST_LIST,qinfo->GetQuestId(),qinfo->GetQuestId(), title.c_str(),(status == QUEST_STATUS_COMPLETE ? GetMangosString(LANG_COMPLETE) : (status == QUEST_STATUS_INCOMPLETE ? GetMangosString(LANG_ACTIVE) : "") ));
+ ++counter;
+ }
+ }
+
+ if (counter==0)
+ SendSysMessage(LANG_COMMAND_NOQUESTFOUND);
+
+ return true;
+}
+
+bool ChatHandler::HandleLookupCreatureCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ // converting string that we try to find to lower case
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ wstrToLower(wnamepart);
+
+ uint32 counter = 0;
+
+ for (uint32 id = 0; id< sCreatureStorage.MaxEntry; id++ )
+ {
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(id);
+ if(!cInfo)
+ continue;
+
+ int loc_idx = m_session->GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ CreatureLocale const *cl = objmgr.GetCreatureLocale(id);
+ if (cl)
+ {
+ if (cl->Name.size() > loc_idx && !cl->Name[loc_idx].empty())
+ {
+ std::string name = cl->Name[loc_idx];
+
+ if (Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_CREATURE_ENTRY_LIST, id, id, name.c_str());
+ ++counter;
+ continue;
+ }
+ }
+ }
+ }
+
+ std::string name = cInfo->Name;
+ if(name.empty())
+ continue;
+
+ if (Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_CREATURE_ENTRY_LIST,id,id,name.c_str());
+ ++counter;
+ }
+ }
+
+ if (counter==0)
+ SendSysMessage(LANG_COMMAND_NOCREATUREFOUND);
+
+ return true;
+}
+
+bool ChatHandler::HandleLookupObjectCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string namepart = args;
+ std::wstring wnamepart;
+
+ // converting string that we try to find to lower case
+ if(!Utf8toWStr(namepart,wnamepart))
+ return false;
+
+ wstrToLower(wnamepart);
+
+ uint32 counter = 0;
+
+ for (uint32 id = 0; id< sGOStorage.MaxEntry; id++ )
+ {
+ GameObjectInfo const* gInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
+ if(!gInfo)
+ continue;
+
+ int loc_idx = m_session->GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ GameObjectLocale const *gl = objmgr.GetGameObjectLocale(id);
+ if (gl)
+ {
+ if (gl->Name.size() > loc_idx && !gl->Name[loc_idx].empty())
+ {
+ std::string name = gl->Name[loc_idx];
+
+ if (Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_GO_ENTRY_LIST, id, id, name.c_str());
+ ++counter;
+ continue;
+ }
+ }
+ }
+ }
+
+ std::string name = gInfo->name;
+ if(name.empty())
+ continue;
+
+ if(Utf8FitTo(name, wnamepart))
+ {
+ PSendSysMessage(LANG_GO_ENTRY_LIST, id, id, name.c_str());
+ ++counter;
+ }
+ }
+
+ if(counter==0)
+ SendSysMessage(LANG_COMMAND_NOGAMEOBJECTFOUND);
+
+ return true;
+}
+
+/** \brief GM command level 3 - Create a guild.
+ *
+ * This command allows a GM (level 3) to create a guild.
+ *
+ * The "args" parameter contains the name of the guild leader
+ * and then the name of the guild.
+ *
+ */
+bool ChatHandler::HandleGuildCreateCommand(const char* args)
+{
+
+ if (!*args)
+ return false;
+
+ Guild *guild;
+ Player * player;
+ char *lname,*gname;
+ std::string guildname;
+
+ lname = strtok((char*)args, " ");
+ gname = strtok(NULL, "");
+
+ if(!lname)
+ return false;
+ else if(!gname)
+ {
+ SendSysMessage(LANG_INSERT_GUILD_NAME);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ guildname = gname;
+ player = ObjectAccessor::Instance().FindPlayerByName(lname);
+
+ if(!player)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!player->GetGuildId())
+ {
+ guild = new Guild;
+ if(!guild->create(player->GetGUID(),guildname))
+ {
+ delete guild;
+ SendSysMessage(LANG_GUILD_NOT_CREATED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ objmgr.AddGuild(guild);
+ }
+ else
+ SendSysMessage(LANG_PLAYER_IN_GUILD);
+
+ return true;
+}
+
+bool ChatHandler::HandleGuildInviteCommand(const char *args)
+{
+ if(!*args)
+ return false;
+
+ char* par1 = strtok((char*)args, " ");
+ char* par2 = strtok (NULL, "");
+ if(!par1 || !par2)
+ return false;
+
+ std::string glName = par2;
+ Guild* targetGuild = objmgr.GetGuildByName(glName);
+ if(!targetGuild)
+ return false;
+
+ std::string plName = par1;
+ if(!normalizePlayerName(plName))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 plGuid = 0;
+ if(Player* targetPlayer = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()))
+ plGuid = targetPlayer->GetGUID();
+ else
+ plGuid = objmgr.GetPlayerGUIDByName(plName.c_str());
+
+ if(!plGuid)
+ false;
+
+ // players's guild membership checked in AddMember before add
+ if(!targetGuild->AddMember(plGuid,targetGuild->GetLowestRank()))
+ return false;
+
+ return true;
+}
+
+bool ChatHandler::HandleGuildUninviteCommand(const char *args)
+{
+ if(!*args)
+ return false;
+
+ char* par1 = strtok((char*)args, " ");
+ if(!par1)
+ return false;
+ std::string plName = par1;
+ if(!normalizePlayerName(plName))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 plGuid = 0;
+ uint32 glId = 0;
+ if(Player* targetPlayer = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()))
+ {
+ plGuid = targetPlayer->GetGUID();
+ glId = targetPlayer->GetGuildId();
+ }
+ else
+ {
+ plGuid = objmgr.GetPlayerGUIDByName(plName.c_str());
+ glId = Player::GetGuildIdFromDB(plGuid);
+ }
+
+ if(!plGuid || !glId)
+ return false;
+
+ Guild* targetGuild = objmgr.GetGuildById(glId);
+ if(!targetGuild)
+ return false;
+
+ targetGuild->DelMember(plGuid);
+
+ return true;
+}
+
+bool ChatHandler::HandleGuildRankCommand(const char *args)
+{
+ if(!*args)
+ return false;
+
+ char* par1 = strtok((char*)args, " ");
+ char* par2 = strtok(NULL, " ");
+ if(!par1 || !par2)
+ return false;
+ std::string plName = par1;
+ if(!normalizePlayerName(plName))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 plGuid = 0;
+ uint32 glId = 0;
+ if(Player* targetPlayer = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()))
+ {
+ plGuid = targetPlayer->GetGUID();
+ glId = targetPlayer->GetGuildId();
+ }
+ else
+ {
+ plGuid = objmgr.GetPlayerGUIDByName(plName.c_str());
+ glId = Player::GetGuildIdFromDB(plGuid);
+ }
+
+ if(!plGuid || !glId)
+ return false;
+
+ Guild* targetGuild = objmgr.GetGuildById(glId);
+ if(!targetGuild)
+ return false;
+
+ uint32 newrank = uint32(atoi(par2));
+ if(newrank > targetGuild->GetLowestRank())
+ return false;
+
+ targetGuild->ChangeRank(plGuid,newrank);
+
+ return true;
+}
+
+bool ChatHandler::HandleGuildDeleteCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* par1 = strtok((char*)args, " ");
+ if(!par1)
+ return false;
+
+ std::string gld = par1;
+
+ Guild* targetGuild = objmgr.GetGuildByName(gld);
+ if(!targetGuild)
+ return false;
+
+ targetGuild->Disband();
+
+ return true;
+}
+
+bool ChatHandler::HandleGetDistanceCommand(const char* /*args*/)
+{
+ Unit* pUnit = getSelectedUnit();
+
+ if(!pUnit)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_DISTANCE, m_session->GetPlayer()->GetDistance(pUnit),m_session->GetPlayer()->GetDistance2d(pUnit));
+
+ return true;
+}
+
+// FIX-ME!!!
+
+bool ChatHandler::HandleAddWeaponCommand(const char* /*args*/)
+{
+ /*if (!*args)
+ return false;
+
+ uint64 guid = m_session->GetPlayer()->GetSelection();
+ if (guid == 0)
+ {
+ SendSysMessage(LANG_NO_SELECTION);
+ return true;
+ }
+
+ Creature *pCreature = ObjectAccessor::GetCreature(*m_session->GetPlayer(), guid);
+
+ if(!pCreature)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ return true;
+ }
+
+ char* pSlotID = strtok((char*)args, " ");
+ if (!pSlotID)
+ return false;
+
+ char* pItemID = strtok(NULL, " ");
+ if (!pItemID)
+ return false;
+
+ uint32 ItemID = atoi(pItemID);
+ uint32 SlotID = atoi(pSlotID);
+
+ ItemPrototype* tmpItem = objmgr.GetItemPrototype(ItemID);
+
+ bool added = false;
+ if(tmpItem)
+ {
+ switch(SlotID)
+ {
+ case 1:
+ pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, ItemID);
+ added = true;
+ break;
+ case 2:
+ pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_01, ItemID);
+ added = true;
+ break;
+ case 3:
+ pCreature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY_02, ItemID);
+ added = true;
+ break;
+ default:
+ PSendSysMessage(LANG_ITEM_SLOT_NOT_EXIST,SlotID);
+ added = false;
+ break;
+ }
+ if(added)
+ {
+ PSendSysMessage(LANG_ITEM_ADDED_TO_SLOT,ItemID,tmpItem->Name1,SlotID);
+ }
+ }
+ else
+ {
+ PSendSysMessage(LANG_ITEM_NOT_FOUND,ItemID);
+ return true;
+ }
+ */
+ return true;
+}
+
+bool ChatHandler::HandleDieCommand(const char* /*args*/)
+{
+ Unit* target = getSelectedUnit();
+
+ if(!target || !m_session->GetPlayer()->GetSelection())
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if( target->isAlive() )
+ {
+ m_session->GetPlayer()->DealDamage(target, target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleDamageCommand(const char * args)
+{
+ if (!*args)
+ return false;
+
+ Unit* target = getSelectedUnit();
+
+ if(!target || !m_session->GetPlayer()->GetSelection())
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if( !target->isAlive() )
+ return true;
+
+ char* damageStr = strtok((char*)args, " ");
+ if(!damageStr)
+ return false;
+
+ int32 damage = atoi((char*)damageStr);
+ if(damage <=0)
+ return true;
+
+ char* schoolStr = strtok((char*)NULL, " ");
+
+ // flat melee damage without resistence/etc reduction
+ if(!schoolStr)
+ {
+ m_session->GetPlayer()->DealDamage(target, damage, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ m_session->GetPlayer()->SendAttackStateUpdate (HITINFO_NORMALSWING2, target, 1, SPELL_SCHOOL_MASK_NORMAL, damage, 0, 0, VICTIMSTATE_NORMAL, 0);
+ return true;
+ }
+
+ uint32 school = schoolStr ? atoi((char*)schoolStr) : SPELL_SCHOOL_NORMAL;
+ if(school >= MAX_SPELL_SCHOOL)
+ return false;
+
+ SpellSchoolMask schoolmask = SpellSchoolMask(1 << school);
+
+ if ( schoolmask & SPELL_SCHOOL_MASK_NORMAL )
+ damage = m_session->GetPlayer()->CalcArmorReducedDamage(target, damage);
+
+ char* spellStr = strtok((char*)NULL, " ");
+
+ // melee damage by specific school
+ if(!spellStr)
+ {
+ uint32 absorb = 0;
+ uint32 resist = 0;
+
+ m_session->GetPlayer()->CalcAbsorbResist(target,schoolmask, SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
+
+ if (damage <= absorb + resist)
+ return true;
+
+ damage -= absorb + resist;
+
+ m_session->GetPlayer()->DealDamage(target, damage, NULL, DIRECT_DAMAGE, schoolmask, NULL, false);
+ m_session->GetPlayer()->SendAttackStateUpdate (HITINFO_NORMALSWING2, target, 1, schoolmask, damage, absorb, resist, VICTIMSTATE_NORMAL, 0);
+ return true;
+ }
+
+ // non-melee damage
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spellid = extractSpellIdFromLink((char*)args);
+ if(!spellid || !sSpellStore.LookupEntry(spellid))
+ return false;
+
+ m_session->GetPlayer()->SpellNonMeleeDamageLog(target, spellid, damage, false);
+ return true;
+}
+
+bool ChatHandler::HandleModifyArenaCommand(const char * args)
+{
+ if (!*args)
+ return false;
+
+ Player *target = getSelectedPlayer();
+ if(!target)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int32 amount = (uint32)atoi(args);
+
+ target->ModifyArenaPoints(amount);
+
+ PSendSysMessage(LANG_COMMAND_MODIFY_ARENA, target->GetName(), target->GetArenaPoints());
+
+ return true;
+}
+
+bool ChatHandler::HandleReviveCommand(const char* args)
+{
+ Player* SelectedPlayer = NULL;
+
+ if (*args)
+ {
+ std::string name = args;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ SelectedPlayer = objmgr.GetPlayer(name.c_str());
+ }
+ else
+ SelectedPlayer = getSelectedPlayer();
+
+ if(!SelectedPlayer)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ SelectedPlayer->ResurrectPlayer(0.5f);
+ SelectedPlayer->SpawnCorpseBones();
+ SelectedPlayer->SaveToDB();
+ return true;
+}
+
+bool ChatHandler::HandleAuraCommand(const char* args)
+{
+ char* px = strtok((char*)args, " ");
+ if (!px)
+ return false;
+
+ Unit *target = getSelectedUnit();
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 spellID = (uint32)atoi(px);
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellID );
+ if(spellInfo)
+ {
+ for(uint32 i = 0;i<3;i++)
+ {
+ uint8 eff = spellInfo->Effect[i];
+ if (eff>=TOTAL_SPELL_EFFECTS)
+ continue;
+ if( IsAreaAuraEffect(eff) ||
+ eff == SPELL_EFFECT_APPLY_AURA ||
+ eff == SPELL_EFFECT_PERSISTENT_AREA_AURA )
+ {
+ Aura *Aur = CreateAura(spellInfo, i, NULL, target);
+ target->AddAura(Aur);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleUnAuraCommand(const char* args)
+{
+ char* px = strtok((char*)args, " ");
+ if (!px)
+ return false;
+
+ Unit *target = getSelectedUnit();
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ std::string argstr = args;
+ if (argstr == "all")
+ {
+ target->RemoveAllAuras();
+ return true;
+ }
+
+ uint32 spellID = (uint32)atoi(px);
+ target->RemoveAurasDueToSpell(spellID);
+
+ return true;
+}
+
+bool ChatHandler::HandleLinkGraveCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* px = strtok((char*)args, " ");
+ if (!px)
+ return false;
+
+ uint32 g_id = (uint32)atoi(px);
+
+ uint32 g_team;
+
+ char* px2 = strtok(NULL, " ");
+
+ if (!px2)
+ g_team = 0;
+ else if (strncmp(px2,"horde",6)==0)
+ g_team = HORDE;
+ else if (strncmp(px2,"alliance",9)==0)
+ g_team = ALLIANCE;
+ else
+ return false;
+
+ WorldSafeLocsEntry const* graveyard = sWorldSafeLocsStore.LookupEntry(g_id);
+
+ if(!graveyard )
+ {
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST, g_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ Player* player = m_session->GetPlayer();
+
+ uint32 zoneId = player->GetZoneId();
+
+ AreaTableEntry const *areaEntry = GetAreaEntryByAreaID(zoneId);
+ if(!areaEntry || areaEntry->zone !=0 )
+ {
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDWRONGZONE, g_id,zoneId);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(graveyard->map_id != areaEntry->mapid && g_team != 0)
+ {
+ SendSysMessage(LANG_COMMAND_GRAVEYARDWRONGTEAM);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(objmgr.AddGraveYardLink(g_id,player->GetZoneId(),g_team))
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDLINKED, g_id,zoneId);
+ else
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDALRLINKED, g_id,zoneId);
+
+ return true;
+}
+
+bool ChatHandler::HandleNearGraveCommand(const char* args)
+{
+ uint32 g_team;
+
+ size_t argslen = strlen(args);
+
+ if(!*args)
+ g_team = 0;
+ else if (strncmp((char*)args,"horde",argslen)==0)
+ g_team = HORDE;
+ else if (strncmp((char*)args,"alliance",argslen)==0)
+ g_team = ALLIANCE;
+ else
+ return false;
+
+ Player* player = m_session->GetPlayer();
+
+ WorldSafeLocsEntry const* graveyard = objmgr.GetClosestGraveYard(
+ player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(),player->GetMapId(),g_team);
+
+ if(graveyard)
+ {
+ uint32 g_id = graveyard->ID;
+
+ GraveYardData const* data = objmgr.FindGraveYardData(g_id,player->GetZoneId());
+ if (!data)
+ {
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDERROR,g_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ g_team = data->team;
+
+ std::string team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_NOTEAM);
+
+ if(g_team == 0)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ANY);
+ else if(g_team == HORDE)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_HORDE);
+ else if(g_team == ALLIANCE)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ALLIANCE);
+
+ PSendSysMessage(LANG_COMMAND_GRAVEYARDNEAREST, g_id,team_name.c_str(),player->GetZoneId());
+ }
+ else
+ {
+ std::string team_name;
+
+ if(g_team == 0)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ANY);
+ else if(g_team == HORDE)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_HORDE);
+ else if(g_team == ALLIANCE)
+ team_name = GetMangosString(LANG_COMMAND_GRAVEYARD_ALLIANCE);
+
+ if(g_team == ~uint32(0))
+ PSendSysMessage(LANG_COMMAND_ZONENOGRAVEYARDS, player->GetZoneId());
+ else
+ PSendSysMessage(LANG_COMMAND_ZONENOGRAFACTION, player->GetZoneId(),team_name.c_str());
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleSpawnTransportCommand(const char* /*args*/)
+{
+ return true;
+}
+
+//play npc emote
+bool ChatHandler::HandlePlayEmoteCommand(const char* args)
+{
+ uint32 emote = atoi((char*)args);
+
+ Creature* target = getSelectedCreature();
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target->SetUInt32Value(UNIT_NPC_EMOTESTATE,emote);
+
+ return true;
+}
+
+bool ChatHandler::HandleNpcInfoCommand(const char* /*args*/)
+{
+ Creature* target = getSelectedCreature();
+
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 faction = target->getFaction();
+ uint32 npcflags = target->GetUInt32Value(UNIT_NPC_FLAGS);
+ uint32 displayid = target->GetDisplayId();
+ uint32 nativeid = target->GetNativeDisplayId();
+ uint32 Entry = target->GetEntry();
+ CreatureInfo const* cInfo = target->GetCreatureInfo();
+
+ int32 curRespawnDelay = target->GetRespawnTimeEx()-time(NULL);
+ if(curRespawnDelay < 0)
+ curRespawnDelay = 0;
+ std::string curRespawnDelayStr = secsToTimeString(curRespawnDelay,true);
+ std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(),true);
+
+ PSendSysMessage(LANG_NPCINFO_CHAR, target->GetDBTableGUIDLow(), faction, npcflags, Entry, displayid, nativeid);
+ PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel());
+ PSendSysMessage(LANG_NPCINFO_HEALTH,target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth());
+ PSendSysMessage(LANG_NPCINFO_FLAGS, target->GetUInt32Value(UNIT_FIELD_FLAGS), target->GetUInt32Value(UNIT_DYNAMIC_FLAGS), target->getFaction());
+ PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(),curRespawnDelayStr.c_str());
+ PSendSysMessage(LANG_NPCINFO_LOOT, cInfo->lootid,cInfo->pickpocketLootId,cInfo->SkinLootId);
+ PSendSysMessage(LANG_NPCINFO_DUNGEON_ID, target->GetInstanceId());
+ PSendSysMessage(LANG_NPCINFO_POSITION,float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ()));
+
+ if ((npcflags & UNIT_NPC_FLAG_VENDOR) )
+ {
+ SendSysMessage(LANG_NPCINFO_VENDOR);
+ }
+ if ((npcflags & UNIT_NPC_FLAG_TRAINER) )
+ {
+ SendSysMessage(LANG_NPCINFO_TRAINER);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleExploreCheatCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ int flag = atoi((char*)args);
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (flag != 0)
+ {
+ PSendSysMessage(LANG_YOU_SET_EXPLORE_ALL, chr->GetName());
+ if(chr!=m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_ALL,m_session->GetPlayer()->GetName());
+ }
+ else
+ {
+ PSendSysMessage(LANG_YOU_SET_EXPLORE_NOTHING, chr->GetName());
+ if(chr!=m_session->GetPlayer())
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_NOTHING,m_session->GetPlayer()->GetName());
+ }
+
+ for (uint8 i=0; i<128; i++)
+ {
+ if (flag != 0)
+ {
+ m_session->GetPlayer()->SetFlag(PLAYER_EXPLORED_ZONES_1+i,0xFFFFFFFF);
+ }
+ else
+ {
+ m_session->GetPlayer()->SetFlag(PLAYER_EXPLORED_ZONES_1+i,0);
+ }
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleHoverCommand(const char* args)
+{
+ char* px = strtok((char*)args, " ");
+ uint32 flag;
+ if (!px)
+ flag = 1;
+ else
+ flag = atoi(px);
+
+ m_session->GetPlayer()->SetHover(flag);
+
+ if (flag)
+ SendSysMessage(LANG_HOVER_ENABLED);
+ else
+ SendSysMessage(LANG_HOVER_DISABLED);
+
+ return true;
+}
+
+bool ChatHandler::HandleLevelUpCommand(const char* args)
+{
+ char* px = strtok((char*)args, " ");
+ char* py = strtok((char*)NULL, " ");
+
+ // command format parsing
+ char* pname = (char*)NULL;
+ int addlevel = 1;
+
+ if(px && py) // .levelup name level
+ {
+ addlevel = atoi(py);
+ pname = px;
+ }
+ else if(px && !py) // .levelup name OR .levelup level
+ {
+ if(isalpha(px[0])) // .levelup name
+ pname = px;
+ else // .levelup level
+ addlevel = atoi(px);
+ }
+ // else .levelup - nothing do for prepering
+
+ // player
+ Player *chr = NULL;
+ uint64 chr_guid = 0;
+
+ std::string name;
+
+ if(pname) // player by name
+ {
+ name = pname;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ chr = objmgr.GetPlayer(name.c_str());
+ if(!chr) // not in game
+ {
+ chr_guid = objmgr.GetPlayerGUIDByName(name);
+ if (chr_guid == 0)
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ }
+ }
+ else // player by selection
+ {
+ chr = getSelectedPlayer();
+
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ name = chr->GetName();
+ }
+
+ assert(chr || chr_guid);
+
+ int32 oldlevel = chr ? chr->getLevel() : Player::GetUInt32ValueFromDB(UNIT_FIELD_LEVEL,chr_guid);
+ int32 newlevel = oldlevel + addlevel;
+ if(newlevel < 1)
+ newlevel = 1;
+ if(newlevel > 255) // hardcoded maximum level
+ newlevel = 255;
+
+ if(chr)
+ {
+ chr->GiveLevel(newlevel);
+ chr->InitTalentForLevel();
+ chr->SetUInt32Value(PLAYER_XP,0);
+
+ if(oldlevel == newlevel)
+ ChatHandler(chr).SendSysMessage(LANG_YOURS_LEVEL_PROGRESS_RESET);
+ else
+ if(oldlevel < newlevel)
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_LEVEL_UP,newlevel-oldlevel);
+ else
+ if(oldlevel > newlevel)
+ ChatHandler(chr).PSendSysMessage(LANG_YOURS_LEVEL_DOWN,newlevel-oldlevel);
+ }
+ else
+ {
+ // update levle and XP at level, all other will be updated at loading
+ Tokens values;
+ Player::LoadValuesArrayFromDB(values,chr_guid);
+ Player::SetUInt32ValueInArray(values,UNIT_FIELD_LEVEL,newlevel);
+ Player::SetUInt32ValueInArray(values,PLAYER_XP,0);
+ Player::SaveValuesArrayInDB(values,chr_guid);
+ }
+
+ if(m_session->GetPlayer() != chr) // including chr==NULL
+ PSendSysMessage(LANG_YOU_CHANGE_LVL,name.c_str(),newlevel);
+ return true;
+}
+
+bool ChatHandler::HandleShowAreaCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ int area = atoi((char*)args);
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int offset = area / 32;
+ uint32 val = (uint32)(1 << (area % 32));
+
+ if(offset >= 128)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 currFields = chr->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
+ chr->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val));
+
+ SendSysMessage(LANG_EXPLORE_AREA);
+ return true;
+}
+
+bool ChatHandler::HandleHideAreaCommand(const char* args)
+{
+ if (!*args)
+ return false;
+
+ int area = atoi((char*)args);
+
+ Player *chr = getSelectedPlayer();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ int offset = area / 32;
+ uint32 val = (uint32)(1 << (area % 32));
+
+ if(offset >= 128)
+ {
+ SendSysMessage(LANG_BAD_VALUE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint32 currFields = chr->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
+ chr->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields ^ val));
+
+ SendSysMessage(LANG_UNEXPLORE_AREA);
+ return true;
+}
+
+bool ChatHandler::HandleUpdate(const char* args)
+{
+ if(!*args)
+ return false;
+
+ uint32 updateIndex;
+ uint32 value;
+
+ char* pUpdateIndex = strtok((char*)args, " ");
+
+ Unit* chr = getSelectedUnit();
+ if (chr == NULL)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!pUpdateIndex)
+ {
+ return true;
+ }
+ updateIndex = atoi(pUpdateIndex);
+ //check updateIndex
+ if(chr->GetTypeId() == TYPEID_PLAYER)
+ {
+ if (updateIndex>=PLAYER_END) return true;
+ }
+ else
+ {
+ if (updateIndex>=UNIT_END) return true;
+ }
+
+ char* pvalue = strtok(NULL, " ");
+ if (!pvalue)
+ {
+ value=chr->GetUInt32Value(updateIndex);
+
+ PSendSysMessage(LANG_UPDATE, chr->GetGUIDLow(),updateIndex,value);
+ return true;
+ }
+
+ value=atoi(pvalue);
+
+ PSendSysMessage(LANG_UPDATE_CHANGE, chr->GetGUIDLow(),updateIndex,value);
+
+ chr->SetUInt32Value(updateIndex,value);
+
+ return true;
+}
+
+bool ChatHandler::HandleBankCommand(const char* /*args*/)
+{
+ m_session->SendShowBank( m_session->GetPlayer()->GetGUID() );
+
+ return true;
+}
+
+bool ChatHandler::HandleChangeWeather(const char* args)
+{
+ if(!*args)
+ return false;
+
+ //Weather is OFF
+ if (!sWorld.getConfig(CONFIG_WEATHER))
+ {
+ SendSysMessage(LANG_WEATHER_DISABLED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ //*Change the weather of a cell
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+
+ if (!px || !py)
+ return false;
+
+ uint32 type = (uint32)atoi(px); //0 to 3, 0: fine, 1: rain, 2: snow, 3: sand
+ float grade = (float)atof(py); //0 to 1, sending -1 is instand good weather
+
+ Player *player = m_session->GetPlayer();
+ uint32 zoneid = player->GetZoneId();
+
+ Weather* wth = sWorld.FindWeather(zoneid);
+
+ if(!wth)
+ wth = sWorld.AddWeather(zoneid);
+ if(!wth)
+ {
+ SendSysMessage(LANG_NO_WEATHER);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ wth->SetWeather(WeatherType(type), grade);
+
+ return true;
+}
+
+bool ChatHandler::HandleSetValue(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+ char* pz = strtok(NULL, " ");
+
+ if (!px || !py)
+ return false;
+
+ Unit* target = getSelectedUnit();
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = target->GetGUID();
+
+ uint32 Opcode = (uint32)atoi(px);
+ if(Opcode >= target->GetValuesCount())
+ {
+ PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, GUID_LOPART(guid), target->GetValuesCount());
+ return false;
+ }
+ uint32 iValue;
+ float fValue;
+ bool isint32 = true;
+ if(pz)
+ isint32 = (bool)atoi(pz);
+ if(isint32)
+ {
+ iValue = (uint32)atoi(py);
+ sLog.outDebug(GetMangosString(LANG_SET_UINT), GUID_LOPART(guid), Opcode, iValue);
+ target->SetUInt32Value( Opcode , iValue );
+ PSendSysMessage(LANG_SET_UINT_FIELD, GUID_LOPART(guid), Opcode,iValue);
+ }
+ else
+ {
+ fValue = (float)atof(py);
+ sLog.outDebug(GetMangosString(LANG_SET_FLOAT), GUID_LOPART(guid), Opcode, fValue);
+ target->SetFloatValue( Opcode , fValue );
+ PSendSysMessage(LANG_SET_FLOAT_FIELD, GUID_LOPART(guid), Opcode,fValue);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleGetValue(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* px = strtok((char*)args, " ");
+ char* pz = strtok(NULL, " ");
+
+ if (!px)
+ return false;
+
+ Unit* target = getSelectedUnit();
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = target->GetGUID();
+
+ uint32 Opcode = (uint32)atoi(px);
+ if(Opcode >= target->GetValuesCount())
+ {
+ PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, GUID_LOPART(guid), target->GetValuesCount());
+ return false;
+ }
+ uint32 iValue;
+ float fValue;
+ bool isint32 = true;
+ if(pz)
+ isint32 = (bool)atoi(pz);
+
+ if(isint32)
+ {
+ iValue = target->GetUInt32Value( Opcode );
+ sLog.outDebug(GetMangosString(LANG_GET_UINT), GUID_LOPART(guid), Opcode, iValue);
+ PSendSysMessage(LANG_GET_UINT_FIELD, GUID_LOPART(guid), Opcode, iValue);
+ }
+ else
+ {
+ fValue = target->GetFloatValue( Opcode );
+ sLog.outDebug(GetMangosString(LANG_GET_FLOAT), GUID_LOPART(guid), Opcode, fValue);
+ PSendSysMessage(LANG_GET_FLOAT_FIELD, GUID_LOPART(guid), Opcode, fValue);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleSet32Bit(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+
+ if (!px || !py)
+ return false;
+
+ uint32 Opcode = (uint32)atoi(px);
+ uint32 Value = (uint32)atoi(py);
+ if (Value > 32) //uint32 = 32 bits
+ return false;
+
+ sLog.outDebug(GetMangosString(LANG_SET_32BIT), Opcode, Value);
+
+ m_session->GetPlayer( )->SetUInt32Value( Opcode , 2^Value );
+
+ PSendSysMessage(LANG_SET_32BIT_FIELD, Opcode,1);
+ return true;
+}
+
+bool ChatHandler::HandleMod32Value(const char* args)
+{
+ if(!*args)
+ return false;
+
+ char* px = strtok((char*)args, " ");
+ char* py = strtok(NULL, " ");
+
+ if (!px || !py)
+ return false;
+
+ uint32 Opcode = (uint32)atoi(px);
+ int Value = atoi(py);
+
+ if(Opcode >= m_session->GetPlayer()->GetValuesCount())
+ {
+ PSendSysMessage(LANG_TOO_BIG_INDEX, Opcode, m_session->GetPlayer()->GetGUIDLow(), m_session->GetPlayer( )->GetValuesCount());
+ return false;
+ }
+
+ sLog.outDebug(GetMangosString(LANG_CHANGE_32BIT), Opcode, Value);
+
+ int CurrentValue = (int)m_session->GetPlayer( )->GetUInt32Value( Opcode );
+
+ CurrentValue += Value;
+ m_session->GetPlayer( )->SetUInt32Value( Opcode , (uint32)CurrentValue );
+
+ PSendSysMessage(LANG_CHANGE_32BIT_FIELD, Opcode,CurrentValue);
+
+ return true;
+}
+
+bool ChatHandler::HandleAddTeleCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ Player *player=m_session->GetPlayer();
+ if (!player)
+ return false;
+
+ std::string name = args;
+
+ if(objmgr.GetGameTele(name))
+ {
+ SendSysMessage(LANG_COMMAND_TP_ALREADYEXIST);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ GameTele tele;
+ tele.position_x = player->GetPositionX();
+ tele.position_y = player->GetPositionY();
+ tele.position_z = player->GetPositionZ();
+ tele.orientation = player->GetOrientation();
+ tele.mapId = player->GetMapId();
+ tele.name = name;
+
+ if(objmgr.AddGameTele(tele))
+ {
+ SendSysMessage(LANG_COMMAND_TP_ADDED);
+ }
+ else
+ {
+ SendSysMessage(LANG_COMMAND_TP_ADDEDERR);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleDelTeleCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ std::string name = args;
+
+ if(!objmgr.DeleteGameTele(name))
+ {
+ SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ SendSysMessage(LANG_COMMAND_TP_DELETED);
+ return true;
+}
+
+bool ChatHandler::HandleListAurasCommand (const char * /*args*/)
+{
+ Unit *unit = getSelectedUnit();
+ if(!unit)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char const* talentStr = GetMangosString(LANG_TALENT);
+ char const* passiveStr = GetMangosString(LANG_PASSIVE);
+
+ Unit::AuraMap const& uAuras = unit->GetAuras();
+ PSendSysMessage(LANG_COMMAND_TARGET_LISTAURAS, uAuras.size());
+ for (Unit::AuraMap::const_iterator itr = uAuras.begin(); itr != uAuras.end(); ++itr)
+ {
+ bool talent = GetTalentSpellCost(itr->second->GetId()) > 0;
+ PSendSysMessage(LANG_COMMAND_TARGET_AURADETAIL, itr->second->GetId(), itr->second->GetEffIndex(),
+ itr->second->GetModifier()->m_auraname, itr->second->GetAuraDuration(), itr->second->GetAuraMaxDuration(),
+ itr->second->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()],
+ (itr->second->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""),
+ IS_PLAYER_GUID(itr->second->GetCasterGUID()) ? "player" : "creature",GUID_LOPART(itr->second->GetCasterGUID()));
+ }
+ for (int i = 0; i < TOTAL_AURAS; i++)
+ {
+ Unit::AuraList const& uAuraList = unit->GetAurasByType(AuraType(i));
+ if (uAuraList.empty()) continue;
+ PSendSysMessage(LANG_COMMAND_TARGET_LISTAURATYPE, uAuraList.size(), i);
+ for (Unit::AuraList::const_iterator itr = uAuraList.begin(); itr != uAuraList.end(); ++itr)
+ {
+ bool talent = GetTalentSpellCost((*itr)->GetId()) > 0;
+ PSendSysMessage(LANG_COMMAND_TARGET_AURASIMPLE, (*itr)->GetId(), (*itr)->GetEffIndex(),
+ (*itr)->GetSpellProto()->SpellName[m_session->GetSessionDbcLocale()],((*itr)->IsPassive() ? passiveStr : ""),(talent ? talentStr : ""),
+ IS_PLAYER_GUID((*itr)->GetCasterGUID()) ? "player" : "creature",GUID_LOPART((*itr)->GetCasterGUID()));
+ }
+ }
+ return true;
+}
+
+bool ChatHandler::HandleResetHonorCommand (const char * args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ if (pName)
+ {
+ std::string name = pName;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str());
+ player = objmgr.GetPlayer(guid);
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ return true;
+ }
+
+ player->SetUInt32Value(PLAYER_FIELD_KILLS, 0);
+ player->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0);
+ player->SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, 0);
+ player->SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0);
+ player->SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0);
+
+ return true;
+}
+
+static bool HandleResetStatsOrLevelHelper(Player* player)
+{
+ PlayerInfo const *info = objmgr.GetPlayerInfo(player->getRace(), player->getClass());
+ if(!info) return false;
+
+ ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(player->getClass());
+ if(!cEntry)
+ {
+ sLog.outError("Class %u not found in DBC (Wrong DBC files?)",player->getClass());
+ return false;
+ }
+
+ uint8 powertype = cEntry->powerType;
+
+ uint32 unitfield;
+ if(powertype == POWER_RAGE)
+ unitfield = 0x1100EE00;
+ else if(powertype == POWER_ENERGY)
+ unitfield = 0x00000000;
+ else if(powertype == POWER_MANA)
+ unitfield = 0x0000EE00;
+ else
+ {
+ sLog.outError("Invalid default powertype %u for player (class %u)",powertype,player->getClass());
+ return false;
+ }
+
+ // reset m_form if no aura
+ if(!player->HasAuraType(SPELL_AURA_MOD_SHAPESHIFT))
+ player->m_form = FORM_NONE;
+
+ player->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE );
+ player->SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f );
+
+ player->setFactionForRace(player->getRace());
+
+ player->SetUInt32Value(UNIT_FIELD_BYTES_0, ( ( player->getRace() ) | ( player->getClass() << 8 ) | ( player->getGender() << 16 ) | ( powertype << 24 ) ) );
+
+ // reset only if player not in some form;
+ if(player->m_form==FORM_NONE)
+ {
+ switch(player->getGender())
+ {
+ case GENDER_FEMALE:
+ player->SetDisplayId(info->displayId_f);
+ player->SetNativeDisplayId(info->displayId_f);
+ break;
+ case GENDER_MALE:
+ player->SetDisplayId(info->displayId_m);
+ player->SetNativeDisplayId(info->displayId_m);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // set UNIT_FIELD_BYTES_1 to init state but preserve m_form value
+ player->SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield);
+ player->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_UNK5 );
+ player->SetByteValue(UNIT_FIELD_BYTES_2, 3, player->m_form);
+
+ player->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+
+ //-1 is default value
+ player->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1));
+
+ //player->SetUInt32Value(PLAYER_FIELD_BYTES, 0xEEE00000 );
+ return true;
+}
+
+bool ChatHandler::HandleResetLevelCommand(const char * args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ if (pName)
+ {
+ std::string name = pName;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str());
+ player = objmgr.GetPlayer(guid);
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!HandleResetStatsOrLevelHelper(player))
+ return false;
+
+ player->SetLevel(1);
+ player->InitStatsForLevel(true);
+ player->InitTaxiNodesForLevel();
+ player->InitTalentForLevel();
+ player->SetUInt32Value(PLAYER_XP,0);
+
+ // reset level to summoned pet
+ Pet* pet = player->GetPet();
+ if(pet && pet->getPetType()==SUMMON_PET)
+ pet->InitStatsForLevel(1);
+
+ return true;
+}
+
+bool ChatHandler::HandleResetStatsCommand(const char * args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ if (pName)
+ {
+ std::string name = pName;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str());
+ player = objmgr.GetPlayer(guid);
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!HandleResetStatsOrLevelHelper(player))
+ return false;
+
+ player->InitStatsForLevel(true);
+ player->InitTaxiNodesForLevel();
+ player->InitTalentForLevel();
+
+ return true;
+}
+
+bool ChatHandler::HandleResetSpellsCommand(const char * args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ uint64 playerGUID = 0;
+ if (pName)
+ {
+ std::string name = pName;
+
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player = objmgr.GetPlayer(name.c_str());
+ if(!player)
+ playerGUID = objmgr.GetPlayerGUIDByName(name.c_str());
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player && !playerGUID)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(player)
+ {
+ player->resetSpells();
+
+ ChatHandler(player).SendSysMessage(LANG_RESET_SPELLS);
+
+ if(m_session->GetPlayer()!=player)
+ PSendSysMessage(LANG_RESET_SPELLS_ONLINE,player->GetName());
+ }
+ else
+ {
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_SPELLS), GUID_LOPART(playerGUID));
+ PSendSysMessage(LANG_RESET_SPELLS_OFFLINE,pName);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleResetTalentsCommand(const char * args)
+{
+ char* pName = strtok((char*)args, "");
+ Player *player = NULL;
+ uint64 playerGUID = 0;
+ if (pName)
+ {
+ std::string name = pName;
+ if(!normalizePlayerName(name))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ player = objmgr.GetPlayer(name.c_str());
+ if(!player)
+ playerGUID = objmgr.GetPlayerGUIDByName(name.c_str());
+ }
+ else
+ player = getSelectedPlayer();
+
+ if(!player && !playerGUID)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(player)
+ {
+ player->resetTalents(true);
+
+ ChatHandler(player).SendSysMessage(LANG_RESET_TALENTS);
+
+ if(m_session->GetPlayer()!=player)
+ PSendSysMessage(LANG_RESET_TALENTS_ONLINE,player->GetName());
+ }
+ else
+ {
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_TALENTS), GUID_LOPART(playerGUID) );
+ PSendSysMessage(LANG_RESET_TALENTS_OFFLINE,pName);
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleResetAllCommand(const char * args)
+{
+ if(!*args)
+ return false;
+
+ std::string casename = args;
+
+ AtLoginFlags atLogin;
+
+ // Command specially created as single command to prevent using short case names
+ if(casename=="spells")
+ {
+ atLogin = AT_LOGIN_RESET_SPELLS;
+ sWorld.SendWorldText(LANG_RESETALL_SPELLS);
+ }
+ else if(casename=="talents")
+ {
+ atLogin = AT_LOGIN_RESET_TALENTS;
+ sWorld.SendWorldText(LANG_RESETALL_TALENTS);
+ }
+ else
+ {
+ PSendSysMessage(LANG_RESETALL_UNKNOWN_CASE,args);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u'",atLogin);
+ HashMapHolder<Player>::MapType const& plist = ObjectAccessor::Instance().GetPlayers();
+ for(HashMapHolder<Player>::MapType::const_iterator itr = plist.begin(); itr != plist.end(); ++itr)
+ itr->second->SetAtLoginFlag(atLogin);
+
+ return true;
+}
+
+bool ChatHandler::HandleShutDownCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ if(std::string(args)=="cancel")
+ {
+ sWorld.ShutdownCancel();
+ }
+ else
+ {
+ int32 time = atoi(args);
+
+ ///- Prevent interpret wrong arg value as 0 secs shutdown time
+ if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
+ return false;
+
+ sWorld.ShutdownServ(time);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleRestartCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ if(std::string(args)=="cancel")
+ {
+ sWorld.ShutdownCancel();
+ }
+ else
+ {
+ int32 time = atoi(args);
+
+ ///- Prevent interpret wrong arg value as 0 secs shutdown time
+ if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
+ return false;
+
+ sWorld.ShutdownServ(time, SHUTDOWN_MASK_RESTART);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleIdleRestartCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ if(std::string(args)=="cancel")
+ {
+ sWorld.ShutdownCancel();
+ }
+ else
+ {
+ int32 time = atoi(args);
+
+ ///- Prevent interpret wrong arg value as 0 secs shutdown time
+ if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
+ return false;
+
+ sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART+SHUTDOWN_MASK_IDLE);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleIdleShutDownCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ if(std::string(args)=="cancel")
+ {
+ sWorld.ShutdownCancel();
+ }
+ else
+ {
+ int32 time = atoi(args);
+
+ ///- Prevent interpret wrong arg value as 0 secs shutdown time
+ if(time == 0 && (args[0]!='0' || args[1]!='\0') || time < 0)
+ return false;
+
+ sWorld.ShutdownServ(time,SHUTDOWN_MASK_IDLE);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleAddQuest(const char* args)
+{
+ Player* player = getSelectedPlayer();
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // .addquest #entry'
+ // number or [name] Shift-click form |color|Hquest:quest_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hquest");
+ if(!cId)
+ return false;
+
+ uint32 entry = atol(cId);
+
+ Quest const* pQuest = objmgr.GetQuestTemplate(entry);
+
+ if(!pQuest)
+ {
+ PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND,entry);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // check item starting quest (it can work incorrectly if added without item in inventory)
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry FROM item_template WHERE startquest = '%u' LIMIT 1",entry);
+ if(result)
+ {
+ Field* fields = result->Fetch();
+ uint32 item_id = fields[0].GetUInt32();
+ delete result;
+
+ PSendSysMessage(LANG_COMMAND_QUEST_STARTFROMITEM, entry,item_id);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // ok, normal (creature/GO starting) quest
+ if( player->CanAddQuest( pQuest, true ) )
+ {
+ player->AddQuest( pQuest, NULL );
+
+ if ( player->CanCompleteQuest( entry ) )
+ player->CompleteQuest( entry );
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleRemoveQuest(const char* args)
+{
+ Player* player = getSelectedPlayer();
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // .removequest #entry'
+ // number or [name] Shift-click form |color|Hquest:quest_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hquest");
+ if(!cId)
+ return false;
+
+ uint32 entry = atol(cId);
+
+ Quest const* pQuest = objmgr.GetQuestTemplate(entry);
+
+ if(!pQuest)
+ {
+ PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // remove all quest entries for 'entry' from quest log
+ for(uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot )
+ {
+ uint32 quest = player->GetQuestSlotQuestId(slot);
+ if(quest==entry)
+ {
+ player->SetQuestSlot(slot,0);
+
+ // we ignore unequippable quest items in this case, its' still be equipped
+ player->TakeQuestSourceItem( quest, false );
+ }
+ }
+
+ // set quest status to not started (will updated in DB at next save)
+ player->SetQuestStatus( entry, QUEST_STATUS_NONE);
+
+ // reset rewarded for restart repeatable quest
+ player->getQuestStatusMap()[entry].m_rewarded = false;
+
+ SendSysMessage(LANG_COMMAND_QUEST_REMOVED);
+ return true;
+}
+
+bool ChatHandler::HandleCompleteQuest(const char* args)
+{
+ Player* player = getSelectedPlayer();
+ if(!player)
+ {
+ SendSysMessage(LANG_NO_CHAR_SELECTED);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // .quest complete #entry
+ // number or [name] Shift-click form |color|Hquest:quest_id|h[name]|h|r
+ char* cId = extractKeyFromLink((char*)args,"Hquest");
+ if(!cId)
+ return false;
+
+ uint32 entry = atol(cId);
+
+ Quest const* pQuest = objmgr.GetQuestTemplate(entry);
+
+ // If player doesn't have the quest
+ if(!pQuest || player->GetQuestStatus(entry) == QUEST_STATUS_NONE)
+ {
+ PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // Add quest items for quests that require items
+ for(uint8 x = 0; x < QUEST_OBJECTIVES_COUNT; ++x)
+ {
+ uint32 id = pQuest->ReqItemId[x];
+ uint32 count = pQuest->ReqItemCount[x];
+ if(!id || !count)
+ continue;
+
+ uint32 curItemCount = player->GetItemCount(id,true);
+
+ ItemPosCountVec dest;
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, id, count-curItemCount );
+ if( msg == EQUIP_ERR_OK )
+ {
+ Item* item = player->StoreNewItem( dest, id, true);
+ player->SendNewItem(item,count-curItemCount,true,false);
+ }
+ }
+
+ // All creature/GO slain/casted (not required, but otherwise it will display "Creature slain 0/10")
+ for(uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ uint32 creature = pQuest->ReqCreatureOrGOId[i];
+ uint32 creaturecount = pQuest->ReqCreatureOrGOCount[i];
+
+ if(uint32 spell_id = pQuest->ReqSpell[i])
+ {
+ for(uint16 z = 0; z < creaturecount; ++z)
+ player->CastedCreatureOrGO(creature,0,spell_id);
+ }
+ else if(creature > 0)
+ {
+ for(uint16 z = 0; z < creaturecount; ++z)
+ player->KilledMonster(creature,0);
+ }
+ else if(creature < 0)
+ {
+ for(uint16 z = 0; z < creaturecount; ++z)
+ player->CastedCreatureOrGO(creature,0,0);
+ }
+ }
+
+ // If the quest requires reputation to complete
+ if(uint32 repFaction = pQuest->GetRepObjectiveFaction())
+ {
+ uint32 repValue = pQuest->GetRepObjectiveValue();
+ uint32 curRep = player->GetReputation(repFaction);
+ if(curRep < repValue)
+ {
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(repFaction);
+ player->SetFactionReputation(factionEntry,repValue);
+ }
+ }
+
+ // If the quest requires money
+ int32 ReqOrRewMoney = pQuest->GetRewOrReqMoney();
+ if(ReqOrRewMoney < 0)
+ player->ModifyMoney(-ReqOrRewMoney);
+
+ player->CompleteQuest(entry);
+ return true;
+}
+
+bool ChatHandler::HandleBanCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ char* type = strtok((char*)args, " ");
+
+ if(!type)
+ return false;
+ char* nameOrIP = strtok(NULL, " ");
+
+ if(!nameOrIP)
+ return false;
+
+ char* duration = strtok(NULL," ");
+ if(!duration || !atoi(duration))
+ return false;
+
+ char* reason = strtok(NULL,"");
+ if(!reason)
+ return false;
+
+ switch(sWorld.BanAccount(type, nameOrIP, duration, reason,m_session->GetPlayerName()))
+ {
+ case BAN_SUCCESS:
+ if(atoi(duration)>0)
+ PSendSysMessage(LANG_BAN_YOUBANNED,nameOrIP,secsToTimeString(TimeStringToSecs(duration),true).c_str(),reason);
+ else
+ PSendSysMessage(LANG_BAN_YOUPERMBANNED,nameOrIP,reason);
+ break;
+ case BAN_SYNTAX_ERROR:
+ return false;
+ case BAN_NOTFOUND:
+ PSendSysMessage(LANG_BAN_NOTFOUND,type,nameOrIP);
+ break;
+ }
+
+ return true;
+}
+
+bool ChatHandler::HandleUnBanCommand(const char* args)
+{
+ if(!args)
+ return false;
+ char* type = strtok((char*)args, " ");
+ if(!type)
+ return false;
+ char* nameOrIP = strtok(NULL, " ");
+
+ if(!nameOrIP)
+ return false;
+
+ if(sWorld.RemoveBanAccount(type,nameOrIP))
+ PSendSysMessage(LANG_UNBAN_UNBANNED,nameOrIP);
+ else
+ PSendSysMessage(LANG_UNBAN_ERROR,nameOrIP);
+
+ return true;
+}
+
+bool ChatHandler::HandleBanInfoCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ char* cType = strtok((char*)args, " ");
+ char* cnameOrIP = strtok(NULL, "");
+ if(!cType || !cnameOrIP)
+ return false;
+
+ std::string nameOrIP = cnameOrIP;
+ std::string type = cType;
+ if (!IsIPAddress(cnameOrIP) && type=="ip")
+ return false;
+
+ Field *fields;
+ if(type != "ip")
+ {
+ //look the accountid up
+ uint32 accountid;
+ std::string accountname;
+ if(type == "account")
+ {
+ loginDatabase.escape_string(nameOrIP);
+ QueryResult *result = loginDatabase.PQuery("SELECT id, username FROM account WHERE username = '%s'",nameOrIP.c_str());
+ if (!result)
+ {
+ PSendSysMessage(LANG_BANINFO_NOACCOUNT);
+ return true;
+ }
+ fields = result->Fetch();
+ accountid = fields[0].GetUInt32();
+ accountname = fields[1].GetCppString();
+ delete result;
+ }
+ else if(type == "character")
+ {
+ if(!normalizePlayerName(nameOrIP))
+ {
+ SendSysMessage(LANG_PLAYER_NOT_FOUND);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ loginDatabase.escape_string(nameOrIP);
+ QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'", nameOrIP.c_str());
+ if (!result)
+ {
+ PSendSysMessage(LANG_BANINFO_NOCHARACTER);
+ return true;
+ }
+ fields = result->Fetch();
+ accountid = fields[0].GetUInt32();
+ delete result;
+ result = loginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", accountid);
+ if (!result)
+ {
+ PSendSysMessage(LANG_BANINFO_NOCHARACTER);
+ return true;
+ }
+ fields = result->Fetch();
+ accountname = fields[0].GetCppString();
+ delete result;
+ }
+ else
+ return false;
+
+ QueryResult *result = loginDatabase.PQuery("SELECT FROM_UNIXTIME(bandate), unbandate-bandate, active, unbandate,banreason,bannedby FROM account_banned WHERE id = '%u' ORDER BY bandate ASC",accountid);
+ if(!result)
+ {
+ PSendSysMessage(LANG_BANINFO_NOACCOUNTBAN, accountname.c_str());
+ return true;
+ }
+
+ PSendSysMessage(LANG_BANINFO_BANHISTORY,accountname.c_str());
+ do
+ {
+ fields = result->Fetch();
+
+ time_t unbandate = time_t(fields[3].GetUInt64());
+ bool active = false;
+ if(fields[2].GetBool() && (fields[1].GetUInt64() == (uint64)0 ||unbandate >= time(NULL)) )
+ active = true;
+ bool permanent = (fields[1].GetUInt64() == (uint64)0);
+ std::string bantime = permanent?GetMangosString(LANG_BANINFO_INFINITE):secsToTimeString(fields[1].GetUInt64(), true);
+ PSendSysMessage(LANG_BANINFO_HISTORYENTRY,
+ fields[0].GetString(), bantime.c_str(), active ? GetMangosString(LANG_BANINFO_YES):GetMangosString(LANG_BANINFO_NO), fields[4].GetString(), fields[5].GetString());
+ }while (result->NextRow());
+
+ delete result;
+ }
+ else
+ {
+ loginDatabase.escape_string(nameOrIP);
+ QueryResult *result = loginDatabase.PQuery("SELECT ip, FROM_UNIXTIME(bandate), FROM_UNIXTIME(unbandate), unbandate-UNIX_TIMESTAMP(), banreason,bannedby,unbandate-bandate FROM ip_banned WHERE ip = '%s'",nameOrIP.c_str());
+ if(!result)
+ {
+ PSendSysMessage(LANG_BANINFO_NOIP);
+ return true;
+ }
+ fields = result->Fetch();
+ bool permanent = (fields[6].GetUInt64()==(uint64)0);
+ PSendSysMessage(LANG_BANINFO_IPENTRY,
+ fields[0].GetString(), fields[1].GetString(), permanent ? GetMangosString(LANG_BANINFO_NEVER):fields[2].GetString(),
+ permanent ? GetMangosString(LANG_BANINFO_INFINITE):secsToTimeString(fields[3].GetUInt64(), true).c_str(), fields[4].GetString(), fields[5].GetString());
+ delete result;
+ }
+ return true;
+}
+
+bool ChatHandler::HandleBanListCommand(const char* args)
+{
+ loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
+ if(!*args)
+ return false;
+ char* cType = strtok((char*)args, " ");
+ char* cFilter = strtok(NULL, "");
+ if(!cType || !cFilter)
+ return false;
+ std::string Filter = cFilter;
+ std::string Type = cType;
+ loginDatabase.escape_string(Filter);
+
+ QueryResult* result = NULL;
+ Field *fields = NULL;
+ if(Type == "ip")
+ {
+ result = loginDatabase.PQuery("SELECT ip FROM ip_banned WHERE ip "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'"),Filter.c_str());
+ if(!result)
+ {
+ PSendSysMessage(LANG_BANLIST_NOIP);
+ return true;
+ }
+ PSendSysMessage(LANG_BANLIST_MATCHINGIP);
+ do
+ {
+ fields = result->Fetch();
+ PSendSysMessage("%s",fields[0].GetString());
+ } while (result->NextRow());
+
+ delete result;
+ return true;
+ }
+ //lookup accountid
+ if(Type == "account")
+ {
+ result = loginDatabase.PQuery("SELECT id FROM account WHERE username "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'"),Filter.c_str());
+ if (!result)
+ {
+ PSendSysMessage(LANG_BANLIST_NOACCOUNT);
+ return true;
+ }
+ //do not delete result
+ }
+ else if(Type == "characters")
+ {
+ result = CharacterDatabase.PQuery("SELECT account FROM characters, WHERE name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'"),Filter.c_str());
+ if (!result)
+ {
+ PSendSysMessage(LANG_BANLIST_NOCHARACTER);
+ return true;
+ }
+ }
+ else
+ return false;
+
+ PSendSysMessage(LANG_BANLIST_MATCHINGACCOUNT);
+ do
+ {
+ fields = result->Fetch();
+ uint32 accountid = fields[0].GetUInt32();
+ QueryResult* banresult = loginDatabase.PQuery("SELECT account.username FROM account,account_banned WHERE account_banned.id='%u' AND account_banned.active = '1' AND account_banned.id=account.id",accountid);
+ if(banresult)
+ {
+ Field* fields2 = banresult->Fetch();
+ PSendSysMessage("%s",fields2[0].GetString());
+ delete banresult;
+ }
+ } while (result->NextRow());
+
+ delete result;
+ return true;
+}
+
+bool ChatHandler::HandleRespawnCommand(const char* /*args*/)
+{
+ Player* pl = m_session->GetPlayer();
+
+ CellPair p(MaNGOS::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::RespawnDo u_do;
+ MaNGOS::WorldObjectWorker<MaNGOS::RespawnDo> worker(u_do);
+
+ TypeContainerVisitor<MaNGOS::WorldObjectWorker<MaNGOS::RespawnDo>, GridTypeMapContainer > obj_worker(worker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, obj_worker, *MapManager::Instance().GetMap(pl->GetMapId(), pl));
+
+ return true;
+}
+
+bool ChatHandler::HandleFlyModeCommand(const char* args)
+{
+ if(!args)
+ return false;
+
+ Unit *unit = getSelectedUnit();
+ if (!unit || (unit->GetTypeId() != TYPEID_PLAYER))
+ unit = m_session->GetPlayer();
+
+ WorldPacket data(12);
+ if (strncmp(args, "on", 3) == 0)
+ data.SetOpcode(SMSG_MOVE_SET_CAN_FLY);
+ else if (strncmp(args, "off", 4) == 0)
+ data.SetOpcode(SMSG_MOVE_UNSET_CAN_FLY);
+ else
+ {
+ SendSysMessage(LANG_USE_BOL);
+ return false;
+ }
+ data.append(unit->GetPackGUID());
+ data << uint32(0); // unknown
+ unit->SendMessageToSet(&data, true);
+ PSendSysMessage(LANG_COMMAND_FLYMODE_STATUS, unit->GetName(), args);
+ return true;
+}
+
+bool ChatHandler::HandleLoadPDumpCommand(const char *args)
+{
+ if(!args)
+ return false;
+
+ char * file = strtok((char*)args, " "); if(!file) return false;
+ char * acc = strtok(NULL, " "); if(!acc) return false;
+ if(!file || !acc)
+ return false;
+
+ uint32 account_id = objmgr.GetAccountByAccountName(acc);
+ if(!account_id)
+ {
+ account_id = atoi(acc);
+ if(account_id)
+ {
+ std::string acc_name;
+ if(!objmgr.GetAccountNameByAccount(account_id,acc_name))
+ return false;
+ }
+ else
+ return false;
+ }
+
+ char * name = strtok(NULL, " ");
+ char * guid_str = name ? strtok(NULL, " ") : NULL;
+
+ uint32 guid = guid_str ? atoi(guid_str) : 0;
+
+ if(PlayerDumpReader().LoadDump(file, account_id, name ? name : "", guid))
+ PSendSysMessage(LANG_COMMAND_IMPORT_SUCCESS);
+ else
+ PSendSysMessage(LANG_COMMAND_IMPORT_FAILED);
+
+ return true;
+}
+
+bool ChatHandler::HandleChangeEntryCommand(const char *args)
+{
+ if(!args)
+ return false;
+
+ uint32 newEntryNum = atoi(args);
+ if(!newEntryNum)
+ return false;
+
+ Unit* unit = getSelectedUnit();
+ if(!unit || unit->GetTypeId() != TYPEID_UNIT)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+ Creature* creature = (Creature*)unit;
+ if(creature->UpdateEntry(newEntryNum))
+ SendSysMessage(LANG_DONE);
+ else
+ SendSysMessage(LANG_ERROR);
+ return true;
+}
+
+bool ChatHandler::HandleWritePDumpCommand(const char *args)
+{
+ if(!args)
+ return false;
+
+ char* file = strtok((char*)args, " ");
+ char* p2 = strtok(NULL, " ");
+
+ if(!file || !p2)
+ return false;
+
+ uint32 guid = objmgr.GetPlayerGUIDByName(p2);
+ if(!guid)
+ guid = atoi(p2);
+
+ if (PlayerDumpWriter().WriteDump(file, guid))
+ PSendSysMessage(LANG_COMMAND_EXPORT_SUCCESS);
+ else
+ PSendSysMessage(LANG_COMMAND_EXPORT_FAILED);
+
+ return true;
+}
+
+bool ChatHandler::HandleMovegensCommand(const char* /*args*/)
+{
+ Unit* unit = getSelectedUnit();
+ if(!unit)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ PSendSysMessage(LANG_MOVEGENS_LIST,(unit->GetTypeId()==TYPEID_PLAYER ? "Player" : "Creature" ),unit->GetGUIDLow());
+
+ MotionMaster* mm = unit->GetMotionMaster();
+ for(MotionMaster::const_iterator itr = mm->begin(); itr != mm->end(); ++itr)
+ {
+ switch((*itr)->GetMovementGeneratorType())
+ {
+ case IDLE_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_IDLE); break;
+ case RANDOM_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_RANDOM); break;
+ case WAYPOINT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_WAYPOINT); break;
+ case ANIMAL_RANDOM_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_ANIMAL_RANDOM); break;
+ case CONFUSED_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_CONFUSED); break;
+ case TARGETED_MOTION_TYPE:
+ {
+ if(unit->GetTypeId()==TYPEID_PLAYER)
+ {
+ TargetedMovementGenerator<Player> const* mgen = static_cast<TargetedMovementGenerator<Player> const*>(*itr);
+ Unit* target = mgen->GetTarget();
+ if(target)
+ PSendSysMessage(LANG_MOVEGENS_TARGETED_PLAYER,target->GetName(),target->GetGUIDLow());
+ else
+ SendSysMessage(LANG_MOVEGENS_TARGETED_NULL);
+ }
+ else
+ {
+ TargetedMovementGenerator<Creature> const* mgen = static_cast<TargetedMovementGenerator<Creature> const*>(*itr);
+ Unit* target = mgen->GetTarget();
+ if(target)
+ PSendSysMessage(LANG_MOVEGENS_TARGETED_CREATURE,target->GetName(),target->GetGUIDLow());
+ else
+ SendSysMessage(LANG_MOVEGENS_TARGETED_NULL);
+ }
+ break;
+ }
+ case HOME_MOTION_TYPE:
+ if(unit->GetTypeId()==TYPEID_UNIT)
+ {
+ float x,y,z;
+ (*itr)->GetDestination(x,y,z);
+ PSendSysMessage(LANG_MOVEGENS_HOME_CREATURE,x,y,z);
+ }
+ else
+ SendSysMessage(LANG_MOVEGENS_HOME_PLAYER);
+ break;
+ case FLIGHT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_FLIGHT); break;
+ case POINT_MOTION_TYPE:
+ {
+ float x,y,z;
+ (*itr)->GetDestination(x,y,z);
+ PSendSysMessage(LANG_MOVEGENS_POINT,x,y,z);
+ break;
+ }
+ case FLEEING_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_FEAR); break;
+ case DISTRACT_MOTION_TYPE: SendSysMessage(LANG_MOVEGENS_DISTRACT); break;
+ default:
+ PSendSysMessage(LANG_MOVEGENS_UNKNOWN,(*itr)->GetMovementGeneratorType());
+ break;
+ }
+ }
+ return true;
+}
+
+bool ChatHandler::HandlePLimitCommand(const char *args)
+{
+ if(*args)
+ {
+ char* param = strtok((char*)args, " ");
+ if(!param)
+ return false;
+
+ int l = strlen(param);
+
+ if( strncmp(param,"player",l) == 0 )
+ sWorld.SetPlayerLimit(-SEC_PLAYER);
+ else if(strncmp(param,"moderator",l) == 0 )
+ sWorld.SetPlayerLimit(-SEC_MODERATOR);
+ else if(strncmp(param,"gamemaster",l) == 0 )
+ sWorld.SetPlayerLimit(-SEC_GAMEMASTER);
+ else if(strncmp(param,"administrator",l) == 0 )
+ sWorld.SetPlayerLimit(-SEC_ADMINISTRATOR);
+ else if(strncmp(param,"reset",l) == 0 )
+ sWorld.SetPlayerLimit( sConfig.GetIntDefault("PlayerLimit", DEFAULT_PLAYER_LIMIT) );
+ else
+ {
+ int val = atoi(param);
+ if(val < -SEC_ADMINISTRATOR) val = -SEC_ADMINISTRATOR;
+
+ sWorld.SetPlayerLimit(val);
+ }
+
+ // kick all low security level players
+ if(sWorld.GetPlayerAmountLimit() > SEC_PLAYER)
+ sWorld.KickAllLess(sWorld.GetPlayerSecurityLimit());
+ }
+
+ uint32 pLimit = sWorld.GetPlayerAmountLimit();
+ AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit();
+ char const* secName = "";
+ switch(allowedAccountType)
+ {
+ case SEC_PLAYER: secName = "Player"; break;
+ case SEC_MODERATOR: secName = "Moderator"; break;
+ case SEC_GAMEMASTER: secName = "Gamemaster"; break;
+ case SEC_ADMINISTRATOR: secName = "Administrator"; break;
+ default: secName = "<unknown>"; break;
+ }
+
+ PSendSysMessage("Player limits: amount %u, min. security level %s.",pLimit,secName);
+
+ return true;
+}
+
+bool ChatHandler::HandleCastCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Unit* target = getSelectedUnit();
+
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell)
+ return false;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo)
+ return false;
+
+ if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* trig_str = strtok(NULL, " ");
+ if(trig_str)
+ {
+ int l = strlen(trig_str);
+ if(strncmp(trig_str,"triggered",l) != 0 )
+ return false;
+ }
+
+ bool triggered = (trig_str != NULL);
+
+ m_session->GetPlayer()->CastSpell(target,spell,triggered);
+
+ return true;
+}
+
+bool ChatHandler::HandleCastBackCommand(const char* args)
+{
+ Creature* caster = getSelectedCreature();
+
+ if(!caster)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell || !sSpellStore.LookupEntry(spell))
+ return false;
+
+ char* trig_str = strtok(NULL, " ");
+ if(trig_str)
+ {
+ int l = strlen(trig_str);
+ if(strncmp(trig_str,"triggered",l) != 0 )
+ return false;
+ }
+
+ bool triggered = (trig_str != NULL);
+
+ // update orientation at server
+ caster->SetOrientation(caster->GetAngle(m_session->GetPlayer()));
+
+ // and client
+ WorldPacket data;
+ caster->BuildHeartBeatMsg(&data);
+ caster->SendMessageToSet(&data,true);
+
+ caster->CastSpell(m_session->GetPlayer(),spell,false);
+
+ return true;
+}
+
+bool ChatHandler::HandleCastDistCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell)
+ return false;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo)
+ return false;
+
+ if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char *distStr = strtok(NULL, " ");
+
+ float dist = 0;
+
+ if(distStr)
+ sscanf(distStr, "%f", &dist);
+
+ char* trig_str = strtok(NULL, " ");
+ if(trig_str)
+ {
+ int l = strlen(trig_str);
+ if(strncmp(trig_str,"triggered",l) != 0 )
+ return false;
+ }
+
+ bool triggered = (trig_str != NULL);
+
+ float x,y,z;
+ m_session->GetPlayer()->GetClosePoint(x,y,z,dist);
+
+ m_session->GetPlayer()->CastSpell(x,y,z,spell,triggered);
+ return true;
+}
+
+bool ChatHandler::HandleCastTargetCommand(const char* args)
+{
+ Creature* caster = getSelectedCreature();
+
+ if(!caster)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if(!caster->getVictim())
+ {
+ SendSysMessage(LANG_SELECTED_TARGET_NOT_HAVE_VICTIM);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell || !sSpellStore.LookupEntry(spell))
+ return false;
+
+ char* trig_str = strtok(NULL, " ");
+ if(trig_str)
+ {
+ int l = strlen(trig_str);
+ if(strncmp(trig_str,"triggered",l) != 0 )
+ return false;
+ }
+
+ bool triggered = (trig_str != NULL);
+
+ // update orientation at server
+ caster->SetOrientation(caster->GetAngle(m_session->GetPlayer()));
+
+ // and client
+ WorldPacket data;
+ caster->BuildHeartBeatMsg(&data);
+ caster->SendMessageToSet(&data,true);
+
+ caster->CastSpell(caster->getVictim(),spell,false);
+
+ return true;
+}
+
+/*
+ComeToMe command REQUIRED for 3rd party scripting library to have access to PointMovementGenerator
+Without this function 3rd party scripting library will get linking errors (unresolved external)
+when attempting to use the PointMovementGenerator
+*/
+bool ChatHandler::HandleComeToMeCommand(const char *args)
+{
+ Creature* caster = getSelectedCreature();
+
+ if(!caster)
+ {
+ SendSysMessage(LANG_SELECT_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ char* newFlagStr = strtok((char*)args, " ");
+
+ if(!newFlagStr)
+ return false;
+
+ uint32 newFlags = atoi(newFlagStr);
+
+ caster->SetUnitMovementFlags(newFlags);
+
+ Player* pl = m_session->GetPlayer();
+
+ caster->GetMotionMaster()->MovePoint(0, pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ());
+ return true;
+}
+
+bool ChatHandler::HandleCastSelfCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ Unit* target = getSelectedUnit();
+
+ if(!target)
+ {
+ SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
+ uint32 spell = extractSpellIdFromLink((char*)args);
+ if(!spell)
+ return false;
+
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
+ if(!spellInfo)
+ return false;
+
+ if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
+ {
+ PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ target->CastSpell(target,spell,false);
+
+ return true;
+}
+
+std::string GetTimeString(uint32 time)
+{
+ uint16 days = time / DAY, hours = (time % DAY) / HOUR, minute = (time % HOUR) / MINUTE;
+ std::ostringstream ss;
+ if(days) ss << days << "d ";
+ if(hours) ss << hours << "h ";
+ ss << minute << "m";
+ return ss.str();
+}
+
+bool ChatHandler::HandleInstanceListBindsCommand(const char* /*args*/)
+{
+ Player* player = getSelectedPlayer();
+ if (!player) player = m_session->GetPlayer();
+ uint32 counter = 0;
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ Player::BoundInstancesMap &binds = player->GetBoundInstances(i);
+ for(Player::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end(); ++itr)
+ {
+ InstanceSave *save = itr->second.save;
+ std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL));
+ PSendSysMessage("map: %d inst: %d perm: %s diff: %s canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty() == DIFFICULTY_NORMAL ? "normal" : "heroic", save->CanReset() ? "yes" : "no", timeleft.c_str());
+ counter++;
+ }
+ }
+ PSendSysMessage("player binds: %d", counter);
+ counter = 0;
+ Group *group = player->GetGroup();
+ if(group)
+ {
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ Group::BoundInstancesMap &binds = group->GetBoundInstances(i);
+ for(Group::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end(); ++itr)
+ {
+ InstanceSave *save = itr->second.save;
+ std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL));
+ PSendSysMessage("map: %d inst: %d perm: %s diff: %s canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty() == DIFFICULTY_NORMAL ? "normal" : "heroic", save->CanReset() ? "yes" : "no", timeleft.c_str());
+ counter++;
+ }
+ }
+ }
+ PSendSysMessage("group binds: %d", counter);
+
+ return true;
+}
+
+bool ChatHandler::HandleInstanceUnbindCommand(const char* args)
+{
+ if(!*args)
+ return false;
+
+ std::string cmd = args;
+ if(cmd == "all")
+ {
+ Player* player = getSelectedPlayer();
+ if (!player) player = m_session->GetPlayer();
+ uint32 counter = 0;
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ Player::BoundInstancesMap &binds = player->GetBoundInstances(i);
+ for(Player::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end();)
+ {
+ if(itr->first != player->GetMapId())
+ {
+ InstanceSave *save = itr->second.save;
+ std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL));
+ PSendSysMessage("unbinding map: %d inst: %d perm: %s diff: %s canReset: %s TTR: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty() == DIFFICULTY_NORMAL ? "normal" : "heroic", save->CanReset() ? "yes" : "no", timeleft.c_str());
+ player->UnbindInstance(itr, i);
+ counter++;
+ }
+ else
+ ++itr;
+ }
+ }
+ PSendSysMessage("instances unbound: %d", counter);
+ }
+ return true;
+}
+
+bool ChatHandler::HandleInstanceStatsCommand(const char* /*args*/)
+{
+ PSendSysMessage("instances loaded: %d", MapManager::Instance().GetNumInstances());
+ PSendSysMessage("players in instances: %d", MapManager::Instance().GetNumPlayersInInstances());
+ PSendSysMessage("instance saves: %d", sInstanceSaveManager.GetNumInstanceSaves());
+ PSendSysMessage("players bound: %d", sInstanceSaveManager.GetNumBoundPlayersTotal());
+ PSendSysMessage("groups bound: %d", sInstanceSaveManager.GetNumBoundGroupsTotal());
+ return true;
+}
+
+bool ChatHandler::HandleInstanceSaveDataCommand(const char * /*args*/)
+{
+ Player* pl = m_session->GetPlayer();
+
+ Map* map = pl->GetMap();
+ if (!map->IsDungeon())
+ {
+ PSendSysMessage("Map is not a dungeon.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ if (!((InstanceMap*)map)->GetInstanceData())
+ {
+ PSendSysMessage("Map has no instance data.");
+ SetSentErrorMessage(true);
+ return false;
+ }
+
+ ((InstanceMap*)map)->GetInstanceData()->SaveToDB();
+ return true;
+}
+
+bool ChatHandler::HandleFlushArenaPointsCommand(const char * /*args*/)
+{
+ sBattleGroundMgr.DistributeArenaPoints();
+ return true;
+}
diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp
index a618d83c15a..010af48e050 100644
--- a/src/game/MiscHandler.cpp
+++ b/src/game/MiscHandler.cpp
@@ -1,1766 +1,1766 @@
-/*
- * 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 or if its enabled in mangosd.conf
- if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || GetPlayer()->isInFlight() || sWorld.getConfig(CONFIG_INSTANT_LOGOUT))
- {
- 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...
-
- // AntiCheat.GMIsland
- if(sWorld.getConfig(CONFIG_KICK_FROM_GMISLAND))
- {
- if(newZone == 876 && GetPlayer()->GetSession()->GetSecurity() == SEC_PLAYER)
- _player->TeleportTo(13,0,0,0,0);
- }
-
- 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);
-}
+/*
+ * 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 or if its enabled in mangosd.conf
+ if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || GetPlayer()->isInFlight() || sWorld.getConfig(CONFIG_INSTANT_LOGOUT))
+ {
+ 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...
+
+ // AntiCheat.GMIsland
+ if(sWorld.getConfig(CONFIG_KICK_FROM_GMISLAND))
+ {
+ if(newZone == 876 && GetPlayer()->GetSession()->GetSecurity() == SEC_PLAYER)
+ _player->TeleportTo(13,0,0,0,0);
+ }
+
+ 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 2a5cc05e986..c2ca16ba85d 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_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);
-}
+/*
+ * 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/NPCHandler.cpp b/src/game/NPCHandler.cpp
index 5d3e9b61aa2..da56186868c 100644
--- a/src/game/NPCHandler.cpp
+++ b/src/game/NPCHandler.cpp
@@ -1,826 +1,826 @@
-/*
- * 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 "WorldSession.h"
-#include "Opcodes.h"
-#include "Log.h"
-#include "World.h"
-#include "ObjectMgr.h"
-#include "SpellMgr.h"
-#include "Player.h"
-#include "GossipDef.h"
-#include "SpellAuras.h"
-#include "UpdateMask.h"
-#include "ScriptCalls.h"
-#include "ObjectAccessor.h"
-#include "Creature.h"
-#include "MapManager.h"
-#include "Pet.h"
-#include "BattleGroundMgr.h"
-#include "BattleGround.h"
-#include "Guild.h"
-
-void WorldSession::HandleTabardVendorActivateOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- uint64 guid;
- recv_data >> guid;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_TABARDDESIGNER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleTabardVendorActivateOpcode - 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);
-
- SendTabardVendorActivate(guid);
-}
-
-void WorldSession::SendTabardVendorActivate( uint64 guid )
-{
- WorldPacket data( MSG_TABARDVENDOR_ACTIVATE, 8 );
- data << guid;
- SendPacket( &data );
-}
-
-void WorldSession::HandleBankerActivateOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- uint64 guid;
-
- sLog.outDebug( "WORLD: Received CMSG_BANKER_ACTIVATE" );
-
- recv_data >> guid;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_BANKER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleBankerActivateOpcode - 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);
-
- SendShowBank(guid);
-}
-
-void WorldSession::SendShowBank( uint64 guid )
-{
- WorldPacket data( SMSG_SHOW_BANK, 8 );
- data << guid;
- SendPacket( &data );
-}
-
-void WorldSession::HandleTrainerListOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- uint64 guid;
-
- recv_data >> guid;
- SendTrainerList( guid );
-}
-
-void WorldSession::SendTrainerList( uint64 guid )
-{
- std::string str = GetMangosString(LANG_NPC_TAINER_HELLO);
- SendTrainerList( guid, str );
-}
-
-void WorldSession::SendTrainerList( uint64 guid,std::string strTitle )
-{
- sLog.outDebug( "WORLD: SendTrainerList" );
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_TRAINER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: SendTrainerList - 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);
-
- // trainer list loaded at check;
- if(!unit->isCanTrainingOf(_player,true))
- return;
-
- CreatureInfo const *ci = unit->GetCreatureInfo();
-
- if (!ci)
- {
- sLog.outDebug( "WORLD: SendTrainerList - (%u) NO CREATUREINFO! (GUID: %u)", uint32(GUID_LOPART(guid)), guid );
- return;
- }
-
- TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
- if(!trainer_spells)
- {
- sLog.outDebug( "WORLD: SendTrainerList - Training spells not found for creature (GUID: %u Entry: %u)", guid, unit->GetEntry());
- return;
- }
-
- WorldPacket data( SMSG_TRAINER_LIST, 8+4+4+trainer_spells->spellList.size()*38 + strTitle.size()+1);
- data << guid;
- data << uint32(trainer_spells->trainerType);
-
- size_t count_pos = data.wpos();
- data << uint32(trainer_spells->spellList.size());
-
- // reputation discount
- float fDiscountMod = _player->GetReputationPriceDiscount(unit);
-
- uint32 count = 0;
- for(TrainerSpellList::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr)
- {
- TrainerSpell const* tSpell = *itr;
-
- if(!_player->IsSpellFitByClassAndRace(tSpell->spell))
- continue;
-
- ++count;
-
- bool primary_prof_first_rank = spellmgr.IsPrimaryProfessionFirstRankSpell(tSpell->spell);
-
- SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->spell);
-
- data << uint32(tSpell->spell);
- data << uint8(_player->GetTrainerSpellState(tSpell));
- data << uint32(floor(tSpell->spellcost * fDiscountMod));
-
- data << uint32(primary_prof_first_rank ? 1 : 0); // primary prof. learn confirmation dialog
- data << uint32(primary_prof_first_rank ? 1 : 0); // must be equal prev. field to have learn button in enabled state
- data << uint8(tSpell->reqlevel);
- data << uint32(tSpell->reqskill);
- data << uint32(tSpell->reqskillvalue);
- data << uint32(chain_node ? (chain_node->prev ? chain_node->prev : chain_node->req) : 0);
- data << uint32(chain_node && chain_node->prev ? chain_node->req : 0);
- data << uint32(0);
- }
-
- data << strTitle;
-
- data.put<uint32>(count_pos,count);
- SendPacket( &data );
-}
-
-void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8+4);
-
- uint64 guid;
- uint32 spellId = 0;
-
- recv_data >> guid >> spellId;
- sLog.outDebug( "WORLD: Received CMSG_TRAINER_BUY_SPELL NpcGUID=%u, learn spell id is: %u",uint32(GUID_LOPART(guid)), spellId );
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_TRAINER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleTrainerBuySpellOpcode - 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);
-
- if(!unit->isCanTrainingOf(_player,true))
- return;
-
- // check present spell in trainer spell list
- TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
- if(!trainer_spells)
- return;
-
- // not found, cheat?
- TrainerSpell const* trainer_spell = trainer_spells->Find(spellId);
- if(!trainer_spell)
- return;
-
- // can't be learn, cheat? Or double learn with lags...
- if(_player->GetTrainerSpellState(trainer_spell) != TRAINER_SPELL_GREEN)
- return;
-
- // apply reputation discount
- uint32 nSpellCost = uint32(floor(trainer_spell->spellcost * _player->GetReputationPriceDiscount(unit)));
-
- // check money requirement
- if(_player->GetMoney() < nSpellCost )
- return;
-
- WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12); // visual effect on trainer
- data << uint64(guid) << uint32(0xB3);
- SendPacket(&data);
-
- data.Initialize(SMSG_PLAY_SPELL_IMPACT, 12); // visual effect on player
- data << uint64(_player->GetGUID()) << uint32(0x016A);
- SendPacket(&data);
-
- _player->ModifyMoney( -int32(nSpellCost) );
-
- // learn explicitly to prevent lost money at lags, learning spell will be only show spell animation
- _player->learnSpell(trainer_spell->spell);
-
- data.Initialize(SMSG_TRAINER_BUY_SUCCEEDED, 12);
- data << uint64(guid) << uint32(spellId);
- SendPacket(&data);
-}
-
-void WorldSession::HandleGossipHelloOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- sLog.outDebug( "WORLD: Received CMSG_GOSSIP_HELLO" );
-
- uint64 guid;
- recv_data >> guid;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_NONE);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleGossipHelloOpcode - 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);
-
- if( unit->isArmorer() || unit->isCivilian() || unit->isQuestGiver() || unit->isServiceProvider())
- {
- unit->StopMoving();
- }
-
- // If spiritguide, no need for gossip menu, just put player into resurrect queue
- if (unit->isSpiritGuide())
- {
- BattleGround *bg = _player->GetBattleGround();
- if(bg)
- {
- bg->AddPlayerToResurrectQueue(unit->GetGUID(), _player->GetGUID());
- sBattleGroundMgr.SendAreaSpiritHealerQueryOpcode(_player, bg, unit->GetGUID());
- return;
- }
- }
-
- if(!Script->GossipHello( _player, unit ))
- {
- _player->TalkedToCreature(unit->GetEntry(),unit->GetGUID());
- unit->prepareGossipMenu(_player,0);
- unit->sendPreparedGossip( _player );
- }
-}
-
-void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8+4+4);
-
- sLog.outDebug("WORLD: CMSG_GOSSIP_SELECT_OPTION");
-
- uint32 option;
- uint32 unk;
- uint64 guid;
- std::string code = "";
-
- recv_data >> guid >> unk >> option;
-
- if(_player->PlayerTalkClass->GossipOptionCoded( option ))
- {
- // recheck
- CHECK_PACKET_SIZE(recv_data,8+4+1);
- sLog.outBasic("reading string");
- recv_data >> code;
- sLog.outBasic("string read: %s", code.c_str());
- }
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_NONE);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleGossipSelectOptionOpcode - 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);
-
- if(!code.empty())
- {
-
- if(!Script->GossipSelectWithCode( _player, unit, _player->PlayerTalkClass->GossipOptionSender( option ), _player->PlayerTalkClass->GossipOptionAction( option ), code.c_str()) )
- unit->OnGossipSelect( _player, option );
- }
- else
-
- if(!Script->GossipSelect( _player, unit, _player->PlayerTalkClass->GossipOptionSender( option ), _player->PlayerTalkClass->GossipOptionAction( option )) )
- unit->OnGossipSelect( _player, option );
-}
-
-void WorldSession::HandleSpiritHealerActivateOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- sLog.outDebug("WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
-
- uint64 guid;
-
- recv_data >> guid;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_SPIRITHEALER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleSpiritHealerActivateOpcode - 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);
-
- SendSpiritResurrect();
-}
-
-void WorldSession::SendSpiritResurrect()
-{
- _player->ResurrectPlayer(0.5f,false, true);
-
- _player->DurabilityLossAll(0.25f,true);
-
- // get corpse nearest graveyard
- WorldSafeLocsEntry const *corpseGrave = NULL;
- Corpse *corpse = _player->GetCorpse();
- if(corpse)
- corpseGrave = objmgr.GetClosestGraveYard(
- corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeam() );
-
- // now can spawn bones
- _player->SpawnCorpseBones();
-
- // teleport to nearest from corpse graveyard, if different from nearest to player ghost
- if(corpseGrave)
- {
- WorldSafeLocsEntry const *ghostGrave = objmgr.GetClosestGraveYard(
- _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetMapId(), _player->GetTeam() );
-
- if(corpseGrave != ghostGrave)
- _player->TeleportTo(corpseGrave->map_id, corpseGrave->x, corpseGrave->y, corpseGrave->z, _player->GetOrientation());
- // or update at original position
- else
- ObjectAccessor::UpdateVisibilityForPlayer(_player);
- }
- // or update at original position
- else
- ObjectAccessor::UpdateVisibilityForPlayer(_player);
-
- _player->SaveToDB();
-}
-
-void WorldSession::HandleBinderActivateOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- uint64 npcGUID;
- recv_data >> npcGUID;
-
- if(!GetPlayer()->isAlive())
- return;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID,UNIT_NPC_FLAG_INNKEEPER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleBinderActivateOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
- return;
- }
-
- // remove fake death
- if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
- GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- SendBindPoint(unit);
-}
-
-void WorldSession::SendBindPoint(Creature *npc)
-{
- uint32 bindspell = 3286;
-
- // update sql homebind
- CharacterDatabase.PExecute("UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'", _player->GetMapId(), _player->GetZoneId(), _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetGUIDLow());
- _player->m_homebindMapId = _player->GetMapId();
- _player->m_homebindZoneId = _player->GetZoneId();
- _player->m_homebindX = _player->GetPositionX();
- _player->m_homebindY = _player->GetPositionY();
- _player->m_homebindZ = _player->GetPositionZ();
-
- // send spell for bind 3286 bind magic
- npc->CastSpell(_player, bindspell, true);
-
- WorldPacket data( SMSG_TRAINER_BUY_SUCCEEDED, (8+4));
- data << npc->GetGUID();
- data << bindspell;
- SendPacket( &data );
-
- // binding
- data.Initialize( SMSG_BINDPOINTUPDATE, (4+4+4+4+4) );
- data << float(_player->GetPositionX());
- data << float(_player->GetPositionY());
- data << float(_player->GetPositionZ());
- data << uint32(_player->GetMapId());
- data << uint32(_player->GetZoneId());
- SendPacket( &data );
-
- DEBUG_LOG("New Home Position X is %f",_player->GetPositionX());
- DEBUG_LOG("New Home Position Y is %f",_player->GetPositionY());
- DEBUG_LOG("New Home Position Z is %f",_player->GetPositionZ());
- DEBUG_LOG("New Home MapId is %u",_player->GetMapId());
- DEBUG_LOG("New Home ZoneId is %u",_player->GetZoneId());
-
- // zone update
- data.Initialize( SMSG_PLAYERBOUND, 8+4 );
- data << uint64(_player->GetGUID());
- data << uint32(_player->GetZoneId());
- SendPacket( &data );
-
- _player->PlayerTalkClass->CloseGossip();
-}
-
-//Need fix
-void WorldSession::HandleListStabledPetsOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- sLog.outDebug("WORLD: Recv MSG_LIST_STABLED_PETS");
- uint64 npcGUID;
-
- recv_data >> npcGUID;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleListStabledPetsOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
- return;
- }
-
- // remove fake death
- if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
- GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- SendStablePet(npcGUID);
-}
-
-void WorldSession::SendStablePet(uint64 guid )
-{
- sLog.outDebug("WORLD: Recv MSG_LIST_STABLED_PETS Send.");
-
- WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
- data << uint64 ( guid );
-
- Pet *pet = _player->GetPet();
-
- data << uint8(0); // place holder for slot show number
- data << uint8(GetPlayer()->m_stableSlots);
-
- uint8 num = 0; // counter for place holder
-
- // not let move dead pet in slot
- if(pet && pet->isAlive() && pet->getPetType()==HUNTER_PET)
- {
- data << uint32(pet->GetCharmInfo()->GetPetNumber());
- data << uint32(pet->GetEntry());
- data << uint32(pet->getLevel());
- data << pet->GetName(); // petname
- data << uint32(pet->GetLoyaltyLevel()); // loyalty
- data << uint8(0x01); // client slot 1 == current pet (0)
- ++num;
- }
-
- // 0 1 2 3 4 5 6
- QueryResult* result = CharacterDatabase.PQuery("SELECT owner, slot, id, entry, level, loyalty, name FROM character_pet WHERE owner = '%u' AND slot > 0 AND slot < 3",_player->GetGUIDLow());
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
-
- data << uint32(fields[2].GetUInt32()); // petnumber
- data << uint32(fields[3].GetUInt32()); // creature entry
- data << uint32(fields[4].GetUInt32()); // level
- data << fields[6].GetString(); // name
- data << uint32(fields[5].GetUInt32()); // loyalty
- data << uint8(fields[1].GetUInt32()+1); // slot
-
- ++num;
- }while( result->NextRow() );
-
- delete result;
- }
-
- data.put<uint8>(8, num); // set real data to placeholder
- SendPacket(&data);
-}
-
-void WorldSession::HandleStablePet( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- sLog.outDebug("WORLD: Recv CMSG_STABLE_PET not dispose.");
- uint64 npcGUID;
-
- recv_data >> npcGUID;
-
- if(!GetPlayer()->isAlive())
- return;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleStablePet - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
- return;
- }
-
- // remove fake death
- if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
- GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- Pet *pet = _player->GetPet();
-
- WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
-
- // can't place in stable dead pet
- if(!pet||!pet->isAlive()||pet->getPetType()!=HUNTER_PET)
- {
- data << uint8(0x06);
- SendPacket(&data);
- return;
- }
-
- uint32 free_slot = 1;
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT owner,slot,id FROM character_pet WHERE owner = '%u' AND slot > 0 AND slot < 3 ORDER BY slot ",_player->GetGUIDLow());
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
-
- uint32 slot = fields[1].GetUInt32();
-
- if(slot==free_slot) // this slot not free
- ++free_slot;
- }while( result->NextRow() );
- }
- delete result;
-
- if( free_slot > 0 && free_slot <= GetPlayer()->m_stableSlots)
- {
- _player->RemovePet(pet,PetSaveMode(free_slot));
- data << uint8(0x08);
- }
- else
- data << uint8(0x06);
-
- SendPacket(&data);
-}
-
-void WorldSession::HandleUnstablePet( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8+4);
-
- sLog.outDebug("WORLD: Recv CMSG_UNSTABLE_PET.");
- uint64 npcGUID;
- uint32 petnumber;
-
- recv_data >> npcGUID >> petnumber;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleUnstablePet - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
- return;
- }
-
- // remove fake death
- if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
- GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
-
- Pet* pet = _player->GetPet();
- if(pet && pet->isAlive())
- {
- uint8 i = 0x06;
- data << uint8(i);
- SendPacket(&data);
- return;
- }
-
- // delete dead pet
- if(pet)
- _player->RemovePet(pet,PET_SAVE_AS_DELETED);
-
- Pet *newpet = NULL;
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT entry FROM character_pet WHERE owner = '%u' AND id = '%u' AND slot > 0 AND slot < 3",_player->GetGUIDLow(),petnumber);
- if(result)
- {
- Field *fields = result->Fetch();
- uint32 petentry = fields[0].GetUInt32();
-
- newpet = new Pet(HUNTER_PET);
- if(!newpet->LoadPetFromDB(_player,petentry,petnumber))
- {
- delete newpet;
- newpet = NULL;
- }
- delete result;
- }
-
- if(newpet)
- data << uint8(0x09);
- else
- data << uint8(0x06);
- SendPacket(&data);
-}
-
-void WorldSession::HandleBuyStableSlot( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8);
-
- sLog.outDebug("WORLD: Recv CMSG_BUY_STABLE_SLOT.");
- uint64 npcGUID;
-
- recv_data >> npcGUID;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleBuyStableSlot - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
- return;
- }
-
- // remove fake death
- if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
- GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- WorldPacket data(SMSG_STABLE_RESULT, 200);
-
- if(GetPlayer()->m_stableSlots < 2) // max slots amount = 2
- {
- StableSlotPricesEntry const *SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
- if(_player->GetMoney() >= SlotPrice->Price)
- {
- ++GetPlayer()->m_stableSlots;
- _player->ModifyMoney(-int32(SlotPrice->Price));
- data << uint8(0x0A); // success buy
- }
- else
- data << uint8(0x06);
- }
- else
- data << uint8(0x06);
-
- SendPacket(&data);
-}
-
-void WorldSession::HandleStableRevivePet( WorldPacket &/* recv_data */)
-{
- sLog.outDebug("HandleStableRevivePet: Not implemented");
-}
-
-void WorldSession::HandleStableSwapPet( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8+4);
-
- sLog.outDebug("WORLD: Recv CMSG_STABLE_SWAP_PET.");
- uint64 npcGUID;
- uint32 pet_number;
-
- recv_data >> npcGUID >> pet_number;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleStableSwapPet - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
- return;
- }
-
- // remove fake death
- if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
- GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
-
- Pet* pet = _player->GetPet();
-
- if(!pet || pet->getPetType()!=HUNTER_PET)
- return;
-
- // find swapped pet slot in stable
- QueryResult *result = CharacterDatabase.PQuery("SELECT slot,entry FROM character_pet WHERE owner = '%u' AND id = '%u'",_player->GetGUIDLow(),pet_number);
- if(!result)
- return;
-
- Field *fields = result->Fetch();
-
- uint32 slot = fields[0].GetUInt32();
- uint32 petentry = fields[1].GetUInt32();
- delete result;
-
- // move alive pet to slot or delele dead pet
- _player->RemovePet(pet,pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
-
- // summon unstabled pet
- Pet *newpet = new Pet;
- if(!newpet->LoadPetFromDB(_player,petentry,pet_number))
- {
- delete newpet;
- data << uint8(0x06);
- }
- else
- data << uint8(0x09);
-
- SendPacket(&data);
-}
-
-void WorldSession::HandleRepairItemOpcode( WorldPacket & recv_data )
-{
- CHECK_PACKET_SIZE(recv_data,8+8+1);
-
- sLog.outDebug("WORLD: CMSG_REPAIR_ITEM");
-
- uint64 npcGUID, itemGUID;
- uint8 guildBank; // new in 2.3.2, bool that means from guild bank money
-
- recv_data >> npcGUID >> itemGUID >> guildBank;
-
- Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_REPAIR);
- if (!unit)
- {
- sLog.outDebug( "WORLD: HandleRepairItemOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
- return;
- }
-
- // remove fake death
- if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
- GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- // reputation discount
- float discountMod = _player->GetReputationPriceDiscount(unit);
-
- uint32 TotalCost = 0;
- if (itemGUID)
- {
- sLog.outDebug("ITEM: Repair item, itemGUID = %u, npcGUID = %u", GUID_LOPART(itemGUID), GUID_LOPART(npcGUID));
-
- Item* item = _player->GetItemByGuid(itemGUID);
-
- if(item)
- TotalCost= _player->DurabilityRepair(item->GetPos(),true,discountMod,guildBank>0?true:false);
- }
- else
- {
- sLog.outDebug("ITEM: Repair all items, npcGUID = %u", GUID_LOPART(npcGUID));
-
- TotalCost = _player->DurabilityRepairAll(true,discountMod,guildBank>0?true:false);
- }
- if (guildBank)
- {
- uint32 GuildId = _player->GetGuildId();
- if (!GuildId)
- return;
- Guild *pGuild = objmgr.GetGuildById(GuildId);
- if (!pGuild)
- return;
- pGuild->LogBankEvent(GUILD_BANK_LOG_REPAIR_MONEY, 0, _player->GetGUIDLow(), TotalCost);
- pGuild->SendMoneyInfo(this, _player->GetGUIDLow());
- }
-}
+/*
+ * 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 "WorldSession.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Player.h"
+#include "GossipDef.h"
+#include "SpellAuras.h"
+#include "UpdateMask.h"
+#include "ScriptCalls.h"
+#include "ObjectAccessor.h"
+#include "Creature.h"
+#include "MapManager.h"
+#include "Pet.h"
+#include "BattleGroundMgr.h"
+#include "BattleGround.h"
+#include "Guild.h"
+
+void WorldSession::HandleTabardVendorActivateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_TABARDDESIGNER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleTabardVendorActivateOpcode - 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);
+
+ SendTabardVendorActivate(guid);
+}
+
+void WorldSession::SendTabardVendorActivate( uint64 guid )
+{
+ WorldPacket data( MSG_TABARDVENDOR_ACTIVATE, 8 );
+ data << guid;
+ SendPacket( &data );
+}
+
+void WorldSession::HandleBankerActivateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+
+ sLog.outDebug( "WORLD: Received CMSG_BANKER_ACTIVATE" );
+
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_BANKER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleBankerActivateOpcode - 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);
+
+ SendShowBank(guid);
+}
+
+void WorldSession::SendShowBank( uint64 guid )
+{
+ WorldPacket data( SMSG_SHOW_BANK, 8 );
+ data << guid;
+ SendPacket( &data );
+}
+
+void WorldSession::HandleTrainerListOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+
+ recv_data >> guid;
+ SendTrainerList( guid );
+}
+
+void WorldSession::SendTrainerList( uint64 guid )
+{
+ std::string str = GetMangosString(LANG_NPC_TAINER_HELLO);
+ SendTrainerList( guid, str );
+}
+
+void WorldSession::SendTrainerList( uint64 guid,std::string strTitle )
+{
+ sLog.outDebug( "WORLD: SendTrainerList" );
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_TRAINER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: SendTrainerList - 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);
+
+ // trainer list loaded at check;
+ if(!unit->isCanTrainingOf(_player,true))
+ return;
+
+ CreatureInfo const *ci = unit->GetCreatureInfo();
+
+ if (!ci)
+ {
+ sLog.outDebug( "WORLD: SendTrainerList - (%u) NO CREATUREINFO! (GUID: %u)", uint32(GUID_LOPART(guid)), guid );
+ return;
+ }
+
+ TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
+ if(!trainer_spells)
+ {
+ sLog.outDebug( "WORLD: SendTrainerList - Training spells not found for creature (GUID: %u Entry: %u)", guid, unit->GetEntry());
+ return;
+ }
+
+ WorldPacket data( SMSG_TRAINER_LIST, 8+4+4+trainer_spells->spellList.size()*38 + strTitle.size()+1);
+ data << guid;
+ data << uint32(trainer_spells->trainerType);
+
+ size_t count_pos = data.wpos();
+ data << uint32(trainer_spells->spellList.size());
+
+ // reputation discount
+ float fDiscountMod = _player->GetReputationPriceDiscount(unit);
+
+ uint32 count = 0;
+ for(TrainerSpellList::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr)
+ {
+ TrainerSpell const* tSpell = *itr;
+
+ if(!_player->IsSpellFitByClassAndRace(tSpell->spell))
+ continue;
+
+ ++count;
+
+ bool primary_prof_first_rank = spellmgr.IsPrimaryProfessionFirstRankSpell(tSpell->spell);
+
+ SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->spell);
+
+ data << uint32(tSpell->spell);
+ data << uint8(_player->GetTrainerSpellState(tSpell));
+ data << uint32(floor(tSpell->spellcost * fDiscountMod));
+
+ data << uint32(primary_prof_first_rank ? 1 : 0); // primary prof. learn confirmation dialog
+ data << uint32(primary_prof_first_rank ? 1 : 0); // must be equal prev. field to have learn button in enabled state
+ data << uint8(tSpell->reqlevel);
+ data << uint32(tSpell->reqskill);
+ data << uint32(tSpell->reqskillvalue);
+ data << uint32(chain_node ? (chain_node->prev ? chain_node->prev : chain_node->req) : 0);
+ data << uint32(chain_node && chain_node->prev ? chain_node->req : 0);
+ data << uint32(0);
+ }
+
+ data << strTitle;
+
+ data.put<uint32>(count_pos,count);
+ SendPacket( &data );
+}
+
+void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ uint64 guid;
+ uint32 spellId = 0;
+
+ recv_data >> guid >> spellId;
+ sLog.outDebug( "WORLD: Received CMSG_TRAINER_BUY_SPELL NpcGUID=%u, learn spell id is: %u",uint32(GUID_LOPART(guid)), spellId );
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_TRAINER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleTrainerBuySpellOpcode - 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);
+
+ if(!unit->isCanTrainingOf(_player,true))
+ return;
+
+ // check present spell in trainer spell list
+ TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
+ if(!trainer_spells)
+ return;
+
+ // not found, cheat?
+ TrainerSpell const* trainer_spell = trainer_spells->Find(spellId);
+ if(!trainer_spell)
+ return;
+
+ // can't be learn, cheat? Or double learn with lags...
+ if(_player->GetTrainerSpellState(trainer_spell) != TRAINER_SPELL_GREEN)
+ return;
+
+ // apply reputation discount
+ uint32 nSpellCost = uint32(floor(trainer_spell->spellcost * _player->GetReputationPriceDiscount(unit)));
+
+ // check money requirement
+ if(_player->GetMoney() < nSpellCost )
+ return;
+
+ WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12); // visual effect on trainer
+ data << uint64(guid) << uint32(0xB3);
+ SendPacket(&data);
+
+ data.Initialize(SMSG_PLAY_SPELL_IMPACT, 12); // visual effect on player
+ data << uint64(_player->GetGUID()) << uint32(0x016A);
+ SendPacket(&data);
+
+ _player->ModifyMoney( -int32(nSpellCost) );
+
+ // learn explicitly to prevent lost money at lags, learning spell will be only show spell animation
+ _player->learnSpell(trainer_spell->spell);
+
+ data.Initialize(SMSG_TRAINER_BUY_SUCCEEDED, 12);
+ data << uint64(guid) << uint32(spellId);
+ SendPacket(&data);
+}
+
+void WorldSession::HandleGossipHelloOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug( "WORLD: Received CMSG_GOSSIP_HELLO" );
+
+ uint64 guid;
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_NONE);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleGossipHelloOpcode - 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);
+
+ if( unit->isArmorer() || unit->isCivilian() || unit->isQuestGiver() || unit->isServiceProvider())
+ {
+ unit->StopMoving();
+ }
+
+ // If spiritguide, no need for gossip menu, just put player into resurrect queue
+ if (unit->isSpiritGuide())
+ {
+ BattleGround *bg = _player->GetBattleGround();
+ if(bg)
+ {
+ bg->AddPlayerToResurrectQueue(unit->GetGUID(), _player->GetGUID());
+ sBattleGroundMgr.SendAreaSpiritHealerQueryOpcode(_player, bg, unit->GetGUID());
+ return;
+ }
+ }
+
+ if(!Script->GossipHello( _player, unit ))
+ {
+ _player->TalkedToCreature(unit->GetEntry(),unit->GetGUID());
+ unit->prepareGossipMenu(_player,0);
+ unit->sendPreparedGossip( _player );
+ }
+}
+
+void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+4);
+
+ sLog.outDebug("WORLD: CMSG_GOSSIP_SELECT_OPTION");
+
+ uint32 option;
+ uint32 unk;
+ uint64 guid;
+ std::string code = "";
+
+ recv_data >> guid >> unk >> option;
+
+ if(_player->PlayerTalkClass->GossipOptionCoded( option ))
+ {
+ // recheck
+ CHECK_PACKET_SIZE(recv_data,8+4+1);
+ sLog.outBasic("reading string");
+ recv_data >> code;
+ sLog.outBasic("string read: %s", code.c_str());
+ }
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_NONE);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleGossipSelectOptionOpcode - 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);
+
+ if(!code.empty())
+ {
+
+ if(!Script->GossipSelectWithCode( _player, unit, _player->PlayerTalkClass->GossipOptionSender( option ), _player->PlayerTalkClass->GossipOptionAction( option ), code.c_str()) )
+ unit->OnGossipSelect( _player, option );
+ }
+ else
+
+ if(!Script->GossipSelect( _player, unit, _player->PlayerTalkClass->GossipOptionSender( option ), _player->PlayerTalkClass->GossipOptionAction( option )) )
+ unit->OnGossipSelect( _player, option );
+}
+
+void WorldSession::HandleSpiritHealerActivateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug("WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
+
+ uint64 guid;
+
+ recv_data >> guid;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_SPIRITHEALER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleSpiritHealerActivateOpcode - 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);
+
+ SendSpiritResurrect();
+}
+
+void WorldSession::SendSpiritResurrect()
+{
+ _player->ResurrectPlayer(0.5f,false, true);
+
+ _player->DurabilityLossAll(0.25f,true);
+
+ // get corpse nearest graveyard
+ WorldSafeLocsEntry const *corpseGrave = NULL;
+ Corpse *corpse = _player->GetCorpse();
+ if(corpse)
+ corpseGrave = objmgr.GetClosestGraveYard(
+ corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeam() );
+
+ // now can spawn bones
+ _player->SpawnCorpseBones();
+
+ // teleport to nearest from corpse graveyard, if different from nearest to player ghost
+ if(corpseGrave)
+ {
+ WorldSafeLocsEntry const *ghostGrave = objmgr.GetClosestGraveYard(
+ _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetMapId(), _player->GetTeam() );
+
+ if(corpseGrave != ghostGrave)
+ _player->TeleportTo(corpseGrave->map_id, corpseGrave->x, corpseGrave->y, corpseGrave->z, _player->GetOrientation());
+ // or update at original position
+ else
+ ObjectAccessor::UpdateVisibilityForPlayer(_player);
+ }
+ // or update at original position
+ else
+ ObjectAccessor::UpdateVisibilityForPlayer(_player);
+
+ _player->SaveToDB();
+}
+
+void WorldSession::HandleBinderActivateOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 npcGUID;
+ recv_data >> npcGUID;
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID,UNIT_NPC_FLAG_INNKEEPER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleBinderActivateOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ SendBindPoint(unit);
+}
+
+void WorldSession::SendBindPoint(Creature *npc)
+{
+ uint32 bindspell = 3286;
+
+ // update sql homebind
+ CharacterDatabase.PExecute("UPDATE character_homebind SET map = '%u', zone = '%u', position_x = '%f', position_y = '%f', position_z = '%f' WHERE guid = '%u'", _player->GetMapId(), _player->GetZoneId(), _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetGUIDLow());
+ _player->m_homebindMapId = _player->GetMapId();
+ _player->m_homebindZoneId = _player->GetZoneId();
+ _player->m_homebindX = _player->GetPositionX();
+ _player->m_homebindY = _player->GetPositionY();
+ _player->m_homebindZ = _player->GetPositionZ();
+
+ // send spell for bind 3286 bind magic
+ npc->CastSpell(_player, bindspell, true);
+
+ WorldPacket data( SMSG_TRAINER_BUY_SUCCEEDED, (8+4));
+ data << npc->GetGUID();
+ data << bindspell;
+ SendPacket( &data );
+
+ // binding
+ data.Initialize( SMSG_BINDPOINTUPDATE, (4+4+4+4+4) );
+ data << float(_player->GetPositionX());
+ data << float(_player->GetPositionY());
+ data << float(_player->GetPositionZ());
+ data << uint32(_player->GetMapId());
+ data << uint32(_player->GetZoneId());
+ SendPacket( &data );
+
+ DEBUG_LOG("New Home Position X is %f",_player->GetPositionX());
+ DEBUG_LOG("New Home Position Y is %f",_player->GetPositionY());
+ DEBUG_LOG("New Home Position Z is %f",_player->GetPositionZ());
+ DEBUG_LOG("New Home MapId is %u",_player->GetMapId());
+ DEBUG_LOG("New Home ZoneId is %u",_player->GetZoneId());
+
+ // zone update
+ data.Initialize( SMSG_PLAYERBOUND, 8+4 );
+ data << uint64(_player->GetGUID());
+ data << uint32(_player->GetZoneId());
+ SendPacket( &data );
+
+ _player->PlayerTalkClass->CloseGossip();
+}
+
+//Need fix
+void WorldSession::HandleListStabledPetsOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug("WORLD: Recv MSG_LIST_STABLED_PETS");
+ uint64 npcGUID;
+
+ recv_data >> npcGUID;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleListStabledPetsOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ SendStablePet(npcGUID);
+}
+
+void WorldSession::SendStablePet(uint64 guid )
+{
+ sLog.outDebug("WORLD: Recv MSG_LIST_STABLED_PETS Send.");
+
+ WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
+ data << uint64 ( guid );
+
+ Pet *pet = _player->GetPet();
+
+ data << uint8(0); // place holder for slot show number
+ data << uint8(GetPlayer()->m_stableSlots);
+
+ uint8 num = 0; // counter for place holder
+
+ // not let move dead pet in slot
+ if(pet && pet->isAlive() && pet->getPetType()==HUNTER_PET)
+ {
+ data << uint32(pet->GetCharmInfo()->GetPetNumber());
+ data << uint32(pet->GetEntry());
+ data << uint32(pet->getLevel());
+ data << pet->GetName(); // petname
+ data << uint32(pet->GetLoyaltyLevel()); // loyalty
+ data << uint8(0x01); // client slot 1 == current pet (0)
+ ++num;
+ }
+
+ // 0 1 2 3 4 5 6
+ QueryResult* result = CharacterDatabase.PQuery("SELECT owner, slot, id, entry, level, loyalty, name FROM character_pet WHERE owner = '%u' AND slot > 0 AND slot < 3",_player->GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ data << uint32(fields[2].GetUInt32()); // petnumber
+ data << uint32(fields[3].GetUInt32()); // creature entry
+ data << uint32(fields[4].GetUInt32()); // level
+ data << fields[6].GetString(); // name
+ data << uint32(fields[5].GetUInt32()); // loyalty
+ data << uint8(fields[1].GetUInt32()+1); // slot
+
+ ++num;
+ }while( result->NextRow() );
+
+ delete result;
+ }
+
+ data.put<uint8>(8, num); // set real data to placeholder
+ SendPacket(&data);
+}
+
+void WorldSession::HandleStablePet( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug("WORLD: Recv CMSG_STABLE_PET not dispose.");
+ uint64 npcGUID;
+
+ recv_data >> npcGUID;
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleStablePet - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ Pet *pet = _player->GetPet();
+
+ WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
+
+ // can't place in stable dead pet
+ if(!pet||!pet->isAlive()||pet->getPetType()!=HUNTER_PET)
+ {
+ data << uint8(0x06);
+ SendPacket(&data);
+ return;
+ }
+
+ uint32 free_slot = 1;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT owner,slot,id FROM character_pet WHERE owner = '%u' AND slot > 0 AND slot < 3 ORDER BY slot ",_player->GetGUIDLow());
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 slot = fields[1].GetUInt32();
+
+ if(slot==free_slot) // this slot not free
+ ++free_slot;
+ }while( result->NextRow() );
+ }
+ delete result;
+
+ if( free_slot > 0 && free_slot <= GetPlayer()->m_stableSlots)
+ {
+ _player->RemovePet(pet,PetSaveMode(free_slot));
+ data << uint8(0x08);
+ }
+ else
+ data << uint8(0x06);
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleUnstablePet( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ sLog.outDebug("WORLD: Recv CMSG_UNSTABLE_PET.");
+ uint64 npcGUID;
+ uint32 petnumber;
+
+ recv_data >> npcGUID >> petnumber;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleUnstablePet - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
+
+ Pet* pet = _player->GetPet();
+ if(pet && pet->isAlive())
+ {
+ uint8 i = 0x06;
+ data << uint8(i);
+ SendPacket(&data);
+ return;
+ }
+
+ // delete dead pet
+ if(pet)
+ _player->RemovePet(pet,PET_SAVE_AS_DELETED);
+
+ Pet *newpet = NULL;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT entry FROM character_pet WHERE owner = '%u' AND id = '%u' AND slot > 0 AND slot < 3",_player->GetGUIDLow(),petnumber);
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ uint32 petentry = fields[0].GetUInt32();
+
+ newpet = new Pet(HUNTER_PET);
+ if(!newpet->LoadPetFromDB(_player,petentry,petnumber))
+ {
+ delete newpet;
+ newpet = NULL;
+ }
+ delete result;
+ }
+
+ if(newpet)
+ data << uint8(0x09);
+ else
+ data << uint8(0x06);
+ SendPacket(&data);
+}
+
+void WorldSession::HandleBuyStableSlot( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ sLog.outDebug("WORLD: Recv CMSG_BUY_STABLE_SLOT.");
+ uint64 npcGUID;
+
+ recv_data >> npcGUID;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleBuyStableSlot - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ WorldPacket data(SMSG_STABLE_RESULT, 200);
+
+ if(GetPlayer()->m_stableSlots < 2) // max slots amount = 2
+ {
+ StableSlotPricesEntry const *SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
+ if(_player->GetMoney() >= SlotPrice->Price)
+ {
+ ++GetPlayer()->m_stableSlots;
+ _player->ModifyMoney(-int32(SlotPrice->Price));
+ data << uint8(0x0A); // success buy
+ }
+ else
+ data << uint8(0x06);
+ }
+ else
+ data << uint8(0x06);
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleStableRevivePet( WorldPacket &/* recv_data */)
+{
+ sLog.outDebug("HandleStableRevivePet: Not implemented");
+}
+
+void WorldSession::HandleStableSwapPet( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ sLog.outDebug("WORLD: Recv CMSG_STABLE_SWAP_PET.");
+ uint64 npcGUID;
+ uint32 pet_number;
+
+ recv_data >> npcGUID >> pet_number;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_STABLEMASTER);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleStableSwapPet - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ WorldPacket data(SMSG_STABLE_RESULT, 200); // guess size
+
+ Pet* pet = _player->GetPet();
+
+ if(!pet || pet->getPetType()!=HUNTER_PET)
+ return;
+
+ // find swapped pet slot in stable
+ QueryResult *result = CharacterDatabase.PQuery("SELECT slot,entry FROM character_pet WHERE owner = '%u' AND id = '%u'",_player->GetGUIDLow(),pet_number);
+ if(!result)
+ return;
+
+ Field *fields = result->Fetch();
+
+ uint32 slot = fields[0].GetUInt32();
+ uint32 petentry = fields[1].GetUInt32();
+ delete result;
+
+ // move alive pet to slot or delele dead pet
+ _player->RemovePet(pet,pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
+
+ // summon unstabled pet
+ Pet *newpet = new Pet;
+ if(!newpet->LoadPetFromDB(_player,petentry,pet_number))
+ {
+ delete newpet;
+ data << uint8(0x06);
+ }
+ else
+ data << uint8(0x09);
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleRepairItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+8+1);
+
+ sLog.outDebug("WORLD: CMSG_REPAIR_ITEM");
+
+ uint64 npcGUID, itemGUID;
+ uint8 guildBank; // new in 2.3.2, bool that means from guild bank money
+
+ recv_data >> npcGUID >> itemGUID >> guildBank;
+
+ Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, npcGUID, UNIT_NPC_FLAG_REPAIR);
+ if (!unit)
+ {
+ sLog.outDebug( "WORLD: HandleRepairItemOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(npcGUID)) );
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ // reputation discount
+ float discountMod = _player->GetReputationPriceDiscount(unit);
+
+ uint32 TotalCost = 0;
+ if (itemGUID)
+ {
+ sLog.outDebug("ITEM: Repair item, itemGUID = %u, npcGUID = %u", GUID_LOPART(itemGUID), GUID_LOPART(npcGUID));
+
+ Item* item = _player->GetItemByGuid(itemGUID);
+
+ if(item)
+ TotalCost= _player->DurabilityRepair(item->GetPos(),true,discountMod,guildBank>0?true:false);
+ }
+ else
+ {
+ sLog.outDebug("ITEM: Repair all items, npcGUID = %u", GUID_LOPART(npcGUID));
+
+ TotalCost = _player->DurabilityRepairAll(true,discountMod,guildBank>0?true:false);
+ }
+ if (guildBank)
+ {
+ uint32 GuildId = _player->GetGuildId();
+ if (!GuildId)
+ return;
+ Guild *pGuild = objmgr.GetGuildById(GuildId);
+ if (!pGuild)
+ return;
+ pGuild->LogBankEvent(GUILD_BANK_LOG_REPAIR_MONEY, 0, _player->GetGUIDLow(), TotalCost);
+ pGuild->SendMoneyInfo(this, _player->GetGUIDLow());
+ }
+}
diff --git a/src/game/Object.cpp b/src/game/Object.cpp
index 1b690902023..7266d5612e7 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 b753360838e..66ad79a6e40 100644
--- a/src/game/ObjectMgr.cpp
+++ b/src/game/ObjectMgr.cpp
@@ -1,6997 +1,6997 @@
-/*
- * 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 "Database/SQLStorage.h"
-
-#include "Log.h"
-#include "MapManager.h"
-#include "ObjectMgr.h"
-#include "SpellMgr.h"
-#include "UpdateMask.h"
-#include "World.h"
-#include "WorldSession.h"
-#include "Group.h"
-#include "Guild.h"
-#include "ArenaTeam.h"
-#include "Transports.h"
-#include "ProgressBar.h"
-#include "Policies/SingletonImp.h"
-#include "Language.h"
-#include "GameEvent.h"
-#include "Spell.h"
-#include "Chat.h"
-#include "InstanceSaveMgr.h"
-#include "SpellAuras.h"
-#include "Util.h"
-
-INSTANTIATE_SINGLETON_1(ObjectMgr);
-
-ScriptMapMap sQuestEndScripts;
-ScriptMapMap sQuestStartScripts;
-ScriptMapMap sSpellScripts;
-ScriptMapMap sGameObjectScripts;
-ScriptMapMap sEventScripts;
-
-bool normalizePlayerName(std::string& name)
-{
- if(name.empty())
- return false;
-
- wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME+1];
- size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
-
- if(!Utf8toWStr(name,&wstr_buf[0],wstr_len))
- return false;
-
- wstr_buf[0] = wcharToUpper(wstr_buf[0]);
- for(size_t i = 1; i < wstr_len; ++i)
- wstr_buf[i] = wcharToLower(wstr_buf[i]);
-
- if(!WStrToUtf8(wstr_buf,wstr_len,name))
- return false;
-
- return true;
-}
-
-LanguageDesc lang_description[LANGUAGES_COUNT] =
-{
- { LANG_ADDON, 0, 0 },
- { LANG_UNIVERSAL, 0, 0 },
- { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
- { LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
- { LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
- { LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
- { LANG_COMMON, 668, SKILL_LANG_COMMON },
- { LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
- { LANG_TITAN, 816, SKILL_LANG_TITAN },
- { LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
- { LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
- { LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
- { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
- { LANG_TROLL, 7341, SKILL_LANG_TROLL },
- { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
- { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
- { LANG_ZOMBIE, 0, 0 },
- { LANG_GNOMISH_BINARY, 0, 0 },
- { LANG_GOBLIN_BINARY, 0, 0 }
-};
-
-LanguageDesc const* GetLanguageDescByID(uint32 lang)
-{
- for(int i = 0; i < LANGUAGES_COUNT; ++i)
- {
- if(uint32(lang_description[i].lang_id) == lang)
- return &lang_description[i];
- }
-
- return NULL;
-}
-
-ObjectMgr::ObjectMgr()
-{
- m_hiCharGuid = 1;
- m_hiCreatureGuid = 1;
- m_hiPetGuid = 1;
- m_hiItemGuid = 1;
- m_hiGoGuid = 1;
- m_hiDoGuid = 1;
- m_hiCorpseGuid = 1;
-
- m_hiPetNumber = 1;
-
- mGuildBankTabPrice.resize(GUILD_BANK_MAX_TABS);
- mGuildBankTabPrice[0] = 100;
- mGuildBankTabPrice[1] = 250;
- mGuildBankTabPrice[2] = 500;
- mGuildBankTabPrice[3] = 1000;
- mGuildBankTabPrice[4] = 2500;
- mGuildBankTabPrice[5] = 5000;
-
- // Only zero condition left, others will be added while loading DB tables
- mConditions.resize(1);
-}
-
-ObjectMgr::~ObjectMgr()
-{
- for( QuestMap::iterator i = mQuestTemplates.begin( ); i != mQuestTemplates.end( ); ++ i )
- {
- delete i->second;
- }
- mQuestTemplates.clear( );
-
- for( GossipTextMap::iterator i = mGossipText.begin( ); i != mGossipText.end( ); ++ i )
- {
- delete i->second;
- }
- mGossipText.clear( );
-
- mAreaTriggers.clear();
-
- for(PetLevelInfoMap::iterator i = petInfo.begin( ); i != petInfo.end( ); ++ i )
- {
- delete[] i->second;
- }
- petInfo.clear();
-
- // free only if loaded
- for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
- delete[] playerClassInfo[class_].levelInfo;
-
- for (int race = 0; race < MAX_RACES; ++race)
- for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
- delete[] playerInfo[race][class_].levelInfo;
-
- // free group and guild objects
- for (GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
- delete (*itr);
- for (GuildSet::iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); ++itr)
- delete (*itr);
-
- for(ItemMap::iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr)
- delete itr->second;
-
- for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
- itr->second.Clear();
-
- for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
- itr->second.Clear();
-}
-
-Group * ObjectMgr::GetGroupByLeader(const uint64 &guid) const
-{
- for(GroupSet::const_iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
- if ((*itr)->GetLeaderGUID() == guid)
- return *itr;
-
- return NULL;
-}
-
-Guild * ObjectMgr::GetGuildById(const uint32 GuildId) const
-{
- for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
- if ((*itr)->GetId() == GuildId)
- return *itr;
-
- return NULL;
-}
-
-Guild * ObjectMgr::GetGuildByName(std::string guildname) const
-{
- for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
- if ((*itr)->GetName() == guildname)
- return *itr;
-
- return NULL;
-}
-
-std::string ObjectMgr::GetGuildNameById(const uint32 GuildId) const
-{
- for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
- if ((*itr)->GetId() == GuildId)
- return (*itr)->GetName();
-
- return "";
-}
-
-Guild* ObjectMgr::GetGuildByLeader(const uint64 &guid) const
-{
- for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); ++itr)
- if( (*itr)->GetLeader() == guid)
- return *itr;
-
- return NULL;
-}
-
-ArenaTeam* ObjectMgr::GetArenaTeamById(const uint32 ArenaTeamId) const
-{
- for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
- if ((*itr)->GetId() == ArenaTeamId)
- return *itr;
-
- return NULL;
-}
-
-ArenaTeam* ObjectMgr::GetArenaTeamByName(std::string arenateamname) const
-{
- for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
- if ((*itr)->GetName() == arenateamname)
- return *itr;
-
- return NULL;
-}
-
-ArenaTeam* ObjectMgr::GetArenaTeamByCapitan(uint64 const& guid) const
-{
- for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
- if ((*itr)->GetCaptain() == guid)
- return *itr;
-
- return NULL;
-}
-
-AuctionHouseObject * ObjectMgr::GetAuctionsMap( uint32 location )
-{
- switch ( location )
- {
- case 6: //horde
- return & mHordeAuctions;
- break;
- case 2: //alliance
- return & mAllianceAuctions;
- break;
- default: //neutral
- return & mNeutralAuctions;
- }
-}
-
-uint32 ObjectMgr::GetAuctionCut(uint32 location, uint32 highBid)
-{
- if (location == 7 && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
- return (uint32) (0.15f * highBid * sWorld.getRate(RATE_AUCTION_CUT));
- else
- return (uint32) (0.05f * highBid * sWorld.getRate(RATE_AUCTION_CUT));
-}
-
-uint32 ObjectMgr::GetAuctionDeposit(uint32 location, uint32 time, Item *pItem)
-{
- float percentance; // in 0..1
- if ( location == 7 && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
- percentance = 0.75f;
- else
- percentance = 0.15f;
-
- percentance *= sWorld.getRate(RATE_AUCTION_DEPOSIT);
-
- return uint32( percentance * pItem->GetProto()->SellPrice * pItem->GetCount() * (time / MIN_AUCTION_TIME ) );
-}
-
-/// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c
-uint32 ObjectMgr::GetAuctionOutBid(uint32 currentBid)
-{
- uint32 outbid = (currentBid / 100) * 5;
- if (!outbid)
- outbid = 1;
- return outbid;
-}
-
-//does not clear ram
-void ObjectMgr::SendAuctionWonMail( AuctionEntry *auction )
-{
- Item *pItem = GetAItem(auction->item_guidlow);
- if(!pItem)
- return;
-
- uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
- Player *bidder = GetPlayer(bidder_guid);
-
- uint32 bidder_accId = 0;
-
- // data for gm.log
- if( sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
- {
- uint32 bidder_security = 0;
- std::string bidder_name;
- if (bidder)
- {
- bidder_accId = bidder->GetSession()->GetAccountId();
- bidder_security = bidder->GetSession()->GetSecurity();
- bidder_name = bidder->GetName();
- }
- else
- {
- bidder_accId = GetPlayerAccountIdByGUID(bidder_guid);
- bidder_security = GetSecurityByAccount(bidder_accId);
-
- if(bidder_security > SEC_PLAYER ) // not do redundant DB requests
- {
- if(!GetPlayerNameByGUID(bidder_guid,bidder_name))
- bidder_name = GetMangosStringForDBCLocale(LANG_UNKNOWN);
- }
- }
-
- if( bidder_security > SEC_PLAYER )
- {
- std::string owner_name;
- if(!GetPlayerNameByGUID(auction->owner,owner_name))
- owner_name = GetMangosStringForDBCLocale(LANG_UNKNOWN);
-
- uint32 owner_accid = GetPlayerAccountIdByGUID(auction->owner);
-
- sLog.outCommand("GM %s (Account: %u) won item in auction: %s (Entry: %u Count: %u) and pay money: %u. Original owner %s (Account: %u)",
- bidder_name.c_str(),bidder_accId,pItem->GetProto()->Name1,pItem->GetEntry(),pItem->GetCount(),auction->bid,owner_name.c_str(),owner_accid);
- }
- }
- else if(!bidder)
- bidder_accId = GetPlayerAccountIdByGUID(bidder_guid);
-
- // receiver exist
- if(bidder || bidder_accId)
- {
- std::ostringstream msgAuctionWonSubject;
- msgAuctionWonSubject << auction->item_template << ":0:" << AUCTION_WON;
-
- std::ostringstream msgAuctionWonBody;
- msgAuctionWonBody.width(16);
- msgAuctionWonBody << std::right << std::hex << auction->owner;
- msgAuctionWonBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
- sLog.outDebug( "AuctionWon body string : %s", msgAuctionWonBody.str().c_str() );
-
- //prepare mail data... :
- uint32 itemTextId = this->CreateItemText( msgAuctionWonBody.str() );
-
- // set owner to bidder (to prevent delete item with sender char deleting)
- // owner in `data` will set at mail receive and item extracting
- CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow());
- CharacterDatabase.CommitTransaction();
-
- MailItemsInfo mi;
- mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
-
- if (bidder)
- bidder->GetSession()->SendAuctionBidderNotification( auction->location, auction->Id, bidder_guid, 0, 0, auction->item_template);
- else
- RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
-
- // will delete item or place to receiver mail list
- WorldSession::SendMailTo(bidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->bidder, msgAuctionWonSubject.str(), itemTextId, &mi, 0, 0, MAIL_CHECK_MASK_AUCTION);
- }
- // receiver not exist
- else
- {
- CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", pItem->GetGUIDLow());
- RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
- delete pItem;
- }
-}
-
-void ObjectMgr::SendAuctionSalePendingMail( AuctionEntry * auction )
-{
- uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
- Player *owner = GetPlayer(owner_guid);
-
- // owner exist (online or offline)
- if(owner || GetPlayerAccountIdByGUID(owner_guid))
- {
- std::ostringstream msgAuctionSalePendingSubject;
- msgAuctionSalePendingSubject << auction->item_template << ":0:" << AUCTION_SALE_PENDING;
-
- std::ostringstream msgAuctionSalePendingBody;
- uint32 auctionCut = GetAuctionCut(auction->location, auction->bid);
-
- time_t distrTime = time(NULL) + HOUR;
-
- msgAuctionSalePendingBody.width(16);
- msgAuctionSalePendingBody << std::right << std::hex << auction->bidder;
- msgAuctionSalePendingBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
- msgAuctionSalePendingBody << ":" << auction->deposit << ":" << auctionCut << ":0:";
- msgAuctionSalePendingBody << secsToTimeBitFields(distrTime);
-
- sLog.outDebug("AuctionSalePending body string : %s", msgAuctionSalePendingBody.str().c_str());
-
- uint32 itemTextId = this->CreateItemText( msgAuctionSalePendingBody.str() );
-
- WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->owner, msgAuctionSalePendingSubject.str(), itemTextId, NULL, 0, 0, MAIL_CHECK_MASK_AUCTION);
- }
-}
-
-//call this method to send mail to auction owner, when auction is successful, it does not clear ram
-void ObjectMgr::SendAuctionSuccessfulMail( AuctionEntry * auction )
-{
- uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
- Player *owner = GetPlayer(owner_guid);
-
- uint32 owner_accId = 0;
- if(!owner)
- owner_accId = GetPlayerAccountIdByGUID(owner_guid);
-
- // owner exist
- if(owner || owner_accId)
- {
- std::ostringstream msgAuctionSuccessfulSubject;
- msgAuctionSuccessfulSubject << auction->item_template << ":0:" << AUCTION_SUCCESSFUL;
-
- std::ostringstream auctionSuccessfulBody;
- uint32 auctionCut = GetAuctionCut(auction->location, auction->bid);
-
- auctionSuccessfulBody.width(16);
- auctionSuccessfulBody << std::right << std::hex << auction->bidder;
- auctionSuccessfulBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
- auctionSuccessfulBody << ":" << auction->deposit << ":" << auctionCut;
-
- sLog.outDebug("AuctionSuccessful body string : %s", auctionSuccessfulBody.str().c_str());
-
- uint32 itemTextId = this->CreateItemText( auctionSuccessfulBody.str() );
-
- uint32 profit = auction->bid + auction->deposit - auctionCut;
-
- if (owner)
- {
- //send auction owner notification, bidder must be current!
- owner->GetSession()->SendAuctionOwnerNotification( auction );
- }
-
- WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->owner, msgAuctionSuccessfulSubject.str(), itemTextId, NULL, profit, 0, MAIL_CHECK_MASK_AUCTION, HOUR);
- }
-}
-
-//does not clear ram
-void ObjectMgr::SendAuctionExpiredMail( AuctionEntry * auction )
-{ //return an item in auction to its owner by mail
- Item *pItem = GetAItem(auction->item_guidlow);
- if(!pItem)
- {
- sLog.outError("Auction item (GUID: %u) not found, and lost.",auction->item_guidlow);
- return;
- }
-
- uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
- Player *owner = GetPlayer(owner_guid);
-
- uint32 owner_accId = 0;
- if(!owner)
- owner_accId = GetPlayerAccountIdByGUID(owner_guid);
-
- // owner exist
- if(owner || owner_accId)
- {
- std::ostringstream subject;
- subject << auction->item_template << ":0:" << AUCTION_EXPIRED;
-
- if ( owner )
- owner->GetSession()->SendAuctionOwnerNotification( auction );
- else
- RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
-
- MailItemsInfo mi;
- mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
-
- // will delete item or place to receiver mail list
- WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, GUID_LOPART(owner_guid), subject.str(), 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
-
- }
- // owner not found
- else
- {
- CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'",pItem->GetGUIDLow());
- RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
- delete pItem;
- }
-}
-
-CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id)
-{
- return sCreatureStorage.LookupEntry<CreatureInfo>(id);
-}
-
-void ObjectMgr::LoadCreatureLocales()
-{
- QueryResult *result = WorldDatabase.Query("SELECT entry,name_loc1,subname_loc1,name_loc2,subname_loc2,name_loc3,subname_loc3,name_loc4,subname_loc4,name_loc5,subname_loc5,name_loc6,subname_loc6,name_loc7,subname_loc7,name_loc8,subname_loc8 FROM locales_creature");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString("");
- sLog.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 entry = fields[0].GetUInt32();
-
- CreatureLocale& data = mCreatureLocaleMap[entry];
-
- for(int i = 1; i < MAX_LOCALE; ++i)
- {
- std::string str = fields[1+2*(i-1)].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.Name.size() <= idx)
- data.Name.resize(idx+1);
-
- data.Name[idx] = str;
- }
- }
- str = fields[1+2*(i-1)+1].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.SubName.size() <= idx)
- data.SubName.resize(idx+1);
-
- data.SubName[idx] = str;
- }
- }
- }
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u creature locale strings", mCreatureLocaleMap.size() );
-}
-
-void ObjectMgr::LoadCreatureTemplates()
-{
- sCreatureStorage.Load();
-
- sLog.outString( ">> Loaded %u creature definitions", sCreatureStorage.RecordCount );
- sLog.outString();
-
- std::set<uint32> heroicEntries; // already loaded heroic value in creatures
- std::set<uint32> hasHeroicEntries; // already loaded creatures with heroic entry values
-
- // check data correctness
- for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
- {
- CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
- if(!cInfo)
- continue;
-
- if(cInfo->HeroicEntry)
- {
- CreatureInfo const* heroicInfo = GetCreatureTemplate(cInfo->HeroicEntry);
- if(!heroicInfo)
- {
- sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u not exist.",cInfo->HeroicEntry,cInfo->HeroicEntry);
- continue;
- }
-
- if(heroicEntries.find(i)!=heroicEntries.end())
- {
- sLog.outErrorDb("Creature (Entry: %u) listed as heroic but have value in `heroic_entry`.",i);
- continue;
- }
-
- if(heroicEntries.find(cInfo->HeroicEntry)!=heroicEntries.end())
- {
- sLog.outErrorDb("Creature (Entry: %u) already listed as heroic for another entry.",cInfo->HeroicEntry);
- continue;
- }
-
- if(hasHeroicEntries.find(cInfo->HeroicEntry)!=hasHeroicEntries.end())
- {
- sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u have heroic entry also.",i,cInfo->HeroicEntry,cInfo->HeroicEntry);
- continue;
- }
-
- if(cInfo->npcflag != heroicInfo->npcflag)
- {
- sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `npcflag` in heroic mode.",i);
- continue;
- }
-
- if(cInfo->classNum != heroicInfo->classNum)
- {
- sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `classNum` in heroic mode.",i);
- continue;
- }
-
- if(cInfo->race != heroicInfo->race)
- {
- sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `race` in heroic mode.",i);
- continue;
- }
-
- if(cInfo->trainer_type != heroicInfo->trainer_type)
- {
- sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_type` in heroic mode.",i);
- continue;
- }
-
- if(cInfo->trainer_spell != heroicInfo->trainer_spell)
- {
- sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_spell` in heroic mode.",i);
- continue;
- }
-
- hasHeroicEntries.insert(i);
- heroicEntries.insert(cInfo->HeroicEntry);
- }
-
- FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A);
- if(!factionTemplate)
- sLog.outErrorDb("Creature (Entry: %u) has non-existing faction_A template (%u)", cInfo->Entry, cInfo->faction_A);
-
- factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_H);
- if(!factionTemplate)
- sLog.outErrorDb("Creature (Entry: %u) has non-existing faction_H template (%u)", cInfo->Entry, cInfo->faction_H);
-
- CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_A);
- if (!minfo)
- sLog.outErrorDb("Creature (Entry: %u) has non-existing modelId_A (%u)", cInfo->Entry, cInfo->DisplayID_A);
- minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_H);
- if (!minfo)
- sLog.outErrorDb("Creature (Entry: %u) has non-existing modelId_H (%u)", cInfo->Entry, cInfo->DisplayID_H);
-
- if(cInfo->dmgschool >= MAX_SPELL_SCHOOL)
- {
- sLog.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`",cInfo->Entry,cInfo->dmgschool);
- const_cast<CreatureInfo*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
- }
-
- if(cInfo->baseattacktime == 0)
- const_cast<CreatureInfo*>(cInfo)->baseattacktime = BASE_ATTACK_TIME;
-
- if(cInfo->rangeattacktime == 0)
- const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;
-
- if((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
- sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u",cInfo->Entry,cInfo->trainer_type);
-
- if(cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
- {
- sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly",cInfo->Entry,cInfo->InhabitType);
- const_cast<CreatureInfo*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
- }
-
- if(cInfo->PetSpellDataId)
- {
- CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
- if(!spellDataId)
- sLog.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId);
- }
-
- if(cInfo->MovementType >= MAX_DB_MOTION_TYPE)
- {
- sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.",cInfo->Entry,cInfo->MovementType);
- const_cast<CreatureInfo*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
- }
-
- if(cInfo->equipmentId > 0) // 0 no equipment
- {
- if(!GetEquipmentInfo(cInfo->equipmentId))
- {
- sLog.outErrorDb("Table `creature_template` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", cInfo->Entry, cInfo->equipmentId);
- const_cast<CreatureInfo*>(cInfo)->equipmentId = 0;
- }
- }
-
- /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
- if(cInfo->scale <= 0.0f)
- {
- CreatureDisplayInfoEntry const* ScaleEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A);
- const_cast<CreatureInfo*>(cInfo)->scale = ScaleEntry ? ScaleEntry->scale : 1.0f;
- }
- }
-}
-
-void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr)
-{
- // Now add the auras, format "spellid effectindex spellid effectindex..."
- char *p,*s;
- std::vector<int> val;
- s=p=(char*)reinterpret_cast<char const*>(addon->auras);
- if(p)
- {
- while (p[0]!=0)
- {
- ++p;
- if (p[0]==' ')
- {
- val.push_back(atoi(s));
- s=++p;
- }
- }
- if (p!=s)
- val.push_back(atoi(s));
-
- // free char* loaded memory
- delete[] (char*)reinterpret_cast<char const*>(addon->auras);
-
- // wrong list
- if (val.size()%2)
- {
- addon->auras = NULL;
- sLog.outErrorDb("Creature (%s: %u) has wrong `auras` data in `%s`.",guidEntryStr,addon->guidOrEntry,table);
- return;
- }
- }
-
- // empty list
- if(val.empty())
- {
- addon->auras = NULL;
- return;
- }
-
- // replace by new strucutres array
- const_cast<CreatureDataAddonAura*&>(addon->auras) = new CreatureDataAddonAura[val.size()/2+1];
-
- int i=0;
- for(int j=0;j<val.size()/2;++j)
- {
- CreatureDataAddonAura& cAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
- cAura.spell_id = (uint32)val[2*j+0];
- cAura.effect_idx = (uint32)val[2*j+1];
- if ( cAura.effect_idx > 2 )
- {
- sLog.outErrorDb("Creature (%s: %u) has wrong effect %u for spell %u in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
- continue;
- }
- SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura.spell_id);
- if (!AdditionalSpellInfo)
- {
- sLog.outErrorDb("Creature (%s: %u) has wrong spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.spell_id,table);
- continue;
- }
-
- if (!AdditionalSpellInfo->Effect[cAura.effect_idx] || !AdditionalSpellInfo->EffectApplyAuraName[cAura.effect_idx])
- {
- sLog.outErrorDb("Creature (%s: %u) has not aura effect %u of spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
- continue;
- }
-
- ++i;
- }
-
- // fill terminator element (after last added)
- CreatureDataAddonAura& endAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
- endAura.spell_id = 0;
- endAura.effect_idx = 0;
-}
-
-void ObjectMgr::LoadCreatureAddons()
-{
- sCreatureInfoAddonStorage.Load();
-
- sLog.outString( ">> Loaded %u creature template addons", sCreatureInfoAddonStorage.RecordCount );
- sLog.outString();
-
- // check data correctness and convert 'auras'
- for(uint32 i = 1; i < sCreatureInfoAddonStorage.MaxEntry; ++i)
- {
- CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i);
- if(!addon)
- continue;
-
- ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_template_addon", "Entry");
-
- if(!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry))
- sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `creature_template_addon`",addon->guidOrEntry);
- }
-
- sCreatureDataAddonStorage.Load();
-
- sLog.outString( ">> Loaded %u creature addons", sCreatureDataAddonStorage.RecordCount );
- sLog.outString();
-
- // check data correctness and convert 'auras'
- for(uint32 i = 1; i < sCreatureDataAddonStorage.MaxEntry; ++i)
- {
- CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(i);
- if(!addon)
- continue;
-
- ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_addon", "GUIDLow");
-
- if(mCreatureDataMap.find(addon->guidOrEntry)==mCreatureDataMap.end())
- sLog.outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`",addon->guidOrEntry);
- }
-}
-
-EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry)
-{
- return sEquipmentStorage.LookupEntry<EquipmentInfo>(entry);
-}
-
-void ObjectMgr::LoadEquipmentTemplates()
-{
- sEquipmentStorage.Load();
-
- sLog.outString( ">> Loaded %u equipment template", sEquipmentStorage.RecordCount );
- sLog.outString();
-}
-
-CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid)
-{
- return sCreatureModelStorage.LookupEntry<CreatureModelInfo>(modelid);
-}
-
-uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data)
-{
- // Load creature model (display id)
- uint32 display_id;
- if (!data || data->displayid == 0) // use defaults from the template
- {
- // DisplayID_A is used if no team is given
- if (team == HORDE)
- display_id = (cinfo->DisplayID_H2 != 0 && urand(0,1) == 0) ? cinfo->DisplayID_H2 : cinfo->DisplayID_H;
- else
- display_id = (cinfo->DisplayID_A2 != 0 && urand(0,1) == 0) ? cinfo->DisplayID_A2 : cinfo->DisplayID_A;
- }
- else // overriden in creature data
- display_id = data->displayid;
-
- return display_id;
-}
-
-CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_id)
-{
- CreatureModelInfo const *minfo = GetCreatureModelInfo(display_id);
- if(!minfo)
- return NULL;
-
- // If a model for another gender exists, 50% chance to use it
- if(minfo->modelid_other_gender != 0 && urand(0,1) == 0)
- {
- CreatureModelInfo const *minfo_tmp = GetCreatureModelInfo(minfo->modelid_other_gender);
- if(!minfo_tmp)
- {
- sLog.outErrorDb("Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", minfo->modelid, minfo->modelid_other_gender);
- return minfo; // not fatal, just use the previous one
- }
- else
- return minfo_tmp;
- }
- else
- return minfo;
-}
-
-void ObjectMgr::LoadCreatureModelInfo()
-{
- sCreatureModelStorage.Load();
-
- sLog.outString( ">> Loaded %u creature model based info", sCreatureModelStorage.RecordCount );
- sLog.outString();
-}
-
-void ObjectMgr::LoadCreatures()
-{
- uint32 count = 0;
- // 0 1 2 3
- QueryResult *result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid,"
- // 4 5 6 7 8 9 10 11
- "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint,"
- // 12 13 14 15 16 17
- "curhealth, curmana, DeathState, MovementType, spawnMask, event "
- "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString("");
- sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
- return;
- }
-
- // build single time for check creature data
- std::set<uint32> heroicCreatures;
- for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
- if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
- if(cInfo->HeroicEntry)
- heroicCreatures.insert(cInfo->HeroicEntry);
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 guid = fields[0].GetUInt32();
-
- CreatureData& data = mCreatureDataMap[guid];
-
- data.id = fields[ 1].GetUInt32();
- data.mapid = fields[ 2].GetUInt32();
- data.displayid = fields[ 3].GetUInt32();
- data.equipmentId = fields[ 4].GetUInt32();
- data.posX = fields[ 5].GetFloat();
- data.posY = fields[ 6].GetFloat();
- data.posZ = fields[ 7].GetFloat();
- data.orientation = fields[ 8].GetFloat();
- data.spawntimesecs = fields[ 9].GetUInt32();
- data.spawndist = fields[10].GetFloat();
- data.currentwaypoint= fields[11].GetUInt32();
- data.curhealth = fields[12].GetUInt32();
- data.curmana = fields[13].GetUInt32();
- data.is_dead = fields[14].GetBool();
- data.movementType = fields[15].GetUInt8();
- data.spawnMask = fields[16].GetUInt8();
- int16 gameEvent = fields[17].GetInt16();
-
- CreatureInfo const* cInfo = GetCreatureTemplate(data.id);
- if(!cInfo)
- {
- sLog.outErrorDb("Table `creature` have creature (GUID: %u) with not existed creature entry %u, skipped.",guid,data.id );
- continue;
- }
-
- if(heroicCreatures.find(data.id)!=heroicCreatures.end())
- {
- sLog.outErrorDb("Table `creature` have creature (GUID: %u) that listed as heroic template in `creature_template_substitution`, skipped.",guid,data.id );
- continue;
- }
-
- if(data.equipmentId > 0) // -1 no equipment, 0 use default
- {
- if(!GetEquipmentInfo(data.equipmentId))
- {
- sLog.outErrorDb("Table `creature` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", data.id, data.equipmentId);
- data.equipmentId = -1;
- }
- }
-
- if(cInfo->RegenHealth && data.curhealth < cInfo->minhealth)
- {
- sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenHealth`=1 and low current health (%u), `creature_template`.`minhealth`=%u.",guid,data.id,data.curhealth, cInfo->minhealth );
- data.curhealth = cInfo->minhealth;
- }
-
- if(data.curmana < cInfo->minmana)
- {
- sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with low current mana (%u), `creature_template`.`minmana`=%u.",guid,data.id,data.curmana, cInfo->minmana );
- data.curmana = cInfo->minmana;
- }
-
- if(data.spawndist < 0.0f)
- {
- sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.",guid,data.id );
- data.spawndist = 0.0f;
- }
- else if(data.movementType == RANDOM_MOTION_TYPE)
- {
- if(data.spawndist == 0.0f)
- {
- sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=1 (random movement) but with `spawndist`=0, replace by idle movement type (0).",guid,data.id );
- data.movementType = IDLE_MOTION_TYPE;
- }
- }
- else if(data.movementType == IDLE_MOTION_TYPE)
- {
- if(data.spawndist != 0.0f)
- {
- sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.",guid,data.id );
- data.spawndist = 0.0f;
- }
- }
-
- if (gameEvent==0) // if not this is to be managed by GameEvent System
- AddCreatureToGrid(guid, &data);
- ++count;
-
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u creatures", mCreatureDataMap.size() );
-}
-
-void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
-{
- uint8 mask = data->spawnMask;
- for(uint8 i = 0; mask != 0; i++, mask >>= 1)
- {
- if(mask & 1)
- {
- CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
- uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
-
- CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
- cell_guids.creatures.insert(guid);
- }
- }
-}
-
-void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data)
-{
- uint8 mask = data->spawnMask;
- for(uint8 i = 0; mask != 0; i++, mask >>= 1)
- {
- if(mask & 1)
- {
- CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
- uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
-
- CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
- cell_guids.creatures.erase(guid);
- }
- }
-}
-
-void ObjectMgr::LoadGameobjects()
-{
- uint32 count = 0;
-
- // 0 1 2 3 4 5 6
- QueryResult *result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation,"
- // 7 8 9 10 11 12 13 14 15
- "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, event "
- "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString();
- sLog.outErrorDb(">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 guid = fields[0].GetUInt32();
-
- GameObjectData& data = mGameObjectDataMap[guid];
-
- data.id = fields[ 1].GetUInt32();
- data.mapid = fields[ 2].GetUInt32();
- data.posX = fields[ 3].GetFloat();
- data.posY = fields[ 4].GetFloat();
- data.posZ = fields[ 5].GetFloat();
- data.orientation = fields[ 6].GetFloat();
- data.rotation0 = fields[ 7].GetFloat();
- data.rotation1 = fields[ 8].GetFloat();
- data.rotation2 = fields[ 9].GetFloat();
- data.rotation3 = fields[10].GetFloat();
- data.spawntimesecs = fields[11].GetInt32();
- data.animprogress = fields[12].GetUInt32();
- data.go_state = fields[13].GetUInt32();
- data.spawnMask = fields[14].GetUInt8();
- int16 gameEvent = fields[15].GetInt16();
-
- GameObjectInfo const* gInfo = GetGameObjectInfo(data.id);
- if(!gInfo)
- {
- sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u) with not existed gameobject entry %u, skipped.",guid,data.id );
- continue;
- }
-
- if (gameEvent==0) // if not this is to be managed by GameEvent System
- AddGameobjectToGrid(guid, &data);
- ++count;
-
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u gameobjects", mGameObjectDataMap.size());
-}
-
-void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
-{
- uint8 mask = data->spawnMask;
- for(uint8 i = 0; mask != 0; i++, mask >>= 1)
- {
- if(mask & 1)
- {
- CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
- uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
-
- CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
- cell_guids.gameobjects.insert(guid);
- }
- }
-}
-
-void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data)
-{
- uint8 mask = data->spawnMask;
- for(uint8 i = 0; mask != 0; i++, mask >>= 1)
- {
- if(mask & 1)
- {
- CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
- uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
-
- CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
- cell_guids.gameobjects.erase(guid);
- }
- }
-}
-
-void ObjectMgr::LoadCreatureRespawnTimes()
-{
- // remove outdated data
- WorldDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
-
- uint32 count = 0;
-
- QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM creature_respawn");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString();
- sLog.outString(">> Loaded 0 creature respawn time.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 loguid = fields[0].GetUInt32();
- uint64 respawn_time = fields[1].GetUInt64();
- uint32 instance = fields[2].GetUInt32();
-
- mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = time_t(respawn_time);
-
- ++count;
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString( ">> Loaded %u creature respawn times", mCreatureRespawnTimes.size() );
- sLog.outString();
-}
-
-void ObjectMgr::LoadGameobjectRespawnTimes()
-{
- // remove outdated data
- WorldDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
-
- uint32 count = 0;
-
- QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM gameobject_respawn");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString();
- sLog.outString(">> Loaded 0 gameobject respawn time.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 loguid = fields[0].GetUInt32();
- uint64 respawn_time = fields[1].GetUInt64();
- uint32 instance = fields[2].GetUInt32();
-
- mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = time_t(respawn_time);
-
- ++count;
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString( ">> Loaded %u gameobject respawn times", mGORespawnTimes.size() );
- sLog.outString();
-}
-
-// name must be checked to correctness (if received) before call this function
-uint64 ObjectMgr::GetPlayerGUIDByName(std::string name) const
-{
- uint64 guid = 0;
-
- CharacterDatabase.escape_string(name);
-
- // Player name safe to sending to DB (checked at login) and this function using
- QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE name = '%s'", name.c_str());
- if(result)
- {
- guid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
-
- delete result;
- }
-
- return guid;
-}
-
-bool ObjectMgr::GetPlayerNameByGUID(const uint64 &guid, std::string &name) const
-{
- // prevent DB access for online player
- if(Player* player = GetPlayer(guid))
- {
- name = player->GetName();
- return true;
- }
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
-
- if(result)
- {
- name = (*result)[0].GetCppString();
- delete result;
- return true;
- }
-
- return false;
-}
-
-uint32 ObjectMgr::GetPlayerTeamByGUID(const uint64 &guid) const
-{
- QueryResult *result = CharacterDatabase.PQuery("SELECT race FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
-
- if(result)
- {
- uint8 race = (*result)[0].GetUInt8();
- delete result;
- return Player::TeamForRace(race);
- }
-
- return 0;
-}
-
-uint32 ObjectMgr::GetPlayerAccountIdByGUID(const uint64 &guid) const
-{
- QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
- if(result)
- {
- uint32 acc = (*result)[0].GetUInt32();
- delete result;
- return acc;
- }
-
- return 0;
-}
-
-uint32 ObjectMgr::GetSecurityByAccount(uint32 acc_id) const
-{
- QueryResult *result = loginDatabase.PQuery("SELECT gmlevel FROM account WHERE id = '%u'", acc_id);
- if(result)
- {
- uint32 sec = (*result)[0].GetUInt32();
- delete result;
- return sec;
- }
-
- return 0;
-}
-
-bool ObjectMgr::GetAccountNameByAccount(uint32 acc_id, std::string &name) const
-{
- QueryResult *result = loginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", acc_id);
- if(result)
- {
- name = (*result)[0].GetCppString();
- delete result;
- return true;
- }
-
- return false;
-}
-
-uint32 ObjectMgr::GetAccountByAccountName(std::string name) const
-{
- loginDatabase.escape_string(name);
- QueryResult *result = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", name.c_str());
- if(result)
- {
- uint32 id = (*result)[0].GetUInt32();
- delete result;
- return id;
- }
-
- return 0;
-}
-
-void ObjectMgr::LoadAuctions()
-{
- QueryResult *result = CharacterDatabase.Query("SELECT COUNT(*) FROM auctionhouse");
- if( !result )
- return;
-
- Field *fields = result->Fetch();
- uint32 AuctionCount=fields[0].GetUInt32();
- delete result;
-
- if(!AuctionCount)
- return;
-
- result = CharacterDatabase.Query( "SELECT id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit,location FROM auctionhouse" );
- if( !result )
- return;
-
- barGoLink bar( AuctionCount );
-
- AuctionEntry *aItem;
-
- do
- {
- fields = result->Fetch();
-
- bar.step();
-
- aItem = new AuctionEntry;
- aItem->Id = fields[0].GetUInt32();
- aItem->auctioneer = fields[1].GetUInt32();
- aItem->item_guidlow = fields[2].GetUInt32();
- aItem->item_template = fields[3].GetUInt32();
- aItem->owner = fields[4].GetUInt32();
- aItem->buyout = fields[5].GetUInt32();
- aItem->time = fields[6].GetUInt32();
- aItem->bidder = fields[7].GetUInt32();
- aItem->bid = fields[8].GetUInt32();
- aItem->startbid = fields[9].GetUInt32();
- aItem->deposit = fields[10].GetUInt32();
- aItem->location = fields[11].GetUInt8();
- //check if sold item exists
- if ( this->GetAItem( aItem->item_guidlow ) )
- {
- GetAuctionsMap( aItem->location )->AddAuction(aItem);
- }
- else
- {
- CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",aItem->Id);
- sLog.outError("Auction %u has not a existing item : %u", aItem->Id, aItem->item_guidlow);
- delete aItem;
- }
- } while (result->NextRow());
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u auctions", AuctionCount );
- sLog.outString();
-}
-
-void ObjectMgr::LoadItemLocales()
-{
- QueryResult *result = WorldDatabase.Query("SELECT entry,name_loc1,description_loc1,name_loc2,description_loc2,name_loc3,description_loc3,name_loc4,description_loc4,name_loc5,description_loc5,name_loc6,description_loc6,name_loc7,description_loc7,name_loc8,description_loc8 FROM locales_item");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString("");
- sLog.outString(">> Loaded 0 Item locale strings. DB table `locales_item` is empty.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 entry = fields[0].GetUInt32();
-
- ItemLocale& data = mItemLocaleMap[entry];
-
- for(int i = 1; i < MAX_LOCALE; ++i)
- {
- std::string str = fields[1+2*(i-1)].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.Name.size() <= idx)
- data.Name.resize(idx+1);
-
- data.Name[idx] = str;
- }
- }
-
- str = fields[1+2*(i-1)+1].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.Description.size() <= idx)
- data.Description.resize(idx+1);
-
- data.Description[idx] = str;
- }
- }
- }
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u Item locale strings", mItemLocaleMap.size() );
-}
-
-void ObjectMgr::LoadItemPrototypes()
-{
- sItemStorage.Load ();
- sLog.outString( ">> Loaded %u item prototypes", sItemStorage.RecordCount );
- sLog.outString();
-
- // check data correctness
- for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i)
- {
- ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype >(i);
- ItemEntry const *dbcitem = sItemStore.LookupEntry(i);
- if(!proto)
- {
- /* to many errors, and possible not all items really used in game
- if (dbcitem)
- sLog.outErrorDb("Item (Entry: %u) doesn't exists in DB, but must exist.",i);
- */
- continue;
- }
-
- if(dbcitem)
- {
- if(proto->InventoryType != dbcitem->InventoryType)
- {
- sLog.outErrorDb("Item (Entry: %u) not correct %u inventory type, must be %u (still using DB value).",i,proto->InventoryType,dbcitem->InventoryType);
- // It safe let use InventoryType from DB
- }
-
- if(proto->DisplayInfoID != dbcitem->DisplayId)
- {
- sLog.outErrorDb("Item (Entry: %u) not correct %u display id, must be %u (using it).",i,proto->DisplayInfoID,dbcitem->DisplayId);
- const_cast<ItemPrototype*>(proto)->DisplayInfoID = dbcitem->DisplayId;
- }
- if(proto->Sheath != dbcitem->Sheath)
- {
- sLog.outErrorDb("Item (Entry: %u) not correct %u sheath, must be %u (using it).",i,proto->Sheath,dbcitem->Sheath);
- const_cast<ItemPrototype*>(proto)->Sheath = dbcitem->Sheath;
- }
- }
- else
- {
- sLog.outErrorDb("Item (Entry: %u) not correct (not listed in list of existed items).",i);
- }
-
- if(proto->Class >= MAX_ITEM_CLASS)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)",i,proto->Class);
- const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_JUNK;
- }
-
- if(proto->SubClass >= MaxItemSubclassValues[proto->Class])
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u",i,proto->SubClass,proto->Class);
- const_cast<ItemPrototype*>(proto)->SubClass = 0;// exist for all item classes
- }
-
- if(proto->Quality >= MAX_ITEM_QUALITY)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong Quality value (%u)",i,proto->Quality);
- const_cast<ItemPrototype*>(proto)->Quality = ITEM_QUALITY_NORMAL;
- }
-
- if(proto->BuyCount <= 0)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).",i,proto->BuyCount);
- const_cast<ItemPrototype*>(proto)->BuyCount = 1;
- }
-
- if(proto->InventoryType >= MAX_INVTYPE)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)",i,proto->InventoryType);
- const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
- }
-
- if(proto->RequiredSkill >= MAX_SKILL_TYPE)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)",i,proto->RequiredSkill);
- const_cast<ItemPrototype*>(proto)->RequiredSkill = 0;
- }
-
- if(!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE))
- {
- sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped.",i,proto->AllowableClass);
- }
-
- if(!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE))
- {
- sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped.",i,proto->AllowableRace);
- }
-
- if(proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell))
- {
- sLog.outErrorDb("Item (Entry: %u) have wrong (non-existed) spell in RequiredSpell (%u)",i,proto->RequiredSpell);
- const_cast<ItemPrototype*>(proto)->RequiredSpell = 0;
- }
-
- if(proto->RequiredReputationRank >= MAX_REPUTATION_RANK)
- sLog.outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.",i,proto->RequiredReputationRank);
-
- if(proto->RequiredReputationFaction)
- {
- if(!sFactionStore.LookupEntry(proto->RequiredReputationFaction))
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)",i,proto->RequiredReputationFaction);
- const_cast<ItemPrototype*>(proto)->RequiredReputationFaction = 0;
- }
-
- if(proto->RequiredReputationRank == MIN_REPUTATION_RANK)
- sLog.outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.",i);
- }
- else if(proto->RequiredReputationRank > MIN_REPUTATION_RANK)
- sLog.outErrorDb("Item (Entry: %u) has RequiredReputationFaction ==0 but RequiredReputationRank > 0, rank setting is useless.",i);
-
- if(proto->Stackable==0)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%u), replace by default 1.",i,proto->Stackable);
- const_cast<ItemPrototype*>(proto)->Stackable = 1;
- }
- else if(proto->Stackable > 255)
- {
- sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (255).",i,proto->Stackable);
- const_cast<ItemPrototype*>(proto)->Stackable = 255;
- }
-
- for (int j = 0; j < 10; j++)
- {
- // for ItemStatValue != 0
- if(proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)",i,j+1,proto->ItemStat[j].ItemStatType);
- const_cast<ItemPrototype*>(proto)->ItemStat[j].ItemStatType = 0;
- }
- }
-
- for (int j = 0; j < 5; j++)
- {
- if(proto->Damage[j].DamageType >= MAX_SPELL_SCHOOL)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong dmg_type%d (%u)",i,j+1,proto->Damage[j].DamageType);
- const_cast<ItemPrototype*>(proto)->Damage[j].DamageType = 0;
- }
- }
-
- // special format
- if(proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN)
- {
- // spell_1
- if(proto->Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format",i,0+1,proto->Spells[0].SpellTrigger);
- const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
- const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
- }
-
- // spell_2 have learning spell
- if(proto->Spells[1].SpellTrigger != ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format.",i,1+1,proto->Spells[1].SpellTrigger);
- const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
- }
- else if(!proto->Spells[1].SpellId)
- {
- sLog.outErrorDb("Item (Entry: %u) not has expected spell in spellid_%d in special learning format.",i,1+1);
- const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
- }
- else
- {
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[1].SpellId);
- if(!spellInfo)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId);
- const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
- }
- // allowed only in special format
- else if(proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN)
- {
- sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId);
- const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
- }
- }
-
- // spell_3*,spell_4*,spell_5* is empty
- for (int j = 2; j < 5; j++)
- {
- if(proto->Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)",i,j+1,proto->Spells[j].SpellTrigger);
- const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
- }
- else if(proto->Spells[j].SpellId != 0)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong spell in spellid_%d (%u) for learning special format",i,j+1,proto->Spells[j].SpellId);
- const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
- }
- }
- }
- // normal spell list
- else
- {
- for (int j = 0; j < 5; j++)
- {
- if(proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)",i,j+1,proto->Spells[j].SpellTrigger);
- const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
- const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
- }
-
- if(proto->Spells[j].SpellId)
- {
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[j].SpellId);
- if(!spellInfo)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId);
- const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
- }
- // allowed only in special format
- else if(proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN)
- {
- sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId);
- const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
- }
- }
- }
- }
-
- if(proto->Bonding >= MAX_BIND_TYPE)
- sLog.outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)",i,proto->Bonding);
-
- if(proto->PageText && !sPageTextStore.LookupEntry<PageText>(proto->PageText))
- sLog.outErrorDb("Item (Entry: %u) has non existing first page (Id:%u)", i,proto->PageText);
-
- if(proto->LockID && !sLockStore.LookupEntry(proto->LockID))
- sLog.outErrorDb("Item (Entry: %u) has wrong LockID (%u)",i,proto->LockID);
-
- if(proto->Sheath >= MAX_SHEATHETYPE)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong Sheath (%u)",i,proto->Sheath);
- const_cast<ItemPrototype*>(proto)->Sheath = SHEATHETYPE_NONE;
- }
-
- if(proto->RandomProperty && !sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(proto->RandomProperty)))
- {
- sLog.outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)",i,proto->RandomProperty);
- const_cast<ItemPrototype*>(proto)->RandomProperty = 0;
- }
-
- if(proto->RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(proto->RandomSuffix)))
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong RandomSuffix (%u)",i,proto->RandomSuffix);
- const_cast<ItemPrototype*>(proto)->RandomSuffix = 0;
- }
-
- if(proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet))
- {
- sLog.outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)",i,proto->ItemSet);
- const_cast<ItemPrototype*>(proto)->ItemSet = 0;
- }
-
- if(proto->Area && !GetAreaEntryByAreaID(proto->Area))
- sLog.outErrorDb("Item (Entry: %u) has wrong Area (%u)",i,proto->Area);
-
- if(proto->Map && !sMapStore.LookupEntry(proto->Map))
- sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)",i,proto->Map);
-
- if(proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory))
- sLog.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)",i,proto->TotemCategory);
-
- for (int j = 0; j < 3; j++)
- {
- if(proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong socketColor_%d (%u)",i,j+1,proto->Socket[j].Color);
- const_cast<ItemPrototype*>(proto)->Socket[j].Color = 0;
- }
- }
-
- if(proto->GemProperties && !sGemPropertiesStore.LookupEntry(proto->GemProperties))
- sLog.outErrorDb("Item (Entry: %u) has wrong GemProperties (%u)",i,proto->GemProperties);
-
- if(proto->FoodType >= MAX_PET_DIET)
- {
- sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)",i,proto->FoodType);
- const_cast<ItemPrototype*>(proto)->FoodType = 0;
- }
- }
-
- // this DBC used currently only for check item templates in DB.
- sItemStore.Clear();
-}
-
-void ObjectMgr::LoadAuctionItems()
-{
- QueryResult *result = CharacterDatabase.Query( "SELECT itemguid,item_template FROM auctionhouse" );
-
- if( !result )
- return;
-
- barGoLink bar( result->GetRowCount() );
-
- uint32 count = 0;
-
- Field *fields;
- do
- {
- bar.step();
-
- fields = result->Fetch();
- uint32 item_guid = fields[0].GetUInt32();
- uint32 item_template = fields[1].GetUInt32();
-
- ItemPrototype const *proto = GetItemPrototype(item_template);
-
- if(!proto)
- {
- sLog.outError( "ObjectMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid,item_template);
- continue;
- }
-
- Item *item = NewItemOrBag(proto);
-
- if(!item->LoadFromDB(item_guid,0))
- {
- delete item;
- continue;
- }
- AddAItem(item);
-
- ++count;
- }
- while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u auction items", count );
-}
-
-void ObjectMgr::LoadPetLevelInfo()
-{
- // Loading levels data
- {
- // 0 1 2 3 4 5 6 7 8 9
- QueryResult *result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
-
- uint32 count = 0;
-
- if (!result)
- {
- barGoLink bar( 1 );
-
- sLog.outString();
- sLog.outString( ">> Loaded %u level pet stats definitions", count );
- sLog.outErrorDb( "Error loading `pet_levelstats` table or empty table.");
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- Field* fields = result->Fetch();
-
- uint32 creature_id = fields[0].GetUInt32();
- if(!sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
- {
- sLog.outErrorDb("Wrong creature id %u in `pet_levelstats` table, ignoring.",creature_id);
- continue;
- }
-
- uint32 current_level = fields[1].GetUInt32();
- if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- {
- if(current_level > 255) // hardcoded level maximum
- sLog.outErrorDb("Wrong (> 255) level %u in `pet_levelstats` table, ignoring.",current_level);
- else
- sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `pet_levelstats` table, ignoring.",current_level);
- continue;
- }
- else if(current_level < 1)
- {
- sLog.outErrorDb("Wrong (<1) level %u in `pet_levelstats` table, ignoring.",current_level);
- continue;
- }
-
- PetLevelInfo*& pInfoMapEntry = petInfo[creature_id];
-
- if(pInfoMapEntry==NULL)
- pInfoMapEntry = new PetLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
-
- // data for level 1 stored in [0] array element, ...
- PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level-1];
-
- pLevelInfo->health = fields[2].GetUInt16();
- pLevelInfo->mana = fields[3].GetUInt16();
- pLevelInfo->armor = fields[9].GetUInt16();
-
- for (int i = 0; i < MAX_STATS; i++)
- {
- pLevelInfo->stats[i] = fields[i+4].GetUInt16();
- }
-
- bar.step();
- ++count;
- }
- while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u level pet stats definitions", count );
- }
-
- // Fill gaps and check integrity
- for (PetLevelInfoMap::iterator itr = petInfo.begin(); itr != petInfo.end(); ++itr)
- {
- PetLevelInfo* pInfo = itr->second;
-
- // fatal error if no level 1 data
- if(!pInfo || pInfo[0].health == 0 )
- {
- sLog.outErrorDb("Creature %u does not have pet stats data for Level 1!",itr->first);
- exit(1);
- }
-
- // fill level gaps
- for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
- {
- if(pInfo[level].health == 0)
- {
- sLog.outErrorDb("Creature %u has no data for Level %i pet stats data, using data of Level %i.",itr->first,level+1, level);
- pInfo[level] = pInfo[level-1];
- }
- }
- }
-}
-
-PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint32 level) const
-{
- if(level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
-
- PetLevelInfoMap::const_iterator itr = petInfo.find(creature_id);
- if(itr == petInfo.end())
- return NULL;
-
- return &itr->second[level-1]; // data for level 1 stored in [0] array element, ...
-}
-
-void ObjectMgr::LoadPlayerInfo()
-{
- // Load playercreate
- {
- // 0 1 2 3 4 5 6
- QueryResult *result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z FROM playercreateinfo");
-
- uint32 count = 0;
-
- if (!result)
- {
- barGoLink bar( 1 );
-
- sLog.outString();
- sLog.outString( ">> Loaded %u player create definitions", count );
- sLog.outErrorDb( "Error loading `playercreateinfo` table or empty table.");
- exit(1);
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- Field* fields = result->Fetch();
-
- uint32 current_race = fields[0].GetUInt32();
- uint32 current_class = fields[1].GetUInt32();
- uint32 mapId = fields[2].GetUInt32();
- uint32 zoneId = fields[3].GetUInt32();
- float positionX = fields[4].GetFloat();
- float positionY = fields[5].GetFloat();
- float positionZ = fields[6].GetFloat();
-
- if(current_race >= MAX_RACES)
- {
- sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.",current_race);
- continue;
- }
-
- ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
- if(!rEntry)
- {
- sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.",current_race);
- continue;
- }
-
- if(current_class >= MAX_CLASSES)
- {
- sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.",current_class);
- continue;
- }
-
- if(!sChrClassesStore.LookupEntry(current_class))
- {
- sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.",current_class);
- continue;
- }
-
- // accept DB data only for valid position (and non instanceable)
- if( !MapManager::IsValidMapCoord(mapId,positionX,positionY,positionZ) )
- {
- sLog.outErrorDb("Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.",current_class,current_race);
- continue;
- }
-
- if( sMapStore.LookupEntry(mapId)->Instanceable() )
- {
- sLog.outErrorDb("Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.",current_class,current_race);
- continue;
- }
-
- PlayerInfo* pInfo = &playerInfo[current_race][current_class];
-
- pInfo->mapId = mapId;
- pInfo->zoneId = zoneId;
- pInfo->positionX = positionX;
- pInfo->positionY = positionY;
- pInfo->positionZ = positionZ;
-
- pInfo->displayId_m = rEntry->model_m;
- pInfo->displayId_f = rEntry->model_f;
-
- bar.step();
- ++count;
- }
- while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u player create definitions", count );
- }
-
- // Load playercreate items
- {
- // 0 1 2 3
- QueryResult *result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
-
- uint32 count = 0;
-
- if (!result)
- {
- barGoLink bar( 1 );
-
- sLog.outString();
- sLog.outString( ">> Loaded %u player create items", count );
- sLog.outErrorDb( "Error loading `playercreateinfo_item` table or empty table.");
- }
- else
- {
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- Field* fields = result->Fetch();
-
- uint32 current_race = fields[0].GetUInt32();
- if(current_race >= MAX_RACES)
- {
- sLog.outErrorDb("Wrong race %u in `playercreateinfo_item` table, ignoring.",current_race);
- continue;
- }
-
- uint32 current_class = fields[1].GetUInt32();
- if(current_class >= MAX_CLASSES)
- {
- sLog.outErrorDb("Wrong class %u in `playercreateinfo_item` table, ignoring.",current_class);
- continue;
- }
-
- PlayerInfo* pInfo = &playerInfo[current_race][current_class];
-
- uint32 item_id = fields[2].GetUInt32();
-
- if(!GetItemPrototype(item_id))
- {
- sLog.outErrorDb("Item id %u (race %u class %u) in `playercreateinfo_item` table but not listed in `item_template`, ignoring.",item_id,current_race,current_class);
- continue;
- }
-
- uint32 amount = fields[3].GetUInt32();
-
- if(!amount)
- {
- sLog.outErrorDb("Item id %u (class %u race %u) have amount==0 in `playercreateinfo_item` table, ignoring.",item_id,current_race,current_class);
- continue;
- }
-
- pInfo->item.push_back(PlayerCreateInfoItem( item_id, amount));
-
- bar.step();
- ++count;
- }
- while(result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u player create items", count );
- }
- }
-
- // Load playercreate spells
- {
-
- QueryResult *result = NULL;
- if(sWorld.getConfig(CONFIG_START_ALL_SPELLS))
- result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell_custom");
- else
- result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell");
-
- uint32 count = 0;
-
- if (!result)
- {
- barGoLink bar( 1 );
-
- sLog.outString();
- sLog.outString( ">> Loaded %u player create spells", count );
- sLog.outErrorDb( "Error loading player starting spells or empty table.");
- }
- else
- {
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- Field* fields = result->Fetch();
-
- uint32 current_race = fields[0].GetUInt32();
- if(current_race >= MAX_RACES)
- {
- sLog.outErrorDb("Wrong race %u in `playercreateinfo_spell` table, ignoring.",current_race);
- continue;
- }
-
- uint32 current_class = fields[1].GetUInt32();
- if(current_class >= MAX_CLASSES)
- {
- sLog.outErrorDb("Wrong class %u in `playercreateinfo_spell` table, ignoring.",current_class);
- continue;
- }
-
- PlayerInfo* pInfo = &playerInfo[current_race][current_class];
- pInfo->spell.push_back(CreateSpellPair(fields[2].GetUInt16(), fields[3].GetUInt8()));
-
- bar.step();
- ++count;
- }
- while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u player create spells", count );
- }
- }
-
- // Load playercreate actions
- {
- // 0 1 2 3 4 5
- QueryResult *result = WorldDatabase.Query("SELECT race, class, button, action, type, misc FROM playercreateinfo_action");
-
- uint32 count = 0;
-
- if (!result)
- {
- barGoLink bar( 1 );
-
- sLog.outString();
- sLog.outString( ">> Loaded %u player create actions", count );
- sLog.outErrorDb( "Error loading `playercreateinfo_action` table or empty table.");
- }
- else
- {
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- Field* fields = result->Fetch();
-
- uint32 current_race = fields[0].GetUInt32();
- if(current_race >= MAX_RACES)
- {
- sLog.outErrorDb("Wrong race %u in `playercreateinfo_action` table, ignoring.",current_race);
- continue;
- }
-
- uint32 current_class = fields[1].GetUInt32();
- if(current_class >= MAX_CLASSES)
- {
- sLog.outErrorDb("Wrong class %u in `playercreateinfo_action` table, ignoring.",current_class);
- continue;
- }
-
- PlayerInfo* pInfo = &playerInfo[current_race][current_class];
- pInfo->action[0].push_back(fields[2].GetUInt16());
- pInfo->action[1].push_back(fields[3].GetUInt16());
- pInfo->action[2].push_back(fields[4].GetUInt16());
- pInfo->action[3].push_back(fields[5].GetUInt16());
-
- bar.step();
- ++count;
- }
- while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u player create actions", count );
- }
- }
-
- // Loading levels data (class only dependent)
- {
- // 0 1 2 3
- QueryResult *result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
-
- uint32 count = 0;
-
- if (!result)
- {
- barGoLink bar( 1 );
-
- sLog.outString();
- sLog.outString( ">> Loaded %u level health/mana definitions", count );
- sLog.outErrorDb( "Error loading `player_classlevelstats` table or empty table.");
- exit(1);
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- Field* fields = result->Fetch();
-
- uint32 current_class = fields[0].GetUInt32();
- if(current_class >= MAX_CLASSES)
- {
- sLog.outErrorDb("Wrong class %u in `player_classlevelstats` table, ignoring.",current_class);
- continue;
- }
-
- uint32 current_level = fields[1].GetUInt32();
- if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- {
- if(current_level > 255) // hardcoded level maximum
- sLog.outErrorDb("Wrong (> 255) level %u in `player_classlevelstats` table, ignoring.",current_level);
- else
- sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_classlevelstats` table, ignoring.",current_level);
- continue;
- }
-
- PlayerClassInfo* pClassInfo = &playerClassInfo[current_class];
-
- if(!pClassInfo->levelInfo)
- pClassInfo->levelInfo = new PlayerClassLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
-
- PlayerClassLevelInfo* pClassLevelInfo = &pClassInfo->levelInfo[current_level-1];
-
- pClassLevelInfo->basehealth = fields[2].GetUInt16();
- pClassLevelInfo->basemana = fields[3].GetUInt16();
-
- bar.step();
- ++count;
- }
- while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u level health/mana definitions", count );
- }
-
- // Fill gaps and check integrity
- for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
- {
- // skip non existed classes
- if(!sChrClassesStore.LookupEntry(class_))
- continue;
-
- PlayerClassInfo* pClassInfo = &playerClassInfo[class_];
-
- // fatal error if no level 1 data
- if(!pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0 )
- {
- sLog.outErrorDb("Class %i Level 1 does not have health/mana data!",class_);
- exit(1);
- }
-
- // fill level gaps
- for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
- {
- if(pClassInfo->levelInfo[level].basehealth == 0)
- {
- sLog.outErrorDb("Class %i Level %i does not have health/mana data. Using stats data of level %i.",class_,level+1, level);
- pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level-1];
- }
- }
- }
-
- // Loading levels data (class/race dependent)
- {
- // 0 1 2 3 4 5 6 7
- QueryResult *result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
-
- uint32 count = 0;
-
- if (!result)
- {
- barGoLink bar( 1 );
-
- sLog.outString();
- sLog.outString( ">> Loaded %u level stats definitions", count );
- sLog.outErrorDb( "Error loading `player_levelstats` table or empty table.");
- exit(1);
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- Field* fields = result->Fetch();
-
- uint32 current_race = fields[0].GetUInt32();
- if(current_race >= MAX_RACES)
- {
- sLog.outErrorDb("Wrong race %u in `player_levelstats` table, ignoring.",current_race);
- continue;
- }
-
- uint32 current_class = fields[1].GetUInt32();
- if(current_class >= MAX_CLASSES)
- {
- sLog.outErrorDb("Wrong class %u in `player_levelstats` table, ignoring.",current_class);
- continue;
- }
-
- uint32 current_level = fields[2].GetUInt32();
- if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- {
- if(current_level > 255) // hardcoded level maximum
- sLog.outErrorDb("Wrong (> 255) level %u in `player_levelstats` table, ignoring.",current_level);
- else
- sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_levelstats` table, ignoring.",current_level);
- continue;
- }
-
- PlayerInfo* pInfo = &playerInfo[current_race][current_class];
-
- if(!pInfo->levelInfo)
- pInfo->levelInfo = new PlayerLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
-
- PlayerLevelInfo* pLevelInfo = &pInfo->levelInfo[current_level-1];
-
- for (int i = 0; i < MAX_STATS; i++)
- {
- pLevelInfo->stats[i] = fields[i+3].GetUInt8();
- }
-
- bar.step();
- ++count;
- }
- while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u level stats definitions", count );
- }
-
- // Fill gaps and check integrity
- for (int race = 0; race < MAX_RACES; ++race)
- {
- // skip non existed races
- if(!sChrRacesStore.LookupEntry(race))
- continue;
-
- for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
- {
- // skip non existed classes
- if(!sChrClassesStore.LookupEntry(class_))
- continue;
-
- PlayerInfo* pInfo = &playerInfo[race][class_];
-
- // skip non loaded combinations
- if(!pInfo->displayId_m || !pInfo->displayId_f)
- continue;
-
- // skip expansion races if not playing with expansion
- if (sWorld.getConfig(CONFIG_EXPANSION) < 1 && (race == RACE_BLOODELF || race == RACE_DRAENEI))
- continue;
-
- // fatal error if no level 1 data
- if(!pInfo->levelInfo || pInfo->levelInfo[0].stats[0] == 0 )
- {
- sLog.outErrorDb("Race %i Class %i Level 1 does not have stats data!",race,class_);
- exit(1);
- }
-
- // fill level gaps
- for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
- {
- if(pInfo->levelInfo[level].stats[0] == 0)
- {
- sLog.outErrorDb("Race %i Class %i Level %i does not have stats data. Using stats data of level %i.",race,class_,level+1, level);
- pInfo->levelInfo[level] = pInfo->levelInfo[level-1];
- }
- }
- }
- }
-}
-
-void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint32 level, PlayerClassLevelInfo* info) const
-{
- if(level < 1 || class_ >= MAX_CLASSES)
- return;
-
- PlayerClassInfo const* pInfo = &playerClassInfo[class_];
-
- if(level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
-
- *info = pInfo->levelInfo[level-1];
-}
-
-void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint32 level, PlayerLevelInfo* info) const
-{
- if(level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
- return;
-
- PlayerInfo const* pInfo = &playerInfo[race][class_];
- if(pInfo->displayId_m==0 || pInfo->displayId_f==0)
- return;
-
- if(level <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- *info = pInfo->levelInfo[level-1];
- else
- BuildPlayerLevelInfo(race,class_,level,info);
-}
-
-void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
-{
- // base data (last known level)
- *info = playerInfo[race][_class].levelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1];
-
- for(int lvl = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl)
- {
- switch(_class)
- {
- case CLASS_WARRIOR:
- info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
- info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
- info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
- info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
- info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl%2) ? 1: 0);
- break;
- case CLASS_PALADIN:
- info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0);
- info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
- info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
- info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
- info->stats[STAT_SPIRIT] += (lvl > 7 ? 1: 0);
- break;
- case CLASS_HUNTER:
- info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0);
- info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
- info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
- info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
- info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
- break;
- case CLASS_ROGUE:
- info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0);
- info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
- info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
- info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
- info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
- break;
- case CLASS_PRIEST:
- info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
- info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
- info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
- info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
- info->stats[STAT_SPIRIT] += (lvl > 3 ? 1: 0);
- break;
- case CLASS_SHAMAN:
- info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
- info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
- info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0);
- info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
- info->stats[STAT_SPIRIT] += (lvl > 4 ? 1: 0);
- break;
- case CLASS_MAGE:
- info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
- info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
- info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
- info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
- info->stats[STAT_SPIRIT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
- break;
- case CLASS_WARLOCK:
- info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
- info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
- info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
- info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
- info->stats[STAT_SPIRIT] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
- break;
- case CLASS_DRUID:
- info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
- info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
- info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
- info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
- info->stats[STAT_SPIRIT] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0));
- }
- }
-}
-
-void ObjectMgr::LoadGuilds()
-{
- Guild *newguild;
- uint32 count = 0;
-
- QueryResult *result = CharacterDatabase.Query( "SELECT guildid FROM guild" );
-
- if( !result )
- {
-
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u guild definitions", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- Field *fields = result->Fetch();
-
- bar.step();
- ++count;
-
- newguild = new Guild;
- if(!newguild->LoadGuildFromDB(fields[0].GetUInt32()))
- {
- newguild->Disband();
- delete newguild;
- continue;
- }
- AddGuild(newguild);
-
- }while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u guild definitions", count );
-}
-
-void ObjectMgr::LoadArenaTeams()
-{
- uint32 count = 0;
-
- QueryResult *result = CharacterDatabase.Query( "SELECT arenateamid FROM arena_team" );
-
- if( !result )
- {
-
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u arenateam definitions", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- Field *fields = result->Fetch();
-
- bar.step();
- ++count;
-
- ArenaTeam *newarenateam = new ArenaTeam;
- if(!newarenateam->LoadArenaTeamFromDB(fields[0].GetUInt32()))
- {
- delete newarenateam;
- continue;
- }
- AddArenaTeam(newarenateam);
- }while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u arenateam definitions", count );
-}
-
-void ObjectMgr::LoadGroups()
-{
- // -- loading groups --
- Group *group = NULL;
- uint64 leaderGuid = 0;
- uint32 count = 0;
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- QueryResult *result = CharacterDatabase.PQuery("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty, leaderGuid FROM groups");
-
- if( !result )
- {
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u group definitions", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- bar.step();
- Field *fields = result->Fetch();
- ++count;
- leaderGuid = MAKE_NEW_GUID(fields[15].GetUInt32(),0,HIGHGUID_PLAYER);
-
- group = new Group;
- if(!group->LoadGroupFromDB(leaderGuid, result, false))
- {
- group->Disband();
- delete group;
- continue;
- }
- AddGroup(group);
- }while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u group definitions", count );
-
- // -- loading members --
- count = 0;
- group = NULL;
- leaderGuid = 0;
- // 0 1 2 3
- result = CharacterDatabase.PQuery("SELECT memberGuid, assistant, subgroup, leaderGuid FROM group_member ORDER BY leaderGuid");
- if(!result)
- {
- barGoLink bar( 1 );
- bar.step();
- }
- else
- {
- barGoLink bar( result->GetRowCount() );
- do
- {
- bar.step();
- Field *fields = result->Fetch();
- count++;
- leaderGuid = MAKE_NEW_GUID(fields[3].GetUInt32(), 0, HIGHGUID_PLAYER);
- if(!group || group->GetLeaderGUID() != leaderGuid)
- {
- group = GetGroupByLeader(leaderGuid);
- if(!group)
- {
- sLog.outErrorDb("Incorrect entry in group_member table : no group with leader %d for member %d!", fields[3].GetUInt32(), fields[0].GetUInt32());
- CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
- continue;
- }
- }
-
- if(!group->LoadMemberFromDB(fields[0].GetUInt32(), fields[2].GetUInt8(), fields[1].GetBool()))
- {
- sLog.outErrorDb("Incorrect entry in group_member table : member %d cannot be added to player %d's group!", fields[0].GetUInt32(), fields[3].GetUInt32());
- CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
- }
- }while( result->NextRow() );
- delete result;
- }
-
- // clean groups
- // TODO: maybe delete from the DB before loading in this case
- for(GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end();)
- {
- if((*itr)->GetMembersCount() < 2)
- {
- (*itr)->Disband();
- delete *itr;
- mGroupSet.erase(itr++);
- }
- else
- ++itr;
- }
-
- // -- loading instances --
- count = 0;
- group = NULL;
- leaderGuid = 0;
- result = CharacterDatabase.PQuery(
- // 0 1 2 3 4 5
- "SELECT leaderGuid, map, instance, permanent, difficulty, resettime, "
- // 6
- "(SELECT COUNT(*) FROM character_instance WHERE guid = leaderGuid AND instance = group_instance.instance AND permanent = 1 LIMIT 1) "
- "FROM group_instance LEFT JOIN instance ON instance = id ORDER BY leaderGuid"
- );
-
- if(!result)
- {
- barGoLink bar( 1 );
- bar.step();
- }
- else
- {
- barGoLink bar( result->GetRowCount() );
- do
- {
- bar.step();
- Field *fields = result->Fetch();
- count++;
- leaderGuid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
- if(!group || group->GetLeaderGUID() != leaderGuid)
- {
- group = GetGroupByLeader(leaderGuid);
- if(!group)
- {
- sLog.outErrorDb("Incorrect entry in group_instance table : no group with leader %d", fields[0].GetUInt32());
- continue;
- }
- }
-
- InstanceSave *save = sInstanceSaveManager.AddInstanceSave(fields[1].GetUInt32(), fields[2].GetUInt32(), fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true);
- group->BindToInstance(save, fields[3].GetBool(), true);
- }while( result->NextRow() );
- delete result;
- }
-
- sLog.outString();
- sLog.outString( ">> Loaded %u group-instance binds total", count );
-
- sLog.outString();
- sLog.outString( ">> Loaded %u group members total", count );
-}
-
-void ObjectMgr::LoadQuests()
-{
- // For reload case
- for(QuestMap::const_iterator itr=mQuestTemplates.begin(); itr != mQuestTemplates.end(); ++itr)
- delete itr->second;
- mQuestTemplates.clear();
-
- mExclusiveQuestGroups.clear();
-
- // 0 1 2 3 4 5 6 7 8
- QueryResult *result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue,"
- // 9 10 11 12 13 14 15 16
- "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
- // 17 18 19 20 21 22 23 24 25 26
- "QuestFlags, SpecialFlags, CharTitleId, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell,"
- // 27 28 29 30 31 32 33 34 35 36
- "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
- // 37 38 39 40 41 42 43 44
- "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4,"
- // 45 46 47 48 49 50 51 52 53 54 54 55
- "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, ReqSourceRef1, ReqSourceRef2, ReqSourceRef3, ReqSourceRef4,"
- // 57 58 59 60 61 62 63 64
- "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
- // 65 66 67 68
- "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
- // 69 70 71 72 73 74
- "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
- // 75 76 77 78 79 80
- "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
- // 81 82 83 84 85 86 87 88
- "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
- // 89 90 91 92 93 94 95 96 97 98
- "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
- // 99 100 101 102 103 104 105 106 107 108
- "RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
- // 109 110 111 112 113 114 115 116 117 118
- "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4,IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
- // 119 120
- "StartScript, CompleteScript"
- " FROM quest_template");
- if(result == NULL)
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded 0 quests definitions" );
- sLog.outErrorDb("`quest_template` table is empty!");
- return;
- }
-
- // create multimap previous quest for each existed quest
- // some quests can have many previous maps set by NextQuestId in previous quest
- // for example set of race quests can lead to single not race specific quest
- barGoLink bar( result->GetRowCount() );
- do
- {
- bar.step();
- Field *fields = result->Fetch();
-
- Quest * newQuest = new Quest(fields);
- mQuestTemplates[newQuest->GetQuestId()] = newQuest;
- } while( result->NextRow() );
-
- delete result;
-
- // Post processing
- for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); iter++)
- {
- Quest * qinfo = iter->second;
-
- // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
-
- if( qinfo->GetQuestMethod() >= 3 )
- {
- sLog.outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.",qinfo->GetQuestId(),qinfo->GetQuestMethod());
- }
-
- if (qinfo->QuestFlags & ~QUEST_MANGOS_FLAGS_DB_ALLOWED)
- {
- sLog.outErrorDb("Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
- qinfo->GetQuestId(),qinfo->QuestFlags,QUEST_MANGOS_FLAGS_DB_ALLOWED >> 16);
- qinfo->QuestFlags &= QUEST_MANGOS_FLAGS_DB_ALLOWED;
- }
-
- if(qinfo->QuestFlags & QUEST_FLAGS_DAILY)
- {
- if(!(qinfo->QuestFlags & QUEST_MANGOS_FLAGS_REPEATABLE))
- {
- sLog.outErrorDb("Daily Quest %u not marked as repeatable in `SpecialFlags`, added.",qinfo->GetQuestId());
- qinfo->QuestFlags |= QUEST_MANGOS_FLAGS_REPEATABLE;
- }
- }
-
- if(qinfo->QuestFlags & QUEST_FLAGS_AUTO_REWARDED)
- {
- // at auto-reward can be rewarded only RewChoiceItemId[0]
- for(int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j )
- {
- if(uint32 id = qinfo->RewChoiceItemId[j])
- {
- sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item from `RewChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.",
- qinfo->GetQuestId(),j+1,id,j+1);
- // no changes, quest ignore this data
- }
- }
- }
-
- // client quest log visual (area case)
- if( qinfo->ZoneOrSort > 0 )
- {
- if(!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
- {
- sLog.outErrorDb("Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
- qinfo->GetQuestId(),qinfo->ZoneOrSort);
- // no changes, quest not dependent from this value but can have problems at client
- }
- }
- // client quest log visual (sort case)
- if( qinfo->ZoneOrSort < 0 )
- {
- QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
- if( !qSort )
- {
- sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
- qinfo->GetQuestId(),qinfo->ZoneOrSort);
- // no changes, quest not dependent from this value but can have problems at client (note some may be 0, we must allow this so no check)
- }
- //check SkillOrClass value (class case).
- if( ClassByQuestSort(-int32(qinfo->ZoneOrSort)) )
- {
- // SkillOrClass should not have class case when class case already set in ZoneOrSort.
- if(qinfo->SkillOrClass < 0)
- {
- sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (class sort case) and `SkillOrClass` = %i (class case), redundant.",
- qinfo->GetQuestId(),qinfo->ZoneOrSort,qinfo->SkillOrClass);
- }
- }
- //check for proper SkillOrClass value (skill case)
- if(int32 skill_id = SkillByQuestSort(-int32(qinfo->ZoneOrSort)))
- {
- // skill is positive value in SkillOrClass
- if(qinfo->SkillOrClass != skill_id )
- {
- sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (skill sort case) but `SkillOrClass` does not have a corresponding value (%i).",
- qinfo->GetQuestId(),qinfo->ZoneOrSort,skill_id);
- //override, and force proper value here?
- }
- }
- }
-
- // SkillOrClass (class case)
- if( qinfo->SkillOrClass < 0 )
- {
- if( !sChrClassesStore.LookupEntry(-int32(qinfo->SkillOrClass)) )
- {
- sLog.outErrorDb("Quest %u has `SkillOrClass` = %i (class case) but class (%i) does not exist",
- qinfo->GetQuestId(),qinfo->SkillOrClass,-qinfo->SkillOrClass);
- }
- }
- // SkillOrClass (skill case)
- if( qinfo->SkillOrClass > 0 )
- {
- if( !sSkillLineStore.LookupEntry(qinfo->SkillOrClass) )
- {
- sLog.outErrorDb("Quest %u has `SkillOrClass` = %u (skill case) but skill (%i) does not exist",
- qinfo->GetQuestId(),qinfo->SkillOrClass,qinfo->SkillOrClass);
- }
- }
-
- if( qinfo->RequiredSkillValue )
- {
- if( qinfo->RequiredSkillValue > sWorld.GetConfigMaxSkillValue() )
- {
- sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but max possible skill is %u, quest can't be done.",
- qinfo->GetQuestId(),qinfo->RequiredSkillValue,sWorld.GetConfigMaxSkillValue());
- // no changes, quest can't be done for this requirement
- }
-
- if( qinfo->SkillOrClass <= 0 )
- {
- sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but `SkillOrClass` = %i (class case), value ignored.",
- qinfo->GetQuestId(),qinfo->RequiredSkillValue,qinfo->SkillOrClass);
- // no changes, quest can't be done for this requirement (fail at wrong skill id)
- }
- }
- // else Skill quests can have 0 skill level, this is ok
-
- if(qinfo->RepObjectiveFaction && !sFactionStore.LookupEntry(qinfo->RepObjectiveFaction))
- {
- sLog.outErrorDb("Quest %u has `RepObjectiveFaction` = %u but faction template %u does not exist, quest can't be done.",
- qinfo->GetQuestId(),qinfo->RepObjectiveFaction,qinfo->RepObjectiveFaction);
- // no changes, quest can't be done for this requirement
- }
-
- if(qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
- {
- sLog.outErrorDb("Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
- qinfo->GetQuestId(),qinfo->RequiredMinRepFaction,qinfo->RequiredMinRepFaction);
- // no changes, quest can't be done for this requirement
- }
-
- if(qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
- {
- sLog.outErrorDb("Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
- qinfo->GetQuestId(),qinfo->RequiredMaxRepFaction,qinfo->RequiredMaxRepFaction);
- // no changes, quest can't be done for this requirement
- }
-
- if(qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > Player::Reputation_Cap)
- {
- sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
- qinfo->GetQuestId(),qinfo->RequiredMinRepValue,Player::Reputation_Cap);
- // no changes, quest can't be done for this requirement
- }
-
- if(qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
- {
- sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
- qinfo->GetQuestId(),qinfo->RequiredMaxRepValue,qinfo->RequiredMinRepValue);
- // no changes, quest can't be done for this requirement
- }
-
- if(!qinfo->RepObjectiveFaction && qinfo->RepObjectiveValue > 0 )
- {
- sLog.outErrorDb("Quest %u has `RepObjectiveValue` = %d but `RepObjectiveFaction` is 0, value has no effect",
- qinfo->GetQuestId(),qinfo->RepObjectiveValue);
- // warning
- }
-
- if(!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue > 0 )
- {
- sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
- qinfo->GetQuestId(),qinfo->RequiredMinRepValue);
- // warning
- }
-
- if(!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue > 0 )
- {
- sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
- qinfo->GetQuestId(),qinfo->RequiredMaxRepValue);
- // warning
- }
-
- if(qinfo->CharTitleId && !sCharTitlesStore.LookupEntry(qinfo->CharTitleId))
- {
- sLog.outErrorDb("Quest %u has `CharTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.",
- qinfo->GetQuestId(),qinfo->GetCharTitleId(),qinfo->GetCharTitleId());
- qinfo->CharTitleId = 0;
- // quest can't reward this title
- }
-
- if(qinfo->SrcItemId)
- {
- if(!sItemStorage.LookupEntry<ItemPrototype>(qinfo->SrcItemId))
- {
- sLog.outErrorDb("Quest %u has `SrcItemId` = %u but item with entry %u does not exist, quest can't be done.",
- qinfo->GetQuestId(),qinfo->SrcItemId,qinfo->SrcItemId);
- qinfo->SrcItemId = 0; // quest can't be done for this requirement
- }
- else if(qinfo->SrcItemCount==0)
- {
- sLog.outErrorDb("Quest %u has `SrcItemId` = %u but `SrcItemCount` = 0, set to 1 but need fix in DB.",
- qinfo->GetQuestId(),qinfo->SrcItemId);
- qinfo->SrcItemCount = 1; // update to 1 for allow quest work for backward comptibility with DB
- }
- }
- else if(qinfo->SrcItemCount>0)
- {
- sLog.outErrorDb("Quest %u has `SrcItemId` = 0 but `SrcItemCount` = %u, useless value.",
- qinfo->GetQuestId(),qinfo->SrcItemCount);
- qinfo->SrcItemCount=0; // no quest work changes in fact
- }
-
- if(qinfo->SrcSpell)
- {
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->SrcSpell);
- if(!spellInfo)
- {
- sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u doesn't exist, quest can't be done.",
- qinfo->GetQuestId(),qinfo->SrcSpell,qinfo->SrcSpell);
- qinfo->SrcSpell = 0; // quest can't be done for this requirement
- }
- else if(!SpellMgr::IsSpellValid(spellInfo))
- {
- sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u is broken, quest can't be done.",
- qinfo->GetQuestId(),qinfo->SrcSpell,qinfo->SrcSpell);
- qinfo->SrcSpell = 0; // quest can't be done for this requirement
- }
- }
-
- for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
- {
- uint32 id = qinfo->ReqItemId[j];
- if(id)
- {
- if(qinfo->ReqItemCount[j]==0)
- {
- sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but `ReqItemCount%d` = 0, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,j+1);
- // no changes, quest can't be done for this requirement
- }
-
- qinfo->SetFlag(QUEST_MANGOS_FLAGS_DELIVER);
-
- if(!sItemStorage.LookupEntry<ItemPrototype>(id))
- {
- sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,id);
- qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
- }
- }
- else if(qinfo->ReqItemCount[j]>0)
- {
- sLog.outErrorDb("Quest %u has `ReqItemId%d` = 0 but `ReqItemCount%d` = %u, quest can't be done.",
- qinfo->GetQuestId(),j+1,j+1,qinfo->ReqItemCount[j]);
- qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
- }
- }
-
- for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j )
- {
- uint32 id = qinfo->ReqSourceId[j];
- if(id)
- {
- if(!sItemStorage.LookupEntry<ItemPrototype>(id))
- {
- sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but item with entry %u does not exist, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,id);
- // no changes, quest can't be done for this requirement
- }
-
- if(!qinfo->ReqSourceCount[j])
- {
- sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceCount%d` = 0, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,j+1);
- qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest
- }
-
- if(!qinfo->ReqSourceRef[j])
- {
- sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceRef%d` = 0, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,j+1);
- qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest
- }
- }
- else
- {
- if(qinfo->ReqSourceCount[j]>0)
- {
- sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceCount%d` = %u.",
- qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceCount[j]);
- // no changes, quest ignore this data
- }
-
- if(qinfo->ReqSourceRef[j]>0)
- {
- sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceRef%d` = %u.",
- qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceRef[j]);
- // no changes, quest ignore this data
- }
- }
- }
-
- for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j )
- {
- uint32 ref = qinfo->ReqSourceRef[j];
- if(ref)
- {
- if(ref > QUEST_OBJECTIVES_COUNT)
- {
- sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but max value in `ReqSourceRef%d` is %u, quest can't be done.",
- qinfo->GetQuestId(),j+1,ref,j+1,QUEST_OBJECTIVES_COUNT);
- // no changes, quest can't be done for this requirement
- }
- else
- if(!qinfo->ReqItemId[ref-1] && !qinfo->ReqSpell[ref-1])
- {
- sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but `ReqItemId%u` = 0 and `ReqSpellCast%u` = 0, quest can't be done.",
- qinfo->GetQuestId(),j+1,ref,ref,ref);
- // no changes, quest can't be done for this requirement
- }
- else if(qinfo->ReqItemId[ref-1] && qinfo->ReqSpell[ref-1])
- {
- sLog.outErrorDb("Quest %u has `ReqItemId%u` = %u and `ReqSpellCast%u` = %u, quest can't have both fields <> 0, then can't be done.",
- qinfo->GetQuestId(),ref,qinfo->ReqItemId[ref-1],ref,qinfo->ReqSpell[ref-1]);
- // no changes, quest can't be done for this requirement
- qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest
- }
- }
- }
-
- for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
- {
- uint32 id = qinfo->ReqSpell[j];
- if(id)
- {
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
- if(!spellInfo)
- {
- sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,id);
- // no changes, quest can't be done for this requirement
- }
-
- if(!qinfo->ReqCreatureOrGOId[j])
- {
- bool found = false;
- for(int k = 0; k < 3; ++k)
- {
- if( spellInfo->Effect[k]==SPELL_EFFECT_QUEST_COMPLETE && uint32(spellInfo->EffectMiscValue[k])==qinfo->QuestId ||
- spellInfo->Effect[k]==SPELL_EFFECT_SEND_EVENT)
- {
- found = true;
- break;
- }
- }
-
- if(found)
- {
- if(!qinfo->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
- {
- sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT for quest %u and ReqCreatureOrGOId%d = 0, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Quest flags or ReqCreatureOrGOId%d must be fixed, quest modified to enable objective.",spellInfo->Id,qinfo->QuestId,j+1,j+1);
-
- // this will prevent quest completing without objective
- const_cast<Quest*>(qinfo)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
- }
- }
- else
- {
- sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u and ReqCreatureOrGOId%d = 0 but spell %u does not have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT effect for this quest, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,j+1,id);
- // no changes, quest can't be done for this requirement
- }
- }
- }
- }
-
- for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
- {
- int32 id = qinfo->ReqCreatureOrGOId[j];
- if(id < 0 && !sGOStorage.LookupEntry<GameObjectInfo>(-id))
- {
- sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but gameobject %u does not exist, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,uint32(-id));
- qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
- }
-
- if(id > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(id))
- {
- sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but creature with entry %u does not exist, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,uint32(id));
- qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
- }
-
- if(id)
- {
- // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
-
- qinfo->SetFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO);
-
- if(!qinfo->ReqCreatureOrGOCount[j])
- {
- sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %u but `ReqCreatureOrGOCount%d` = 0, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,j+1);
- // no changes, quest can be incorrectly done, but we already report this
- }
- }
- else if(qinfo->ReqCreatureOrGOCount[j]>0)
- {
- sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = 0 but `ReqCreatureOrGOCount%d` = %u.",
- qinfo->GetQuestId(),j+1,j+1,qinfo->ReqCreatureOrGOCount[j]);
- // no changes, quest ignore this data
- }
- }
-
- for(int j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j )
- {
- uint32 id = qinfo->RewChoiceItemId[j];
- if(id)
- {
- if(!sItemStorage.LookupEntry<ItemPrototype>(id))
- {
- sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
- qinfo->GetQuestId(),j+1,id,id);
- qinfo->RewChoiceItemId[j] = 0; // no changes, quest will not reward this
- }
-
- if(!qinfo->RewChoiceItemCount[j])
- {
- sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but `RewChoiceItemCount%d` = 0, quest can't be done.",
- qinfo->GetQuestId(),j+1,id,j+1);
- // no changes, quest can't be done
- }
- }
- else if(qinfo->RewChoiceItemCount[j]>0)
- {
- sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemCount%d` = %u.",
- qinfo->GetQuestId(),j+1,j+1,qinfo->RewChoiceItemCount[j]);
- // no changes, quest ignore this data
- }
- }
-
- for(int j = 0; j < QUEST_REWARDS_COUNT; ++j )
- {
- uint32 id = qinfo->RewItemId[j];
- if(id)
- {
- if(!sItemStorage.LookupEntry<ItemPrototype>(id))
- {
- sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
- qinfo->GetQuestId(),j+1,id,id);
- qinfo->RewItemId[j] = 0; // no changes, quest will not reward this item
- }
-
- if(!qinfo->RewItemCount[j])
- {
- sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but `RewItemCount%d` = 0, quest will not reward this item.",
- qinfo->GetQuestId(),j+1,id,j+1);
- // no changes
- }
- }
- else if(qinfo->RewItemCount[j]>0)
- {
- sLog.outErrorDb("Quest %u has `RewItemId%d` = 0 but `RewItemCount%d` = %u.",
- qinfo->GetQuestId(),j+1,j+1,qinfo->RewItemCount[j]);
- // no changes, quest ignore this data
- }
- }
-
- for(int j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
- {
- if(qinfo->RewRepFaction[j])
- {
- if(!qinfo->RewRepValue[j])
- {
- sLog.outErrorDb("Quest %u has `RewRepFaction%d` = %u but `RewRepValue%d` = 0, quest will not reward this reputation.",
- qinfo->GetQuestId(),j+1,qinfo->RewRepValue[j],j+1);
- // no changes
- }
-
- if(!sFactionStore.LookupEntry(qinfo->RewRepFaction[j]))
- {
- sLog.outErrorDb("Quest %u has `RewRepFaction%d` = %u but raw faction (faction.dbc) %u does not exist, quest will not reward reputation for this faction.",
- qinfo->GetQuestId(),j+1,qinfo->RewRepFaction[j] ,qinfo->RewRepFaction[j] );
- qinfo->RewRepFaction[j] = 0; // quest will not reward this
- }
- }
- else if(qinfo->RewRepValue[j]!=0)
- {
- sLog.outErrorDb("Quest %u has `RewRepFaction%d` = 0 but `RewRepValue%d` = %u.",
- qinfo->GetQuestId(),j+1,j+1,qinfo->RewRepValue[j]);
- // no changes, quest ignore this data
- }
- }
-
- if(qinfo->RewSpell)
- {
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpell);
-
- if(!spellInfo)
- {
- sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u does not exist, spell removed as display reward.",
- qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
- qinfo->RewSpell = 0; // no spell reward will display for this quest
- }
-
- else if(!SpellMgr::IsSpellValid(spellInfo))
- {
- sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest can't be done.",
- qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
- qinfo->RewSpell = 0; // no spell reward will display for this quest
- }
-
- }
-
- if(qinfo->RewSpellCast)
- {
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpellCast);
-
- if(!spellInfo)
- {
- sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.",
- qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
- qinfo->RewSpellCast = 0; // no spell will be casted on player
- }
-
- else if(!SpellMgr::IsSpellValid(spellInfo))
- {
- sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest can't be done.",
- qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
- qinfo->RewSpellCast = 0; // no spell will be casted on player
- }
-
- }
-
- if(qinfo->RewMailTemplateId)
- {
- if(!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
- {
- sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
- qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId);
- qinfo->RewMailTemplateId = 0; // no mail will send to player
- qinfo->RewMailDelaySecs = 0; // no mail will send to player
- }
- }
-
- if(qinfo->NextQuestInChain)
- {
- if(mQuestTemplates.find(qinfo->NextQuestInChain) == mQuestTemplates.end())
- {
- sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.",
- qinfo->GetQuestId(),qinfo->NextQuestInChain ,qinfo->NextQuestInChain );
- qinfo->NextQuestInChain = 0;
- }
- else
- mQuestTemplates[qinfo->NextQuestInChain]->prevChainQuests.push_back(qinfo->GetQuestId());
- }
-
- // fill additional data stores
- if(qinfo->PrevQuestId)
- {
- if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
- {
- sLog.outErrorDb("Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
- }
- else
- {
- qinfo->prevQuests.push_back(qinfo->PrevQuestId);
- }
- }
-
- if(qinfo->NextQuestId)
- {
- if (mQuestTemplates.find(abs(qinfo->GetNextQuestId())) == mQuestTemplates.end())
- {
- sLog.outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
- }
- else
- {
- int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
- mQuestTemplates[abs(qinfo->GetNextQuestId())]->prevQuests.push_back(signedQuestId);
- }
- }
-
- if(qinfo->ExclusiveGroup)
- mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
- if(qinfo->LimitTime)
- qinfo->SetFlag(QUEST_MANGOS_FLAGS_TIMED);
- }
-
- // check QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
- for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
- {
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(i);
- if(!spellInfo)
- continue;
-
- for(int j = 0; j < 3; ++j)
- {
- if(spellInfo->Effect[j] != SPELL_EFFECT_QUEST_COMPLETE)
- continue;
-
- uint32 quest_id = spellInfo->EffectMiscValue[j];
-
- Quest const* quest = GetQuestTemplate(quest_id);
-
- // some quest referenced in spells not exist (outdataed spells)
- if(!quest)
- continue;
-
- if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
- {
- sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u , but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.",spellInfo->Id,quest_id);
-
- // this will prevent quest completing without objective
- const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
- }
- }
- }
-
- sLog.outString();
- sLog.outString( ">> Loaded %u quests definitions", mQuestTemplates.size() );
-}
-
-void ObjectMgr::LoadQuestLocales()
-{
- QueryResult *result = WorldDatabase.Query("SELECT entry,"
- "Title_loc1,Details_loc1,Objectives_loc1,OfferRewardText_loc1,RequestItemsText_loc1,EndText_loc1,ObjectiveText1_loc1,ObjectiveText2_loc1,ObjectiveText3_loc1,ObjectiveText4_loc1,"
- "Title_loc2,Details_loc2,Objectives_loc2,OfferRewardText_loc2,RequestItemsText_loc2,EndText_loc2,ObjectiveText1_loc2,ObjectiveText2_loc2,ObjectiveText3_loc2,ObjectiveText4_loc2,"
- "Title_loc3,Details_loc3,Objectives_loc3,OfferRewardText_loc3,RequestItemsText_loc3,EndText_loc3,ObjectiveText1_loc3,ObjectiveText2_loc3,ObjectiveText3_loc3,ObjectiveText4_loc3,"
- "Title_loc4,Details_loc4,Objectives_loc4,OfferRewardText_loc4,RequestItemsText_loc4,EndText_loc4,ObjectiveText1_loc4,ObjectiveText2_loc4,ObjectiveText3_loc4,ObjectiveText4_loc4,"
- "Title_loc5,Details_loc5,Objectives_loc5,OfferRewardText_loc5,RequestItemsText_loc5,EndText_loc5,ObjectiveText1_loc5,ObjectiveText2_loc5,ObjectiveText3_loc5,ObjectiveText4_loc5,"
- "Title_loc6,Details_loc6,Objectives_loc6,OfferRewardText_loc6,RequestItemsText_loc6,EndText_loc6,ObjectiveText1_loc6,ObjectiveText2_loc6,ObjectiveText3_loc6,ObjectiveText4_loc6,"
- "Title_loc7,Details_loc7,Objectives_loc7,OfferRewardText_loc7,RequestItemsText_loc7,EndText_loc7,ObjectiveText1_loc7,ObjectiveText2_loc7,ObjectiveText3_loc7,ObjectiveText4_loc7,"
- "Title_loc8,Details_loc8,Objectives_loc8,OfferRewardText_loc8,RequestItemsText_loc8,EndText_loc8,ObjectiveText1_loc8,ObjectiveText2_loc8,ObjectiveText3_loc8,ObjectiveText4_loc8"
- " FROM locales_quest"
- );
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString("");
- sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 entry = fields[0].GetUInt32();
-
- QuestLocale& data = mQuestLocaleMap[entry];
-
- for(int i = 1; i < MAX_LOCALE; ++i)
- {
- std::string str = fields[1+10*(i-1)].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.Title.size() <= idx)
- data.Title.resize(idx+1);
-
- data.Title[idx] = str;
- }
- }
- str = fields[1+10*(i-1)+1].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.Details.size() <= idx)
- data.Details.resize(idx+1);
-
- data.Details[idx] = str;
- }
- }
- str = fields[1+10*(i-1)+2].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.Objectives.size() <= idx)
- data.Objectives.resize(idx+1);
-
- data.Objectives[idx] = str;
- }
- }
- str = fields[1+10*(i-1)+3].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.OfferRewardText.size() <= idx)
- data.OfferRewardText.resize(idx+1);
-
- data.OfferRewardText[idx] = str;
- }
- }
- str = fields[1+10*(i-1)+4].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.RequestItemsText.size() <= idx)
- data.RequestItemsText.resize(idx+1);
-
- data.RequestItemsText[idx] = str;
- }
- }
- str = fields[1+10*(i-1)+5].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.EndText.size() <= idx)
- data.EndText.resize(idx+1);
-
- data.EndText[idx] = str;
- }
- }
- for(int k = 0; k < 4; ++k)
- {
- str = fields[1+10*(i-1)+6+k].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.ObjectiveText[k].size() <= idx)
- data.ObjectiveText[k].resize(idx+1);
-
- data.ObjectiveText[k][idx] = str;
- }
- }
- }
- }
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u Quest locale strings", mQuestLocaleMap.size() );
-}
-
-void ObjectMgr::LoadPetCreateSpells()
-{
- QueryResult *result = WorldDatabase.PQuery("SELECT entry, Spell1, Spell2, Spell3, Spell4 FROM petcreateinfo_spell");
- if(!result)
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded 0 pet create spells" );
- sLog.outErrorDb("`petcreateinfo_spell` table is empty!");
- return;
- }
-
- uint32 count = 0;
-
- barGoLink bar( result->GetRowCount() );
-
- mPetCreateSpell.clear();
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 creature_id = fields[0].GetUInt32();
-
- if(!creature_id || !sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
- continue;
-
- PetCreateSpellEntry PetCreateSpell;
- for(int i = 0; i < 4; i++)
- {
- PetCreateSpell.spellid[i] = fields[i + 1].GetUInt32();
-
- if(PetCreateSpell.spellid[i] && !sSpellStore.LookupEntry(PetCreateSpell.spellid[i]))
- sLog.outErrorDb("Spell %u listed in `petcreateinfo_spell` does not exist",PetCreateSpell.spellid[i]);
- }
-
- mPetCreateSpell[creature_id] = PetCreateSpell;
-
- ++count;
- }
- while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u pet create spells", count );
-}
-
-void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
-{
- if(sWorld.IsScriptScheduled()) // function don't must be called in time scripts use.
- return;
-
- sLog.outString( "%s :", tablename);
-
- scripts.clear(); // need for reload support
-
- QueryResult *result = WorldDatabase.PQuery( "SELECT id,delay,command,datalong,datalong2,datatext, x, y, z, o FROM %s", tablename );
-
- uint32 count = 0;
-
- if( !result )
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u script definitions", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- bar.step();
-
- Field *fields = result->Fetch();
- ScriptInfo tmp;
- tmp.id = fields[0].GetUInt32();
- tmp.delay = fields[1].GetUInt32();
- tmp.command = fields[2].GetUInt32();
- tmp.datalong = fields[3].GetUInt32();
- tmp.datalong2 = fields[4].GetUInt32();
- tmp.datatext = fields[5].GetCppString();
- tmp.x = fields[6].GetFloat();
- tmp.y = fields[7].GetFloat();
- tmp.z = fields[8].GetFloat();
- tmp.o = fields[9].GetFloat();
-
- // generic command args check
- switch(tmp.command)
- {
- case SCRIPT_COMMAND_TALK:
- {
- if(tmp.datalong > 3)
- {
- sLog.outErrorDb("Table `%s` has invalid talk type (datalong = %u) in SCRIPT_COMMAND_TALK for script id %u",tablename,tmp.datalong,tmp.id);
- continue;
- }
- break;
- }
-
- case SCRIPT_COMMAND_TELEPORT_TO:
- {
- if(!sMapStore.LookupEntry(tmp.datalong))
- {
- sLog.outErrorDb("Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",tablename,tmp.datalong,tmp.id);
- continue;
- }
-
- if(!MaNGOS::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
- {
- sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",tablename,tmp.x,tmp.y,tmp.id);
- continue;
- }
- break;
- }
-
- case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
- {
- if(!MaNGOS::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
- {
- sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",tablename,tmp.x,tmp.y,tmp.id);
- continue;
- }
-
- if(!GetCreatureTemplate(tmp.datalong))
- {
- sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",tablename,tmp.datalong,tmp.id);
- continue;
- }
- break;
- }
-
- case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
- {
- GameObjectData const* data = GetGOData(tmp.datalong);
- if(!data)
- {
- sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,tmp.datalong,tmp.id);
- continue;
- }
-
- GameObjectInfo const* info = GetGameObjectInfo(data->id);
- if(!info)
- {
- sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,tmp.datalong,data->id,tmp.id);
- continue;
- }
-
- if( info->type==GAMEOBJECT_TYPE_FISHINGNODE ||
- info->type==GAMEOBJECT_TYPE_FISHINGHOLE ||
- info->type==GAMEOBJECT_TYPE_DOOR ||
- info->type==GAMEOBJECT_TYPE_BUTTON ||
- info->type==GAMEOBJECT_TYPE_TRAP )
- {
- sLog.outErrorDb("Table `%s` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,info->id,tmp.id);
- continue;
- }
- break;
- }
- case SCRIPT_COMMAND_OPEN_DOOR:
- case SCRIPT_COMMAND_CLOSE_DOOR:
- {
- GameObjectData const* data = GetGOData(tmp.datalong);
- if(!data)
- {
- sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u",tablename,tmp.datalong,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
- continue;
- }
-
- GameObjectInfo const* info = GetGameObjectInfo(data->id);
- if(!info)
- {
- sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u",tablename,tmp.datalong,data->id,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
- continue;
- }
-
- if( info->type!=GAMEOBJECT_TYPE_DOOR)
- {
- sLog.outErrorDb("Table `%s` has gameobject type (%u) non supported by command %s for script id %u",tablename,info->id,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
- continue;
- }
-
- break;
- }
- case SCRIPT_COMMAND_QUEST_EXPLORED:
- {
- Quest const* quest = GetQuestTemplate(tmp.datalong);
- if(!quest)
- {
- sLog.outErrorDb("Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",tablename,tmp.datalong,tmp.id);
- continue;
- }
-
- if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
- {
- sLog.outErrorDb("Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",tablename,tmp.datalong,tmp.id);
-
- // this will prevent quest completing without objective
- const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
-
- // continue; - quest objective requiremet set and command can be allowed
- }
-
- if(float(tmp.datalong2) > DEFAULT_VISIBILITY_DISTANCE)
- {
- sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",tablename,tmp.datalong2,tmp.id);
- continue;
- }
-
- if(tmp.datalong2 && float(tmp.datalong2) > DEFAULT_VISIBILITY_DISTANCE)
- {
- sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, max distance is %u or 0 for disable distance check",tablename,tmp.datalong2,tmp.id,uint32(DEFAULT_VISIBILITY_DISTANCE));
- continue;
- }
-
- if(tmp.datalong2 && float(tmp.datalong2) < INTERACTION_DISTANCE)
- {
- sLog.outErrorDb("Table `%s` has too small distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, min distance is %u or 0 for disable distance check",tablename,tmp.datalong2,tmp.id,uint32(INTERACTION_DISTANCE));
- continue;
- }
-
- break;
- }
-
- case SCRIPT_COMMAND_REMOVE_AURA:
- case SCRIPT_COMMAND_CAST_SPELL:
- {
- if(!sSpellStore.LookupEntry(tmp.datalong))
- {
- sLog.outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u",tablename,tmp.datalong,tmp.id);
- continue;
- }
- break;
- }
- }
-
- if (scripts.find(tmp.id) == scripts.end())
- {
- ScriptMap emptyMap;
- scripts[tmp.id] = emptyMap;
- }
- scripts[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
-
- ++count;
- } while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u script definitions", count );
-}
-
-void ObjectMgr::LoadGameObjectScripts()
-{
- LoadScripts(sGameObjectScripts, "gameobject_scripts");
-
- // check ids
- for(ScriptMapMap::const_iterator itr = sGameObjectScripts.begin(); itr != sGameObjectScripts.end(); ++itr)
- {
- if(!GetGOData(itr->first))
- sLog.outErrorDb("Table `gameobject_scripts` has not existing gameobject (GUID: %u) as script id",itr->first);
- }
-}
-
-void ObjectMgr::LoadQuestEndScripts()
-{
- LoadScripts(sQuestEndScripts, "quest_end_scripts");
-
- // check ids
- for(ScriptMapMap::const_iterator itr = sQuestEndScripts.begin(); itr != sQuestEndScripts.end(); ++itr)
- {
- if(!GetQuestTemplate(itr->first))
- sLog.outErrorDb("Table `quest_end_scripts` has not existing quest (Id: %u) as script id",itr->first);
- }
-}
-
-void ObjectMgr::LoadQuestStartScripts()
-{
- LoadScripts(sQuestStartScripts,"quest_start_scripts");
-
- // check ids
- for(ScriptMapMap::const_iterator itr = sQuestStartScripts.begin(); itr != sQuestStartScripts.end(); ++itr)
- {
- if(!GetQuestTemplate(itr->first))
- sLog.outErrorDb("Table `quest_start_scripts` has not existing quest (Id: %u) as script id",itr->first);
- }
-}
-
-void ObjectMgr::LoadSpellScripts()
-{
- LoadScripts(sSpellScripts, "spell_scripts");
-
- // check ids
- for(ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
- {
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first);
-
- if(!spellInfo)
- {
- sLog.outErrorDb("Table `spell_scripts` has not existing spell (Id: %u) as script id",itr->first);
- continue;
- }
-
- //check for correct spellEffect
- bool found = false;
- for(int i=0; i<3; ++i)
- {
- // skip empty effects
- if( !spellInfo->Effect[i] )
- continue;
-
- if( spellInfo->Effect[i] == SPELL_EFFECT_SCRIPT_EFFECT )
- {
- found = true;
- break;
- }
- }
-
- if(!found)
- sLog.outErrorDb("Table `spell_scripts` has unsupported spell (Id: %u) without SPELL_EFFECT_SCRIPT_EFFECT (%u) spell effect",itr->first,SPELL_EFFECT_SCRIPT_EFFECT);
- }
-}
-
-void ObjectMgr::LoadEventScripts()
-{
- LoadScripts(sEventScripts, "event_scripts");
-
- std::set<uint32> evt_scripts;
- // Load all possible script entries from gameobjects
- for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i)
- {
- GameObjectInfo const * goInfo = sGOStorage.LookupEntry<GameObjectInfo>(i);
- if (goInfo)
- {
- switch(goInfo->type)
- {
- case GAMEOBJECT_TYPE_GOOBER:
- if(goInfo->goober.eventId)
- evt_scripts.insert(goInfo->goober.eventId);
- break;
- case GAMEOBJECT_TYPE_CHEST:
- if(goInfo->chest.eventId)
- evt_scripts.insert(goInfo->chest.eventId);
- break;
- default:
- break;
- }
- }
- }
- // Load all possible script entries from spells
- for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
- {
- SpellEntry const * spell = sSpellStore.LookupEntry(i);
- if (spell)
- {
- for(int j=0; j<3; ++j)
- {
- if( spell->Effect[j] == SPELL_EFFECT_SEND_EVENT )
- {
- if (spell->EffectMiscValue[j])
- evt_scripts.insert(spell->EffectMiscValue[j]);
- }
- }
- }
- }
- // Then check if all scripts are in above list of possible script entries
- for(ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
- {
- std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
- if (itr2 == evt_scripts.end())
- sLog.outErrorDb("Table `event_scripts` has script (Id: %u) not refering to any gameobject_template type 10 data2 field or type 3 data6 field or any spell effect %u", itr->first, SPELL_EFFECT_SEND_EVENT);
- }
-}
-
-void ObjectMgr::LoadItemTexts()
-{
- QueryResult *result = CharacterDatabase.PQuery("SELECT id, text FROM item_text");
-
- uint32 count = 0;
-
- if( !result )
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u item pages", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- Field* fields;
- do
- {
- bar.step();
-
- fields = result->Fetch();
-
- mItemTexts[ fields[0].GetUInt32() ] = fields[1].GetCppString();
-
- ++count;
-
- } while ( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u item texts", count );
-}
-
-void ObjectMgr::LoadPageTexts()
-{
- sPageTextStore.Free(); // for reload case
-
- sPageTextStore.Load();
- sLog.outString( ">> Loaded %u page texts", sPageTextStore.RecordCount );
- sLog.outString();
-
- for(uint32 i = 1; i < sPageTextStore.MaxEntry; ++i)
- {
- // check data correctness
- PageText const* page = sPageTextStore.LookupEntry<PageText>(i);
- if(!page)
- continue;
-
- if(page->Next_Page && !sPageTextStore.LookupEntry<PageText>(page->Next_Page))
- {
- sLog.outErrorDb("Page text (Id: %u) has not existing next page (Id:%u)", i,page->Next_Page);
- continue;
- }
-
- // detect circular reference
- std::set<uint32> checkedPages;
- for(PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry<PageText>(pageItr->Next_Page))
- {
- if(!pageItr->Next_Page)
- break;
- checkedPages.insert(pageItr->Page_ID);
- if(checkedPages.find(pageItr->Next_Page)!=checkedPages.end())
- {
- std::ostringstream ss;
- ss<< "The text page(s) ";
- for (std::set<uint32>::iterator itr= checkedPages.begin();itr!=checkedPages.end(); itr++)
- ss << *itr << " ";
- ss << "create(s) a circular reference, which can cause the server to freeze. Changing Next_Page of page "
- << pageItr->Page_ID <<" to 0";
- sLog.outErrorDb(ss.str().c_str());
- const_cast<PageText*>(pageItr)->Next_Page = 0;
- break;
- }
- }
- }
-}
-
-void ObjectMgr::LoadPageTextLocales()
-{
- QueryResult *result = WorldDatabase.PQuery("SELECT entry,text_loc1,text_loc2,text_loc3,text_loc4,text_loc5,text_loc6,text_loc7,text_loc8 FROM locales_page_text");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString("");
- sLog.outString(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 entry = fields[0].GetUInt32();
-
- PageTextLocale& data = mPageTextLocaleMap[entry];
-
- for(int i = 1; i < MAX_LOCALE; ++i)
- {
- std::string str = fields[i].GetCppString();
- if(str.empty())
- continue;
-
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.Text.size() <= idx)
- data.Text.resize(idx+1);
-
- data.Text[idx] = str;
- }
- }
-
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u PageText locale strings", mPageTextLocaleMap.size() );
-}
-
-void ObjectMgr::LoadInstanceTemplate()
-{
- sInstanceTemplate.Load();
-
- for(uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++)
- {
- InstanceTemplate* temp = (InstanceTemplate*)GetInstanceTemplate(i);
- if(!temp) continue;
- const MapEntry* entry = sMapStore.LookupEntry(temp->map);
- if(!entry)
- {
- sLog.outErrorDb("ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", temp->map);
- continue;
- }
- else if(!entry->HasResetTime())
- continue;
-
- if(temp->reset_delay == 0)
- {
- // use defaults from the DBC
- if(entry->SupportsHeroicMode())
- {
- temp->reset_delay = entry->resetTimeHeroic / DAY;
- }
- else if (entry->resetTimeRaid && entry->map_type == MAP_RAID)
- {
- temp->reset_delay = entry->resetTimeRaid / DAY;
- }
- }
-
- // the reset_delay must be atleast one day
- temp->reset_delay = std::max((uint32)1, (uint32)(temp->reset_delay * sWorld.getRate(RATE_INSTANCE_RESET_TIME)));
- }
-
- sLog.outString( ">> Loaded %u Instance Template definitions", sInstanceTemplate.RecordCount );
- sLog.outString();
-}
-
-void ObjectMgr::AddGossipText(GossipText *pGText)
-{
- ASSERT( pGText->Text_ID );
- ASSERT( mGossipText.find(pGText->Text_ID) == mGossipText.end() );
- mGossipText[pGText->Text_ID] = pGText;
-}
-
-GossipText *ObjectMgr::GetGossipText(uint32 Text_ID)
-{
- GossipTextMap::const_iterator itr;
- for (itr = mGossipText.begin(); itr != mGossipText.end(); itr++)
- {
- if(itr->second->Text_ID == Text_ID)
- return itr->second;
- }
- return NULL;
-}
-
-void ObjectMgr::LoadGossipText()
-{
- GossipText *pGText;
- QueryResult *result = WorldDatabase.Query( "SELECT * FROM npc_text" );
-
- int count = 0;
- if( !result )
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u npc texts", count );
- return;
- }
-
- int cic;
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- ++count;
- cic = 0;
-
- Field *fields = result->Fetch();
-
- bar.step();
-
- pGText = new GossipText;
- pGText->Text_ID = fields[cic++].GetUInt32();
-
- for (int i=0; i< 8; i++)
- {
- pGText->Options[i].Text_0 = fields[cic++].GetCppString();
- pGText->Options[i].Text_1 = fields[cic++].GetCppString();
-
- pGText->Options[i].Language = fields[cic++].GetUInt32();
- pGText->Options[i].Probability = fields[cic++].GetFloat();
-
- pGText->Options[i].Emotes[0]._Delay = fields[cic++].GetUInt32();
- pGText->Options[i].Emotes[0]._Emote = fields[cic++].GetUInt32();
-
- pGText->Options[i].Emotes[1]._Delay = fields[cic++].GetUInt32();
- pGText->Options[i].Emotes[1]._Emote = fields[cic++].GetUInt32();
-
- pGText->Options[i].Emotes[2]._Delay = fields[cic++].GetUInt32();
- pGText->Options[i].Emotes[2]._Emote = fields[cic++].GetUInt32();
- }
-
- if ( !pGText->Text_ID ) continue;
- AddGossipText( pGText );
-
- } while( result->NextRow() );
-
- sLog.outString();
- sLog.outString( ">> Loaded %u npc texts", count );
- delete result;
-}
-
-void ObjectMgr::LoadNpcTextLocales()
-{
- QueryResult *result = WorldDatabase.Query("SELECT entry,"
- "Text0_0_loc1,Text0_1_loc1,Text1_0_loc1,Text1_1_loc1,Text2_0_loc1,Text2_1_loc1,Text3_0_loc1,Text3_1_loc1,Text4_0_loc1,Text4_1_loc1,Text5_0_loc1,Text5_1_loc1,Text6_0_loc1,Text6_1_loc1,Text7_0_loc1,Text7_1_loc1,"
- "Text0_0_loc2,Text0_1_loc2,Text1_0_loc2,Text1_1_loc2,Text2_0_loc2,Text2_1_loc2,Text3_0_loc2,Text3_1_loc1,Text4_0_loc2,Text4_1_loc2,Text5_0_loc2,Text5_1_loc2,Text6_0_loc2,Text6_1_loc2,Text7_0_loc2,Text7_1_loc2,"
- "Text0_0_loc3,Text0_1_loc3,Text1_0_loc3,Text1_1_loc3,Text2_0_loc3,Text2_1_loc3,Text3_0_loc3,Text3_1_loc1,Text4_0_loc3,Text4_1_loc3,Text5_0_loc3,Text5_1_loc3,Text6_0_loc3,Text6_1_loc3,Text7_0_loc3,Text7_1_loc3,"
- "Text0_0_loc4,Text0_1_loc4,Text1_0_loc4,Text1_1_loc4,Text2_0_loc4,Text2_1_loc4,Text3_0_loc4,Text3_1_loc1,Text4_0_loc4,Text4_1_loc4,Text5_0_loc4,Text5_1_loc4,Text6_0_loc4,Text6_1_loc4,Text7_0_loc4,Text7_1_loc4,"
- "Text0_0_loc5,Text0_1_loc5,Text1_0_loc5,Text1_1_loc5,Text2_0_loc5,Text2_1_loc5,Text3_0_loc5,Text3_1_loc1,Text4_0_loc5,Text4_1_loc5,Text5_0_loc5,Text5_1_loc5,Text6_0_loc5,Text6_1_loc5,Text7_0_loc5,Text7_1_loc5,"
- "Text0_0_loc6,Text0_1_loc6,Text1_0_loc6,Text1_1_loc6,Text2_0_loc6,Text2_1_loc6,Text3_0_loc6,Text3_1_loc1,Text4_0_loc6,Text4_1_loc6,Text5_0_loc6,Text5_1_loc6,Text6_0_loc6,Text6_1_loc6,Text7_0_loc6,Text7_1_loc6,"
- "Text0_0_loc7,Text0_1_loc7,Text1_0_loc7,Text1_1_loc7,Text2_0_loc7,Text2_1_loc7,Text3_0_loc7,Text3_1_loc1,Text4_0_loc7,Text4_1_loc7,Text5_0_loc7,Text5_1_loc7,Text6_0_loc7,Text6_1_loc7,Text7_0_loc7,Text7_1_loc7, "
- "Text0_0_loc8,Text0_1_loc8,Text1_0_loc8,Text1_1_loc8,Text2_0_loc8,Text2_1_loc8,Text3_0_loc8,Text3_1_loc1,Text4_0_loc8,Text4_1_loc8,Text5_0_loc8,Text5_1_loc8,Text6_0_loc8,Text6_1_loc8,Text7_0_loc8,Text7_1_loc8 "
- " FROM locales_npc_text");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString("");
- sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_npc_text` is empty.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 entry = fields[0].GetUInt32();
-
- NpcTextLocale& data = mNpcTextLocaleMap[entry];
-
- for(int i=1; i<MAX_LOCALE; ++i)
- {
- for(int j=0; j<8; ++j)
- {
- std::string str0 = fields[1+8*2*(i-1)+2*j].GetCppString();
- if(!str0.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.Text_0[j].size() <= idx)
- data.Text_0[j].resize(idx+1);
-
- data.Text_0[j][idx] = str0;
- }
- }
- std::string str1 = fields[1+8*2*(i-1)+2*j+1].GetCppString();
- if(!str1.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.Text_1[j].size() <= idx)
- data.Text_1[j].resize(idx+1);
-
- data.Text_1[j][idx] = str1;
- }
- }
- }
- }
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u NpcText locale strings", mNpcTextLocaleMap.size() );
-}
-
-//not very fast function but it is called only once a day, or on starting-up
-void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
-{
- time_t basetime = time(NULL);
- sLog.outDebug("Returning mails current time: hour: %d, minute: %d, second: %d ", localtime(&basetime)->tm_hour, localtime(&basetime)->tm_min, localtime(&basetime)->tm_sec);
- //delete all old mails without item and without body immediately, if starting server
- if (!serverUp)
- CharacterDatabase.PExecute("DELETE FROM mail WHERE expire_time < '" I64FMTD "' AND has_items = '0' AND itemTextId = 0", (uint64)basetime);
- // 0 1 2 3 4 5 6 7 8 9
- QueryResult* result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,itemTextId,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" I64FMTD "'", (uint64)basetime);
- if ( !result )
- return; // any mails need to be returned or deleted
- Field *fields;
- //std::ostringstream delitems, delmails; //will be here for optimization
- //bool deletemail = false, deleteitem = false;
- //delitems << "DELETE FROM item_instance WHERE guid IN ( ";
- //delmails << "DELETE FROM mail WHERE id IN ( "
- do
- {
- fields = result->Fetch();
- Mail *m = new Mail;
- m->messageID = fields[0].GetUInt32();
- m->messageType = fields[1].GetUInt8();
- m->sender = fields[2].GetUInt32();
- m->receiver = fields[3].GetUInt32();
- m->itemTextId = fields[4].GetUInt32();
- bool has_items = fields[5].GetBool();
- m->expire_time = (time_t)fields[6].GetUInt64();
- m->deliver_time = 0;
- m->COD = fields[7].GetUInt32();
- m->checked = fields[8].GetUInt32();
- m->mailTemplateId = fields[9].GetInt16();
-
- Player *pl = 0;
- if (serverUp)
- pl = GetPlayer((uint64)m->receiver);
- if (pl && pl->m_mailsLoaded)
- { //this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
- //his in mailbox and he has already listed his mails )
- delete m;
- continue;
- }
- //delete or return mail:
- if (has_items)
- {
- QueryResult *resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", m->messageID);
- if(resultItems)
- {
- do
- {
- Field *fields2 = resultItems->Fetch();
-
- uint32 item_guid_low = fields2[0].GetUInt32();
- uint32 item_template = fields2[1].GetUInt32();
-
- m->AddItem(item_guid_low, item_template);
- }
- while (resultItems->NextRow());
-
- delete resultItems;
- }
- //if it is mail from AH, it shouldn't be returned, but deleted
- if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_AUCTION | MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
- {
- // mail open and then not returned
- for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
- CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
- }
- else
- {
- //mail will be returned:
- CharacterDatabase.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" I64FMTD "', deliver_time = '" I64FMTD "',cod = '0', checked = '%u' WHERE id = '%u'", m->receiver, m->sender, (uint64)(basetime + 30*DAY), (uint64)basetime, MAIL_CHECK_MASK_RETURNED, m->messageID);
- delete m;
- continue;
- }
- }
-
- if (m->itemTextId)
- CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", m->itemTextId);
-
- //deletemail = true;
- //delmails << m->messageID << ", ";
- CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
- delete m;
- } while (result->NextRow());
- delete result;
-}
-
-void ObjectMgr::LoadQuestAreaTriggers()
-{
- mQuestAreaTriggerMap.clear(); // need for reload case
-
- QueryResult *result = WorldDatabase.Query( "SELECT id,quest FROM areatrigger_involvedrelation" );
-
- uint32 count = 0;
-
- if( !result )
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u quest trigger points", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- ++count;
- bar.step();
-
- Field *fields = result->Fetch();
-
- uint32 trigger_ID = fields[0].GetUInt32();
- uint32 quest_ID = fields[1].GetUInt32();
-
- AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
- if(!atEntry)
- {
- sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",trigger_ID);
- continue;
- }
-
- Quest const* quest = GetQuestTemplate(quest_ID);
-
- if(!quest)
- {
- sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u",trigger_ID,quest_ID);
- continue;
- }
-
- if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
- {
- sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.",trigger_ID,quest_ID);
-
- // this will prevent quest completing without objective
- const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
-
- // continue; - quest modified to required obkective and trigger can be allowed.
- }
-
- mQuestAreaTriggerMap[trigger_ID] = quest_ID;
-
- } while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u quest trigger points", count );
-}
-
-void ObjectMgr::LoadTavernAreaTriggers()
-{
- mTavernAreaTriggerSet.clear(); // need for reload case
-
- QueryResult *result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
-
- uint32 count = 0;
-
- if( !result )
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u tavern triggers", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- ++count;
- bar.step();
-
- Field *fields = result->Fetch();
-
- uint32 Trigger_ID = fields[0].GetUInt32();
-
- AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
- if(!atEntry)
- {
- sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
- continue;
- }
-
- mTavernAreaTriggerSet.insert(Trigger_ID);
- } while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u tavern triggers", count );
-}
-
-void ObjectMgr::LoadAreaTriggerScripts()
-{
- mAreaTriggerScripts.clear(); // need for reload case
- QueryResult *result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
-
- uint32 count = 0;
-
- if( !result )
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u areatrigger scripts", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- ++count;
- bar.step();
-
- Field *fields = result->Fetch();
-
- uint32 Trigger_ID = fields[0].GetUInt32();
- std::string scriptName = fields[1].GetCppString();
-
- AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
- if(!atEntry)
- {
- sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
- continue;
- }
- mAreaTriggerScripts[Trigger_ID] = scriptName;
- } while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u areatrigger scripts", count );
-}
-uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid )
-{
- bool found = false;
- float dist;
- uint32 id = 0;
-
- for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
- {
- TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
- if(node && node->map_id == mapid)
- {
- float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z);
- if(found)
- {
- if(dist2 < dist)
- {
- dist = dist2;
- id = i;
- }
- }
- else
- {
- found = true;
- dist = dist2;
- id = i;
- }
- }
- }
-
- return id;
-}
-
-void ObjectMgr::GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost)
-{
- TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
- if(src_i==sTaxiPathSetBySource.end())
- {
- path = 0;
- cost = 0;
- return;
- }
-
- TaxiPathSetForSource& pathSet = src_i->second;
-
- TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
- if(dest_i==pathSet.end())
- {
- path = 0;
- cost = 0;
- return;
- }
-
- cost = dest_i->second.price;
- path = dest_i->second.ID;
-}
-
-uint16 ObjectMgr::GetTaxiMount( uint32 id, uint32 team )
-{
- uint16 mount_entry = 0;
- uint16 mount_id = 0;
-
- TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
- if(node)
- {
- if (team == ALLIANCE)
- {
- mount_entry = node->alliance_mount_type;
- CreatureInfo const *ci = GetCreatureTemplate(mount_entry);
- if(ci)
- mount_id = ci->DisplayID_A;
- }
- if (team == HORDE)
- {
- mount_entry = node->horde_mount_type;
- CreatureInfo const *ci = GetCreatureTemplate(mount_entry);
- if(ci)
- mount_id = ci->DisplayID_H;
- }
- }
-
- CreatureModelInfo const *minfo = GetCreatureModelInfo(mount_id);
- if(!minfo)
- {
- sLog.outErrorDb("Taxi mount (Entry: %u) for taxi node (Id: %u) for team %u has model %u not found in table `creature_model_info`, can't load. ",
- mount_entry,id,team,mount_id);
-
- return false;
- }
- if(minfo->modelid_other_gender!=0)
- mount_id = urand(0,1) ? mount_id : minfo->modelid_other_gender;
-
- return mount_id;
-}
-
-void ObjectMgr::GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds)
-{
- if(path >= sTaxiPathNodesByPath.size())
- return;
-
- TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
-
- pathnodes.Resize(nodeList.size());
- mapIds.resize(nodeList.size());
-
- for(size_t i = 0; i < nodeList.size(); ++i)
- {
- pathnodes[ i ].x = nodeList[i].x;
- pathnodes[ i ].y = nodeList[i].y;
- pathnodes[ i ].z = nodeList[i].z;
-
- mapIds[i] = nodeList[i].mapid;
- }
-}
-
-void ObjectMgr::GetTransportPathNodes( uint32 path, TransportPath &pathnodes )
-{
- if(path >= sTaxiPathNodesByPath.size())
- return;
-
- TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
-
- pathnodes.Resize(nodeList.size());
-
- for(size_t i = 0; i < nodeList.size(); ++i)
- {
- pathnodes[ i ].mapid = nodeList[i].mapid;
- pathnodes[ i ].x = nodeList[i].x;
- pathnodes[ i ].y = nodeList[i].y;
- pathnodes[ i ].z = nodeList[i].z;
- pathnodes[ i ].actionFlag = nodeList[i].actionFlag;
- pathnodes[ i ].delay = nodeList[i].delay;
- }
-}
-
-void ObjectMgr::LoadGraveyardZones()
-{
- mGraveYardMap.clear(); // need for reload case
-
- QueryResult *result = WorldDatabase.Query("SELECT id,ghost_zone,faction FROM game_graveyard_zone");
-
- uint32 count = 0;
-
- if( !result )
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u graveyard-zone links", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- ++count;
- bar.step();
-
- Field *fields = result->Fetch();
-
- uint32 safeLocId = fields[0].GetUInt32();
- uint32 zoneId = fields[1].GetUInt32();
- uint32 team = fields[2].GetUInt32();
-
- WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
- if(!entry)
- {
- sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.",safeLocId);
- continue;
- }
-
- AreaTableEntry const *areaEntry = GetAreaEntryByAreaID(zoneId);
- if(!areaEntry)
- {
- sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing zone id (%u), skipped.",zoneId);
- continue;
- }
-
- if(areaEntry->zone != 0)
- {
- sLog.outErrorDb("Table `game_graveyard_zone` has record subzone id (%u) instead of zone, skipped.",zoneId);
- continue;
- }
-
- if(team!=0 && team!=HORDE && team!=ALLIANCE)
- {
- sLog.outErrorDb("Table `game_graveyard_zone` has record for non player faction (%u), skipped.",team);
- continue;
- }
-
- if(entry->map_id != areaEntry->mapid && team != 0)
- {
- sLog.outErrorDb("Table `game_graveyard_zone` has record for ghost zone (%u) at map %u and graveyard (%u) at map %u for team %u, but in case maps are different, player faction setting is ignored. Use faction 0 instead.",zoneId,areaEntry->mapid, safeLocId, entry->map_id, team);
- team = 0;
- }
-
- if(!AddGraveYardLink(safeLocId,zoneId,team,false))
- sLog.outErrorDb("Table `game_graveyard_zone` has a duplicate record for Garveyard (ID: %u) and Zone (ID: %u), skipped.",safeLocId,zoneId);
- } while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u graveyard-zone links", count );
-}
-
-WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team)
-{
- // search for zone associated closest graveyard
- uint32 zoneId = MapManager::Instance().GetZoneId(MapId,x,y);
-
- // Simulate std. algorithm:
- // found some graveyard associated to (ghost_zone,ghost_map)
- //
- // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
- // then check faction
- // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
- // then skip check faction
- GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
- GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
- if(graveLow==graveUp)
- {
- sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team);
- return NULL;
- }
-
- bool foundNear = false;
- float distNear;
- WorldSafeLocsEntry const* entryNear = NULL;
- WorldSafeLocsEntry const* entryFar = NULL;
-
- for(GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
- {
- GraveYardData const& data = itr->second;
-
- WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
- if(!entry)
- {
- sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.",data.safeLocId);
- continue;
- }
-
- // remember first graveyard at another map and ignore other
- if(MapId != entry->map_id)
- {
- if(!entryFar)
- entryFar = entry;
- continue;
- }
-
- // skip enemy faction graveyard at same map (normal area, city, or battleground)
- // team == 0 case can be at call from .neargrave
- if(data.team != 0 && team != 0 && data.team != team)
- continue;
-
- // find now nearest graveyard at same map
- float dist2 = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y)+(entry->z - z)*(entry->z - z);
- if(foundNear)
- {
- if(dist2 < distNear)
- {
- distNear = dist2;
- entryNear = entry;
- }
- }
- else
- {
- foundNear = true;
- distNear = dist2;
- entryNear = entry;
- }
- }
-
- if(entryNear)
- return entryNear;
-
- return entryFar;
-}
-
-GraveYardData const* ObjectMgr::FindGraveYardData(uint32 id, uint32 zoneId)
-{
- GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
- GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
-
- for(GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
- {
- if(itr->second.safeLocId==id)
- return &itr->second;
- }
-
- return NULL;
-}
-
-bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool inDB)
-{
- if(FindGraveYardData(id,zoneId))
- return false;
-
- // add link to loaded data
- GraveYardData data;
- data.safeLocId = id;
- data.team = team;
-
- mGraveYardMap.insert(GraveYardMap::value_type(zoneId,data));
-
- // add link to DB
- if(inDB)
- {
- WorldDatabase.PExecuteLog("INSERT INTO game_graveyard_zone ( id,ghost_zone,faction) "
- "VALUES ('%u', '%u','%u')",id,zoneId,team);
- }
-
- return true;
-}
-
-void ObjectMgr::LoadAreaTriggerTeleports()
-{
- mAreaTriggers.clear(); // need for reload case
-
- uint32 count = 0;
-
- // 0 1 2 3 4 5 6 7 8 9 10 11 12
- QueryResult *result = WorldDatabase.Query("SELECT id, required_level, required_item, required_item2, heroic_key, heroic_key2, required_quest_done, required_failed_text, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
- if( !result )
- {
-
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u area trigger teleport definitions", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- Field *fields = result->Fetch();
-
- bar.step();
-
- ++count;
-
- uint32 Trigger_ID = fields[0].GetUInt32();
-
- AreaTrigger at;
-
- at.requiredLevel = fields[1].GetUInt8();
- at.requiredItem = fields[2].GetUInt32();
- at.requiredItem2 = fields[3].GetUInt32();
- at.heroicKey = fields[4].GetUInt32();
- at.heroicKey2 = fields[5].GetUInt32();
- at.requiredQuest = fields[6].GetUInt32();
- at.requiredFailedText = fields[7].GetCppString();
- at.target_mapId = fields[8].GetUInt32();
- at.target_X = fields[9].GetFloat();
- at.target_Y = fields[10].GetFloat();
- at.target_Z = fields[11].GetFloat();
- at.target_Orientation = fields[12].GetFloat();
-
- AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
- if(!atEntry)
- {
- sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
- continue;
- }
-
- if(at.requiredItem)
- {
- ItemPrototype const *pProto = GetItemPrototype(at.requiredItem);
- if(!pProto)
- {
- sLog.outError("Key item %u does not exist for trigger %u, removing key requirement.", at.requiredItem, Trigger_ID);
- at.requiredItem = 0;
- }
- }
- if(at.requiredItem2)
- {
- ItemPrototype const *pProto = GetItemPrototype(at.requiredItem2);
- if(!pProto)
- {
- sLog.outError("Second item %u not exist for trigger %u, remove key requirement.", at.requiredItem2, Trigger_ID);
- at.requiredItem2 = 0;
- }
- }
-
- if(at.heroicKey)
- {
- ItemPrototype const *pProto = GetItemPrototype(at.heroicKey);
- if(!pProto)
- {
- sLog.outError("Heroic key item %u not exist for trigger %u, remove key requirement.", at.heroicKey, Trigger_ID);
- at.heroicKey = 0;
- }
- }
-
- if(at.heroicKey2)
- {
- ItemPrototype const *pProto = GetItemPrototype(at.heroicKey2);
- if(!pProto)
- {
- sLog.outError("Heroic second key item %u not exist for trigger %u, remove key requirement.", at.heroicKey2, Trigger_ID);
- at.heroicKey2 = 0;
- }
- }
-
- if(at.requiredQuest)
- {
- if(!mQuestTemplates[at.requiredQuest])
- {
- sLog.outErrorDb("Required Quest %u not exist for trigger %u, remove quest done requirement.",at.requiredQuest,Trigger_ID);
- at.requiredQuest = 0;
- }
- }
-
- MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
- if(!mapEntry)
- {
- sLog.outErrorDb("Area trigger (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.",Trigger_ID,at.target_mapId);
- continue;
- }
-
- if(at.target_X==0 && at.target_Y==0 && at.target_Z==0)
- {
- sLog.outErrorDb("Area trigger (ID:%u) target coordinates not provided.",Trigger_ID);
- continue;
- }
-
- mAreaTriggers[Trigger_ID] = at;
-
- } while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u area trigger teleport definitions", count );
-}
-
-AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const
-{
- const MapEntry *mapEntry = sMapStore.LookupEntry(Map);
- if(!mapEntry) return NULL;
- for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); itr++)
- {
- if(itr->second.target_mapId == mapEntry->parent_map)
- {
- AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
- if(atEntry && atEntry->mapid == Map)
- return &itr->second;
- }
- }
- return NULL;
-}
-
-void ObjectMgr::SetHighestGuids()
-{
- QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guid) FROM characters" );
- if( result )
- {
- m_hiCharGuid = (*result)[0].GetUInt32()+1;
-
- delete result;
- }
-
- result = WorldDatabase.Query( "SELECT MAX(guid) FROM creature" );
- if( result )
- {
- m_hiCreatureGuid = (*result)[0].GetUInt32()+1;
-
- delete result;
- }
-
- result = CharacterDatabase.Query( "SELECT MAX(id) FROM character_pet" );
- if( result )
- {
- m_hiPetGuid = (*result)[0].GetUInt32()+1;
-
- delete result;
- }
-
- result = CharacterDatabase.Query( "SELECT MAX(guid) FROM item_instance" );
- if( result )
- {
- m_hiItemGuid = (*result)[0].GetUInt32()+1;
-
- delete result;
- }
-
- // Cleanup other tables from not existed guids (>=m_hiItemGuid)
- CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", m_hiItemGuid);
- CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", m_hiItemGuid);
- CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE itemguid >= '%u'", m_hiItemGuid);
- CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", m_hiItemGuid);
-
- result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject" );
- if( result )
- {
- m_hiGoGuid = (*result)[0].GetUInt32()+1;
-
- delete result;
- }
-
- result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse" );
- if( result )
- {
- m_auctionid = (*result)[0].GetUInt32()+1;
-
- delete result;
- }
- else
- {
- m_auctionid = 0;
- }
- result = CharacterDatabase.Query( "SELECT MAX(id) FROM mail" );
- if( result )
- {
- m_mailid = (*result)[0].GetUInt32()+1;
-
- delete result;
- }
- else
- {
- m_mailid = 0;
- }
- result = CharacterDatabase.Query( "SELECT MAX(id) FROM item_text" );
- if( result )
- {
- m_ItemTextId = (*result)[0].GetUInt32();
-
- delete result;
- }
- else
- m_ItemTextId = 0;
-
- result = CharacterDatabase.Query( "SELECT MAX(guid) FROM corpse" );
- if( result )
- {
- m_hiCorpseGuid = (*result)[0].GetUInt32()+1;
-
- delete result;
- }
-}
-
-uint32 ObjectMgr::GenerateAuctionID()
-{
- ++m_auctionid;
- if(m_auctionid>=0xFFFFFFFF)
- {
- sLog.outError("Auctions ids overflow!! Can't continue, shuting down server. ");
- sWorld.m_stopEvent = true;
- }
- return m_auctionid;
-}
-
-uint32 ObjectMgr::GenerateMailID()
-{
- ++m_mailid;
- if(m_mailid>=0xFFFFFFFF)
- {
- sLog.outError("Mail ids overflow!! Can't continue, shuting down server. ");
- sWorld.m_stopEvent = true;
- }
- return m_mailid;
-}
-
-uint32 ObjectMgr::GenerateItemTextID()
-{
- ++m_ItemTextId;
- if(m_ItemTextId>=0xFFFFFFFF)
- {
- sLog.outError("Item text ids overflow!! Can't continue, shuting down server. ");
- sWorld.m_stopEvent = true;
- }
- return m_ItemTextId;
-}
-
-uint32 ObjectMgr::CreateItemText(std::string text)
-{
- uint32 newItemTextId = GenerateItemTextID();
- //insert new itempage to container
- mItemTexts[ newItemTextId ] = text;
- //save new itempage
- CharacterDatabase.escape_string(text);
- //any Delete query needed, itemTextId is maximum of all ids
- std::ostringstream query;
- query << "INSERT INTO item_text (id,text) VALUES ( '" << newItemTextId << "', '" << text << "')";
- CharacterDatabase.Execute(query.str().c_str()); //needs to be run this way, because mail body may be more than 1024 characters
- return newItemTextId;
-}
-
-uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh)
-{
- switch(guidhigh)
- {
- case HIGHGUID_ITEM:
- ++m_hiItemGuid;
- if(m_hiItemGuid>=0xFFFFFFFF)
- {
- sLog.outError("Item guid overflow!! Can't continue, shuting down server. ");
- sWorld.m_stopEvent = true;
- }
- return m_hiItemGuid;
- case HIGHGUID_UNIT:
- ++m_hiCreatureGuid;
- if(m_hiCreatureGuid>=0x00FFFFFF)
- {
- sLog.outError("Creature guid overflow!! Can't continue, shuting down server. ");
- sWorld.m_stopEvent = true;
- }
- return m_hiCreatureGuid;
- case HIGHGUID_PET:
- ++m_hiPetGuid;
- if(m_hiPetGuid>=0x00FFFFFF)
- {
- sLog.outError("Pet guid overflow!! Can't continue, shuting down server. ");
- sWorld.m_stopEvent = true;
- }
- return m_hiPetGuid;
- case HIGHGUID_PLAYER:
- ++m_hiCharGuid;
- if(m_hiCharGuid>=0xFFFFFFFF)
- {
- sLog.outError("Players guid overflow!! Can't continue, shuting down server. ");
- sWorld.m_stopEvent = true;
- }
- return m_hiCharGuid;
- case HIGHGUID_GAMEOBJECT:
- ++m_hiGoGuid;
- if(m_hiGoGuid>=0x00FFFFFF)
- {
- sLog.outError("Gameobject guid overflow!! Can't continue, shuting down server. ");
- sWorld.m_stopEvent = true;
- }
- return m_hiGoGuid;
- case HIGHGUID_CORPSE:
- ++m_hiCorpseGuid;
- if(m_hiCorpseGuid>=0xFFFFFFFF)
- {
- sLog.outError("Corpse guid overflow!! Can't continue, shuting down server. ");
- sWorld.m_stopEvent = true;
- }
- return m_hiCorpseGuid;
- case HIGHGUID_DYNAMICOBJECT:
- ++m_hiDoGuid;
- if(m_hiDoGuid>=0xFFFFFFFF)
- {
- sLog.outError("DynamicObject guid overflow!! Can't continue, shuting down server. ");
- sWorld.m_stopEvent = true;
- }
- return m_hiDoGuid;
- default:
- ASSERT(0);
- }
-
- ASSERT(0);
- return 0;
-}
-
-void ObjectMgr::LoadGameObjectLocales()
-{
- QueryResult *result = WorldDatabase.Query("SELECT entry,"
- "name_loc1,name_loc2,name_loc3,name_loc4,name_loc5,name_loc6,name_loc7,name_loc8,"
- "castbarcaption_loc1,castbarcaption_loc2,castbarcaption_loc3,castbarcaption_loc4,"
- "castbarcaption_loc5,castbarcaption_loc6,castbarcaption_loc7,castbarcaption_loc8 FROM locales_gameobject");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString("");
- sLog.outString(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 entry = fields[0].GetUInt32();
-
- GameObjectLocale& data = mGameObjectLocaleMap[entry];
-
- for(int i = 1; i < MAX_LOCALE; ++i)
- {
- std::string str = fields[i].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.Name.size() <= idx)
- data.Name.resize(idx+1);
-
- data.Name[idx] = str;
- }
- }
- }
-
- for(int i = MAX_LOCALE; i < MAX_LOCALE*2-1; ++i)
- {
- std::string str = fields[i].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- if(data.CastBarCaption.size() <= idx)
- data.CastBarCaption.resize(idx+1);
-
- data.CastBarCaption[idx] = str;
- }
- }
- }
-
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u gameobject locale strings", mGameObjectLocaleMap.size() );
-}
-
-void ObjectMgr::LoadGameobjectInfo()
-{
- sGOStorage.Load();
-
- // some checks
- for(uint32 id = 1; id < sGOStorage.MaxEntry; id++)
- {
- GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
- if(!goInfo)
- continue;
-
- switch(goInfo->type)
- {
- case GAMEOBJECT_TYPE_DOOR: //0
- {
- if(goInfo->door.lockId)
- {
- if(!sLockStore.LookupEntry(goInfo->door.lockId))
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.",
- id,goInfo->type,goInfo->door.lockId,goInfo->door.lockId);
- }
- break;
- }
- case GAMEOBJECT_TYPE_BUTTON: //1
- {
- if(goInfo->button.lockId)
- {
- if(!sLockStore.LookupEntry(goInfo->button.lockId))
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.",
- id,goInfo->type,goInfo->button.lockId,goInfo->button.lockId);
- }
- break;
- }
- case GAMEOBJECT_TYPE_CHEST: //3
- {
- if(goInfo->chest.lockId)
- {
- if(!sLockStore.LookupEntry(goInfo->chest.lockId))
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but lock (Id: %u) not found.",
- id,goInfo->type,goInfo->chest.lockId,goInfo->chest.lockId);
- }
- if(goInfo->chest.linkedTrapId) // linked trap
- {
- if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->chest.linkedTrapId))
- {
- if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
- id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
- }
- /* disable check for while
- else
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
- id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId);
- */
- }
- break;
- }
- case GAMEOBJECT_TYPE_TRAP: //6
- {
- /* disable check for while
- if(goInfo->trap.spellId) // spell
- {
- if(!sSpellStore.LookupEntry(goInfo->trap.spellId))
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.",
- id,goInfo->type,goInfo->trap.spellId,goInfo->trap.spellId);
- }
- */
- break;
- }
- case GAMEOBJECT_TYPE_CHAIR: //7
- if(goInfo->chair.height > 2)
- {
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but correct chair height in range 0..2.",
- id,goInfo->type,goInfo->chair.height);
-
- // prevent client and server unexpected work
- const_cast<GameObjectInfo*>(goInfo)->chair.height = 0;
- }
- break;
- case GAMEOBJECT_TYPE_SPELL_FOCUS: //8
- {
- if(goInfo->spellFocus.focusId)
- {
- if(!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId))
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
- id,goInfo->type,goInfo->spellFocus.focusId,goInfo->spellFocus.focusId);
- }
-
- if(goInfo->spellFocus.linkedTrapId) // linked trap
- {
- if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->spellFocus.linkedTrapId))
- {
- if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
- id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
- }
- /* disable check for while
- else
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
- id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId);
- */
- }
- break;
- }
- case GAMEOBJECT_TYPE_GOOBER: //10
- {
- if(goInfo->goober.pageId) // pageId
- {
- if(!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId))
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
- id,goInfo->type,goInfo->goober.pageId,goInfo->goober.pageId);
- }
- /* disable check for while
- if(goInfo->goober.spellId) // spell
- {
- if(!sSpellStore.LookupEntry(goInfo->goober.spellId))
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but Spell (Entry %u) not exist.",
- id,goInfo->type,goInfo->goober.spellId,goInfo->goober.spellId);
- }
- */
- if(goInfo->goober.linkedTrapId) // linked trap
- {
- if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->goober.linkedTrapId))
- {
- if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
- id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
- }
- /* disable check for while
- else
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
- id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId);
- */
- }
- break;
- }
- case GAMEOBJECT_TYPE_MO_TRANSPORT: //15
- {
- if(goInfo->moTransport.taxiPathId)
- {
- if(goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty())
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
- id,goInfo->type,goInfo->moTransport.taxiPathId,goInfo->moTransport.taxiPathId);
- }
- break;
- }
- case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
- {
- /* disabled
- if(goInfo->summoningRitual.spellId)
- {
- if(!sSpellStore.LookupEntry(goInfo->summoningRitual.spellId))
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but Spell (Entry %u) not exist.",
- id,goInfo->type,goInfo->summoningRitual.spellId,goInfo->summoningRitual.spellId);
- }
- */
- break;
- }
- case GAMEOBJECT_TYPE_SPELLCASTER: //22
- {
- if(goInfo->spellcaster.spellId) // spell
- {
- if(!sSpellStore.LookupEntry(goInfo->spellcaster.spellId))
- sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.",
- id,goInfo->type,goInfo->spellcaster.spellId,goInfo->spellcaster.spellId);
- }
- break;
- }
- }
- }
-
- sLog.outString( ">> Loaded %u game object templates", sGOStorage.RecordCount );
- sLog.outString();
-}
-
-void ObjectMgr::LoadExplorationBaseXP()
-{
- uint32 count = 0;
- QueryResult *result = WorldDatabase.Query("SELECT level,basexp FROM exploration_basexp");
-
- if( !result )
- {
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u BaseXP definitions", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- bar.step();
-
- Field *fields = result->Fetch();
- uint32 level = fields[0].GetUInt32();
- uint32 basexp = fields[1].GetUInt32();
- mBaseXPTable[level] = basexp;
- ++count;
- }
- while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u BaseXP definitions", count );
-}
-
-uint32 ObjectMgr::GetBaseXP(uint32 level)
-{
- return mBaseXPTable[level] ? mBaseXPTable[level] : 0;
-}
-
-void ObjectMgr::LoadPetNames()
-{
- uint32 count = 0;
- QueryResult *result = WorldDatabase.Query("SELECT word,entry,half FROM pet_name_generation");
-
- if( !result )
- {
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u pet name parts", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- bar.step();
-
- Field *fields = result->Fetch();
- std::string word = fields[0].GetString();
- uint32 entry = fields[1].GetUInt32();
- bool half = fields[2].GetBool();
- if(half)
- PetHalfName1[entry].push_back(word);
- else
- PetHalfName0[entry].push_back(word);
- ++count;
- }
- while (result->NextRow());
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u pet name parts", count );
-}
-
-void ObjectMgr::LoadPetNumber()
-{
- QueryResult* result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
- if(result)
- {
- Field *fields = result->Fetch();
- m_hiPetNumber = fields[0].GetUInt32()+1;
- delete result;
- }
-
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded the max pet number: %d", m_hiPetNumber-1);
-}
-
-std::string ObjectMgr::GeneratePetName(uint32 entry)
-{
- std::vector<std::string> & list0 = PetHalfName0[entry];
- std::vector<std::string> & list1 = PetHalfName1[entry];
-
- if(list0.empty() || list1.empty())
- {
- CreatureInfo const *cinfo = GetCreatureTemplate(entry);
- char* petname = GetPetName(cinfo->family, sWorld.GetDefaultDbcLocale());
- if(!petname)
- petname = cinfo->Name;
- return std::string(petname);
- }
-
- return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
-}
-
-uint32 ObjectMgr::GeneratePetNumber()
-{
- return ++m_hiPetNumber;
-}
-
-void ObjectMgr::LoadCorpses()
-{
- uint32 count = 0;
- // 0 1 2 3 4 5 6 7 8 10
- QueryResult *result = CharacterDatabase.PQuery("SELECT position_x, position_y, position_z, orientation, map, data, time, corpse_type, instance, guid FROM corpse WHERE corpse_type <> 0");
-
- if( !result )
- {
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u corpses", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- bar.step();
-
- Field *fields = result->Fetch();
-
- uint32 guid = fields[result->GetFieldCount()-1].GetUInt32();
-
- Corpse *corpse = new Corpse;
- if(!corpse->LoadFromDB(guid,fields))
- {
- delete corpse;
- continue;
- }
-
- ObjectAccessor::Instance().AddCorpse(corpse);
-
- ++count;
- }
- while (result->NextRow());
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u corpses", count );
-}
-
-void ObjectMgr::LoadReputationOnKill()
-{
- uint32 count = 0;
-
- // 0 1 2
- QueryResult *result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2,"
- // 3 4 5 6 7 8 9
- "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
- "FROM creature_onkill_reputation");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString();
- sLog.outErrorDb(">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 creature_id = fields[0].GetUInt32();
-
- ReputationOnKillEntry repOnKill;
- repOnKill.repfaction1 = fields[1].GetUInt32();
- repOnKill.repfaction2 = fields[2].GetUInt32();
- repOnKill.is_teamaward1 = fields[3].GetBool();
- repOnKill.reputation_max_cap1 = fields[4].GetUInt32();
- repOnKill.repvalue1 = fields[5].GetInt32();
- repOnKill.is_teamaward2 = fields[6].GetBool();
- repOnKill.reputation_max_cap2 = fields[7].GetUInt32();
- repOnKill.repvalue2 = fields[8].GetInt32();
- repOnKill.team_dependent = fields[9].GetUInt8();
-
- if(!GetCreatureTemplate(creature_id))
- {
- sLog.outErrorDb("Table `creature_onkill_reputation` have data for not existed creature entry (%u), skipped",creature_id);
- continue;
- }
-
- if(repOnKill.repfaction1)
- {
- FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(repOnKill.repfaction1);
- if(!factionEntry1)
- {
- sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`",repOnKill.repfaction1);
- continue;
- }
- }
-
- if(repOnKill.repfaction2)
- {
- FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(repOnKill.repfaction2);
- if(!factionEntry2)
- {
- sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`",repOnKill.repfaction2);
- continue;
- }
- }
-
- mRepOnKill[creature_id] = repOnKill;
-
- ++count;
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString(">> Loaded %u creature award reputation definitions", count);
-}
-
-void ObjectMgr::LoadWeatherZoneChances()
-{
- uint32 count = 0;
-
- // 0 1 2 3 4 5 6 7 8 9 10 11 12
- QueryResult *result = WorldDatabase.Query("SELECT zone, spring_rain_chance, spring_snow_chance, spring_storm_chance, summer_rain_chance, summer_snow_chance, summer_storm_chance, fall_rain_chance, fall_snow_chance, fall_storm_chance, winter_rain_chance, winter_snow_chance, winter_storm_chance FROM game_weather");
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString();
- sLog.outErrorDb(">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 zone_id = fields[0].GetUInt32();
-
- WeatherZoneChances& wzc = mWeatherZoneMap[zone_id];
-
- for(int season = 0; season < WEATHER_SEASONS; ++season)
- {
- wzc.data[season].rainChance = fields[season * (MAX_WEATHER_TYPE-1) + 1].GetUInt32();
- wzc.data[season].snowChance = fields[season * (MAX_WEATHER_TYPE-1) + 2].GetUInt32();
- wzc.data[season].stormChance = fields[season * (MAX_WEATHER_TYPE-1) + 3].GetUInt32();
-
- if(wzc.data[season].rainChance > 100)
- {
- wzc.data[season].rainChance = 25;
- sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%",zone_id,season);
- }
-
- if(wzc.data[season].snowChance > 100)
- {
- wzc.data[season].snowChance = 25;
- sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%",zone_id,season);
- }
-
- if(wzc.data[season].stormChance > 100)
- {
- wzc.data[season].stormChance = 25;
- sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%",zone_id,season);
- }
- }
-
- ++count;
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString(">> Loaded %u weather definitions", count);
-}
-
-void ObjectMgr::SaveCreatureRespawnTime(uint32 loguid, uint32 instance, time_t t)
-{
- mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = t;
- WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance);
- if(t)
- WorldDatabase.PExecute("INSERT INTO creature_respawn VALUES ( '%u', '" I64FMTD "', '%u' )", loguid, uint64(t), instance);
-}
-
-void ObjectMgr::DeleteCreatureData(uint32 guid)
-{
- // remove mapid*cellid -> guid_set map
- CreatureData const* data = GetCreatureData(guid);
- if(data)
- RemoveCreatureFromGrid(guid, data);
-
- mCreatureDataMap.erase(guid);
-}
-
-void ObjectMgr::SaveGORespawnTime(uint32 loguid, uint32 instance, time_t t)
-{
- mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = t;
- WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance);
- if(t)
- WorldDatabase.PExecute("INSERT INTO gameobject_respawn VALUES ( '%u', '" I64FMTD "', '%u' )", loguid, uint64(t), instance);
-}
-
-void ObjectMgr::DeleteRespawnTimeForInstance(uint32 instance)
-{
- RespawnTimes::iterator next;
-
- for(RespawnTimes::iterator itr = mGORespawnTimes.begin(); itr != mGORespawnTimes.end(); itr = next)
- {
- next = itr;
- ++next;
-
- if(GUID_HIPART(itr->first)==instance)
- mGORespawnTimes.erase(itr);
- }
-
- for(RespawnTimes::iterator itr = mCreatureRespawnTimes.begin(); itr != mCreatureRespawnTimes.end(); itr = next)
- {
- next = itr;
- ++next;
-
- if(GUID_HIPART(itr->first)==instance)
- mCreatureRespawnTimes.erase(itr);
- }
-
- WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'", instance);
- WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", instance);
-}
-
-void ObjectMgr::DeleteGOData(uint32 guid)
-{
- // remove mapid*cellid -> guid_set map
- GameObjectData const* data = GetGOData(guid);
- if(data)
- RemoveGameobjectFromGrid(guid, data);
-
- mGameObjectDataMap.erase(guid);
-}
-
-void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance)
-{
- // corpses are always added to spawn mode 0 and they are spawned by their instance id
- CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid,0)][cellid];
- cell_guids.corpses[player_guid] = instance;
-}
-
-void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid)
-{
- // corpses are always added to spawn mode 0 and they are spawned by their instance id
- CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid,0)][cellid];
- cell_guids.corpses.erase(player_guid);
-}
-
-void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map,char const* table)
-{
- map.clear(); // need for reload case
-
- uint32 count = 0;
-
- QueryResult *result = WorldDatabase.PQuery("SELECT id,quest FROM %s",table);
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString();
- sLog.outErrorDb(">> Loaded 0 quest relations from %s. DB table `%s` is empty.",table,table);
- return;
- }
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- uint32 id = fields[0].GetUInt32();
- uint32 quest = fields[1].GetUInt32();
-
- if(mQuestTemplates.find(quest) == mQuestTemplates.end())
- {
- sLog.outErrorDb("Table `%s: Quest %u listed for entry %u does not exist.",table,quest,id);
- continue;
- }
-
- map.insert(QuestRelations::value_type(id,quest));
-
- ++count;
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString(">> Loaded %u quest relations from %s", count,table);
-}
-
-void ObjectMgr::LoadGameobjectQuestRelations()
-{
- LoadQuestRelationsHelper(mGOQuestRelations,"gameobject_questrelation");
-
- for(QuestRelations::iterator itr = mGOQuestRelations.begin(); itr != mGOQuestRelations.end(); ++itr)
- {
- GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
- if(!goInfo)
- sLog.outErrorDb("Table `gameobject_questrelation` have data for not existed gameobject entry (%u) and existed quest %u",itr->first,itr->second);
- else if(goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
- sLog.outErrorDb("Table `gameobject_questrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER",itr->first,itr->second);
- }
-}
-
-void ObjectMgr::LoadGameobjectInvolvedRelations()
-{
- LoadQuestRelationsHelper(mGOQuestInvolvedRelations,"gameobject_involvedrelation");
-
- for(QuestRelations::iterator itr = mGOQuestInvolvedRelations.begin(); itr != mGOQuestInvolvedRelations.end(); ++itr)
- {
- GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
- if(!goInfo)
- sLog.outErrorDb("Table `gameobject_involvedrelation` have data for not existed gameobject entry (%u) and existed quest %u",itr->first,itr->second);
- else if(goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
- sLog.outErrorDb("Table `gameobject_involvedrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER",itr->first,itr->second);
- }
-}
-
-void ObjectMgr::LoadCreatureQuestRelations()
-{
- LoadQuestRelationsHelper(mCreatureQuestRelations,"creature_questrelation");
-
- for(QuestRelations::iterator itr = mCreatureQuestRelations.begin(); itr != mCreatureQuestRelations.end(); ++itr)
- {
- CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
- if(!cInfo)
- sLog.outErrorDb("Table `creature_questrelation` have data for not existed creature entry (%u) and existed quest %u",itr->first,itr->second);
- else if(!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
- sLog.outErrorDb("Table `creature_questrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER",itr->first,itr->second);
- }
-}
-
-void ObjectMgr::LoadCreatureInvolvedRelations()
-{
- LoadQuestRelationsHelper(mCreatureQuestInvolvedRelations,"creature_involvedrelation");
-
- for(QuestRelations::iterator itr = mCreatureQuestInvolvedRelations.begin(); itr != mCreatureQuestInvolvedRelations.end(); ++itr)
- {
- CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
- if(!cInfo)
- sLog.outErrorDb("Table `creature_involvedrelation` have data for not existed creature entry (%u) and existed quest %u",itr->first,itr->second);
- else if(!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
- sLog.outErrorDb("Table `creature_involvedrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER",itr->first,itr->second);
- }
-}
-
-void ObjectMgr::LoadReservedPlayersNames()
-{
- m_ReservedNames.clear(); // need for reload case
-
- QueryResult *result = WorldDatabase.PQuery("SELECT name FROM reserved_name");
-
- uint32 count = 0;
-
- if( !result )
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded %u reserved player names", count );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- Field* fields;
- do
- {
- bar.step();
- fields = result->Fetch();
- std::string name= fields[0].GetCppString();
- if(normalizePlayerName(name))
- {
- m_ReservedNames.insert(name);
- ++count;
- }
- } while ( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u reserved player names", count );
-}
-
-enum LanguageType
-{
- LT_BASIC_LATIN = 0x0000,
- LT_EXTENDEN_LATIN = 0x0001,
- LT_CYRILLIC = 0x0002,
- LT_EAST_ASIA = 0x0004,
- LT_ANY = 0xFFFF
-};
-
-static LanguageType GetRealmLanguageType(bool create)
-{
- switch(sWorld.getConfig(CONFIG_REALM_ZONE))
- {
- case REALM_ZONE_UNKNOWN: // any language
- case REALM_ZONE_DEVELOPMENT:
- case REALM_ZONE_TEST_SERVER:
- case REALM_ZONE_QA_SERVER:
- return LT_ANY;
- case REALM_ZONE_UNITED_STATES: // extended-Latin
- case REALM_ZONE_OCEANIC:
- case REALM_ZONE_LATIN_AMERICA:
- case REALM_ZONE_ENGLISH:
- case REALM_ZONE_GERMAN:
- case REALM_ZONE_FRENCH:
- case REALM_ZONE_SPANISH:
- return LT_EXTENDEN_LATIN;
- case REALM_ZONE_KOREA: // East-Asian
- case REALM_ZONE_TAIWAN:
- case REALM_ZONE_CHINA:
- return LT_EAST_ASIA;
- case REALM_ZONE_RUSSIAN: // Cyrillic
- return LT_CYRILLIC;
- default:
- return create ? LT_BASIC_LATIN : LT_ANY; // basic-Latin at create, any at login
- }
-}
-
-bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
-{
- if(strictMask==0) // any language, ignore realm
- {
- if(isExtendedLatinString(wstr,numericOrSpace))
- return true;
- if(isCyrillicString(wstr,numericOrSpace))
- return true;
- if(isEastAsianString(wstr,numericOrSpace))
- return true;
- return false;
- }
-
- if(strictMask & 0x2) // realm zone specific
- {
- LanguageType lt = GetRealmLanguageType(create);
- if(lt & LT_EXTENDEN_LATIN)
- if(isExtendedLatinString(wstr,numericOrSpace))
- return true;
- if(lt & LT_CYRILLIC)
- if(isCyrillicString(wstr,numericOrSpace))
- return true;
- if(lt & LT_EAST_ASIA)
- if(isEastAsianString(wstr,numericOrSpace))
- return true;
- }
-
- if(strictMask & 0x1) // basic latin
- {
- if(isBasicLatinString(wstr,numericOrSpace))
- return true;
- }
-
- return false;
-}
-
-bool ObjectMgr::IsValidName( std::string name, bool create )
-{
- std::wstring wname;
- if(!Utf8toWStr(name,wname))
- return false;
-
- if(wname.size() < 1 || wname.size() > MAX_PLAYER_NAME)
- return false;
-
- uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PLAYER_NAMES);
-
- return isValidString(wname,strictMask,false,create);
-}
-
-bool ObjectMgr::IsValidCharterName( std::string name )
-{
- std::wstring wname;
- if(!Utf8toWStr(name,wname))
- return false;
-
- if(wname.size() < 1)
- return false;
-
- uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_CHARTER_NAMES);
-
- return isValidString(wname,strictMask,true);
-}
-
-bool ObjectMgr::IsValidPetName( std::string name )
-{
- std::wstring wname;
- if(!Utf8toWStr(name,wname))
- return false;
-
- if(wname.size() < 1)
- return false;
-
- uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PET_NAMES);
-
- return isValidString(wname,strictMask,false);
-}
-
-int ObjectMgr::GetIndexForLocale( LocaleConstant loc )
-{
- if(loc==LOCALE_enUS)
- return -1;
-
- for(size_t i=0;i < m_LocalForIndex.size(); ++i)
- if(m_LocalForIndex[i]==loc)
- return i;
-
- return -1;
-}
-
-LocaleConstant ObjectMgr::GetLocaleForIndex(int i)
-{
- if (i<0 || i>=m_LocalForIndex.size())
- return LOCALE_enUS;
-
- return m_LocalForIndex[i];
-}
-
-int ObjectMgr::GetOrNewIndexForLocale( LocaleConstant loc )
-{
- if(loc==LOCALE_enUS)
- return -1;
-
- for(size_t i=0;i < m_LocalForIndex.size(); ++i)
- if(m_LocalForIndex[i]==loc)
- return i;
-
- m_LocalForIndex.push_back(loc);
- return m_LocalForIndex.size()-1;
-}
-
-void ObjectMgr::LoadBattleMastersEntry()
-{
- mBattleMastersMap.clear(); // need for reload case
-
- QueryResult *result = WorldDatabase.Query( "SELECT entry,bg_template FROM battlemaster_entry" );
-
- uint32 count = 0;
-
- if( !result )
- {
- barGoLink bar( 1 );
- bar.step();
-
- sLog.outString();
- sLog.outString( ">> Loaded 0 battlemaster entries - table is empty!" );
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- ++count;
- bar.step();
-
- Field *fields = result->Fetch();
-
- uint32 entry = fields[0].GetUInt32();
- uint32 bgTypeId = fields[1].GetUInt32();
-
- mBattleMastersMap[entry] = bgTypeId;
-
- } while( result->NextRow() );
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u battlemaster entries", count );
-}
-
-void ObjectMgr::LoadGameObjectForQuests()
-{
- mGameObjectForQuestSet.clear(); // need for reload case
-
- uint32 count = 0;
-
- // collect GO entries for GO that must activated
- for(uint32 go_entry = 1; go_entry < sGOStorage.MaxEntry; ++go_entry)
- {
- GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(go_entry);
- if(!goInfo)
- continue;
-
- switch(goInfo->type)
- {
- // scan GO chest with loot including quest items
- case GAMEOBJECT_TYPE_CHEST:
- {
- uint32 loot_id = GameObject::GetLootId(goInfo);
-
- // find quest loot for GO
- if(LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
- {
- mGameObjectForQuestSet.insert(go_entry);
- ++count;
- }
- break;
- }
- case GAMEOBJECT_TYPE_GOOBER:
- {
- if(goInfo->goober.questId) //quests objects
- {
- mGameObjectForQuestSet.insert(go_entry);
- count++;
- }
- break;
- }
- default:
- break;
- }
- }
-
- sLog.outString();
- sLog.outString( ">> Loaded %u GameObject for quests", count );
-}
-
-bool ObjectMgr::LoadMangosStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value)
-{
- // cleanup affected map part for reloading case
- for(MangosStringLocaleMap::iterator itr = mMangosStringLocaleMap.begin(); itr != mMangosStringLocaleMap.end();)
- {
- if(itr->first >= min_value && itr->first <= max_value)
- {
- MangosStringLocaleMap::iterator itr2 = itr;
- ++itr;
- mMangosStringLocaleMap.erase(itr2);
- }
- else
- ++itr;
- }
-
- QueryResult *result = db.PQuery("SELECT entry,content_default,content_loc1,content_loc2,content_loc3,content_loc4,content_loc5,content_loc6,content_loc7,content_loc8 FROM %s",table);
-
- if(!result)
- {
- barGoLink bar(1);
-
- bar.step();
-
- sLog.outString("");
- if(min_value > 0) // error only in case internal strings
- sLog.outErrorDb(">> Loaded 0 mangos strings. DB table `%s` is empty. Cannot continue.",table);
- else
- sLog.outString(">> Loaded 0 string templates. DB table `%s` is empty.",table);
- return false;
- }
-
- uint32 count = 0;
-
- barGoLink bar(result->GetRowCount());
-
- do
- {
- Field *fields = result->Fetch();
- bar.step();
-
- int32 entry = fields[0].GetInt32();
-
- if(entry==0)
- {
- sLog.outErrorDb("Table `%s` contain reserved entry 0, ignored.",table);
- continue;
- }
- else if(entry < min_value || entry > max_value)
- {
- int32 start = min_value > 0 ? min_value : max_value;
- int32 end = min_value > 0 ? max_value : min_value;
- sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,start,end);
- continue;
- }
-
- MangosStringLocale& data = mMangosStringLocaleMap[entry];
-
- if(data.Content.size() > 0)
- {
- sLog.outErrorDb("Table `%s` contain data for already loaded entry %i (from another table?), ignored.",table,entry);
- continue;
- }
-
- data.Content.resize(1);
- ++count;
-
- // 0 -> default, idx in to idx+1
- data.Content[0] = fields[1].GetCppString();
-
- for(int i = 1; i < MAX_LOCALE; ++i)
- {
- std::string str = fields[i+1].GetCppString();
- if(!str.empty())
- {
- int idx = GetOrNewIndexForLocale(LocaleConstant(i));
- if(idx >= 0)
- {
- // 0 -> default, idx in to idx+1
- if(data.Content.size() <= idx+1)
- data.Content.resize(idx+2);
-
- data.Content[idx+1] = str;
- }
- }
- }
- } while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- if(min_value > 0) // internal mangos strings
- sLog.outString( ">> Loaded %u MaNGOS strings from table %s", count,table);
- else
- sLog.outString( ">> Loaded %u string templates from %s", count,table);
-
- return true;
-}
-
-const char *ObjectMgr::GetMangosString(int32 entry, int locale_idx) const
-{
- // locale_idx==-1 -> default, locale_idx >= 0 in to idx+1
- // Content[0] always exist if exist MangosStringLocale
- if(MangosStringLocale const *msl = GetMangosStringLocale(entry))
- {
- if(msl->Content.size() > locale_idx+1 && !msl->Content[locale_idx+1].empty())
- return msl->Content[locale_idx+1].c_str();
- else
- return msl->Content[0].c_str();
- }
-
- if(entry > 0)
- sLog.outErrorDb("Entry %i not found in `mangos_string` table.",entry);
- else
- sLog.outErrorDb("Mangos string entry %i not found in DB.",entry);
- return "<error>";
-}
-
-void ObjectMgr::LoadFishingBaseSkillLevel()
-{
- mFishingBaseForArea.clear(); // for relaod case
-
- uint32 count = 0;
- QueryResult *result = WorldDatabase.Query("SELECT entry,skill FROM skill_fishing_base_level");
-
- if( !result )
- {
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outErrorDb(">> Loaded `skill_fishing_base_level`, table is empty!");
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- bar.step();
-
- Field *fields = result->Fetch();
- uint32 entry = fields[0].GetUInt32();
- int32 skill = fields[1].GetInt32();
-
- AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry);
- if(!fArea)
- {
- sLog.outErrorDb("AreaId %u defined in `skill_fishing_base_level` does not exist",entry);
- continue;
- }
-
- mFishingBaseForArea[entry] = skill;
- ++count;
- }
- while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u areas for fishing base skill level", count );
-}
-
-// Searches for the same condition already in Conditions store
-// Returns Id if found, else adds it to Conditions and returns Id
-uint16 ObjectMgr::GetConditionId( ConditionType condition, uint32 value1, uint32 value2 )
-{
- PlayerCondition lc = PlayerCondition(condition, value1, value2);
- for (uint16 i=0; i < mConditions.size(); ++i)
- {
- if (lc == mConditions[i])
- return i;
- }
-
- mConditions.push_back(lc);
-
- if(mConditions.size() > 0xFFFF)
- {
- sLog.outError("Conditions store overflow! Current and later loaded conditions will ignored!");
- return 0;
- }
-
- return mConditions.size() - 1;
-}
-
-bool ObjectMgr::CheckDeclinedNames( std::wstring mainpart, DeclinedName const& names )
-{
- for(int i =0; i < MAX_DECLINED_NAME_CASES; ++i)
- {
- std::wstring wname;
- if(!Utf8toWStr(names.name[i],wname))
- return false;
-
- if(mainpart!=GetMainPartOfName(wname,i+1))
- return false;
- }
- return true;
-}
-
-const char* ObjectMgr::GetAreaTriggerScriptName(uint32 id)
-{
- AreaTriggerScriptMap::const_iterator i = mAreaTriggerScripts.find(id);
- if(i!= mAreaTriggerScripts.end())
- return i->second.c_str();
- return "";
-}
-
-// Checks if player meets the condition
-bool PlayerCondition::Meets(Player const * player) const
-{
- if( !player )
- return false; // player not present, return false
-
- switch (condition)
- {
- case CONDITION_NONE:
- return true; // empty condition, always met
- case CONDITION_AURA:
- return player->HasAura(value1, value2);
- case CONDITION_ITEM:
- return player->HasItemCount(value1, value2);
- case CONDITION_ITEM_EQUIPPED:
- return player->GetItemOrItemWithGemEquipped(value1) != NULL;
- case CONDITION_ZONEID:
- return player->GetZoneId() == value1;
- case CONDITION_REPUTATION_RANK:
- {
- FactionEntry const* faction = sFactionStore.LookupEntry(value1);
- return faction && player->GetReputationRank(faction) >= value2;
- }
- case CONDITION_TEAM:
- return player->GetTeam() == value1;
- case CONDITION_SKILL:
- return player->HasSkill(value1) && player->GetBaseSkillValue(value1) >= value2;
- case CONDITION_QUESTREWARDED:
- return player->GetQuestRewardStatus(value1);
- case CONDITION_QUESTTAKEN:
- {
- QuestStatus status = player->GetQuestStatus(value1);
- return (status == QUEST_STATUS_INCOMPLETE);
- }
- case CONDITION_AD_COMMISSION_AURA:
- {
- Unit::AuraMap const& auras = player->GetAuras();
- for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
- if((itr->second->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetSpellProto()->SpellVisual==3580)
- return true;
- return false;
- }
- default:
- return false;
- }
-}
-
-// Verification of condition values validity
-bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 value2)
-{
- if( condition >= MAX_CONDITION) // Wrong condition type
- {
- sLog.outErrorDb("Condition has bad type of %u, skipped ", condition );
- return false;
- }
-
- switch (condition)
- {
- case CONDITION_AURA:
- {
- if(!sSpellStore.LookupEntry(value1))
- {
- sLog.outErrorDb("Aura condition requires to have non existing spell (Id: %d), skipped", value1);
- return false;
- }
- if(value2 > 2)
- {
- sLog.outErrorDb("Aura condition requires to have non existing effect index (%u) (must be 0..2), skipped", value2);
- return false;
- }
- break;
- }
- case CONDITION_ITEM:
- {
- ItemPrototype const *proto = objmgr.GetItemPrototype(value1);
- if(!proto)
- {
- sLog.outErrorDb("Item condition requires to have non existing item (%u), skipped", value1);
- return false;
- }
- break;
- }
- case CONDITION_ITEM_EQUIPPED:
- {
- ItemPrototype const *proto = objmgr.GetItemPrototype(value1);
- if(!proto)
- {
- sLog.outErrorDb("ItemEquipped condition requires to have non existing item (%u) equipped, skipped", value1);
- return false;
- }
- break;
- }
- case CONDITION_ZONEID:
- {
- AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(value1);
- if(!areaEntry)
- {
- sLog.outErrorDb("Zone condition requires to be in non existing area (%u), skipped", value1);
- return false;
- }
- if(areaEntry->zone != 0)
- {
- sLog.outErrorDb("Zone condition requires to be in area (%u) which is a subzone but zone expected, skipped", value1);
- return false;
- }
- break;
- }
- case CONDITION_REPUTATION_RANK:
- {
- FactionEntry const* factionEntry = sFactionStore.LookupEntry(value1);
- if(!factionEntry)
- {
- sLog.outErrorDb("Reputation condition requires to have reputation non existing faction (%u), skipped", value1);
- return false;
- }
- break;
- }
- case CONDITION_TEAM:
- {
- if (value1 != ALLIANCE && value1 != HORDE)
- {
- sLog.outErrorDb("Team condition specifies unknown team (%u), skipped", value1);
- return false;
- }
- break;
- }
- case CONDITION_SKILL:
- {
- SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(value1);
- if (!pSkill)
- {
- sLog.outErrorDb("Skill condition specifies non-existing skill (%u), skipped", value1);
- return false;
- }
- if (value2 < 1 || value2 > sWorld.GetConfigMaxSkillValue() )
- {
- sLog.outErrorDb("Skill condition specifies invalid skill value (%u), skipped", value2);
- return false;
- }
- break;
- }
- case CONDITION_QUESTREWARDED:
- case CONDITION_QUESTTAKEN:
- {
- Quest const *Quest = objmgr.GetQuestTemplate(value1);
- if (!Quest)
- {
- sLog.outErrorDb("Quest condition specifies non-existing quest (%u), skipped", value1);
- return false;
- }
- if(value2)
- sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2);
- break;
- }
- case CONDITION_AD_COMMISSION_AURA:
- {
- if(value1)
- sLog.outErrorDb("Quest condition has useless data in value1 (%u)!", value1);
- if(value2)
- sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2);
- break;
- }
- }
- return true;
-}
-
-SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial)
-{
- switch(pSkill->categoryId)
- {
- case SKILL_CATEGORY_LANGUAGES: return SKILL_RANGE_LANGUAGE;
- case SKILL_CATEGORY_WEAPON:
- if(pSkill->id!=SKILL_FIST_WEAPONS)
- return SKILL_RANGE_LEVEL;
- else
- return SKILL_RANGE_MONO;
- case SKILL_CATEGORY_ARMOR:
- case SKILL_CATEGORY_CLASS:
- if(pSkill->id != SKILL_POISONS && pSkill->id != SKILL_LOCKPICKING)
- return SKILL_RANGE_MONO;
- else
- return SKILL_RANGE_LEVEL;
- case SKILL_CATEGORY_SECONDARY:
- case SKILL_CATEGORY_PROFESSION:
- // not set skills for professions and racial abilities
- if(IsProfessionSkill(pSkill->id))
- return SKILL_RANGE_RANK;
- else if(racial)
- return SKILL_RANGE_NONE;
- else
- return SKILL_RANGE_MONO;
- default:
- case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc
- case SKILL_CATEGORY_NOT_DISPLAYED: //only GENEREC(DND)
- return SKILL_RANGE_NONE;
- }
-}
-
-void ObjectMgr::LoadGameTele()
-{
- m_GameTeleMap.clear(); // for relaod case
-
- uint32 count = 0;
- QueryResult *result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
-
- if( !result )
- {
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outErrorDb(">> Loaded `game_tele`, table is empty!");
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- do
- {
- bar.step();
-
- Field *fields = result->Fetch();
-
- uint32 id = fields[0].GetUInt32();
-
- GameTele gt;
-
- gt.position_x = fields[1].GetFloat();
- gt.position_y = fields[2].GetFloat();
- gt.position_z = fields[3].GetFloat();
- gt.orientation = fields[4].GetFloat();
- gt.mapId = fields[5].GetUInt32();
- gt.name = fields[6].GetCppString();
-
- if(!MapManager::IsValidMapCoord(gt.mapId,gt.position_x,gt.position_y,gt.position_z,gt.orientation))
- {
- sLog.outErrorDb("Wrong position for id %u (name: %s) in `game_tele` table, ignoring.",id,gt.name.c_str());
- continue;
- }
-
- if(!Utf8toWStr(gt.name,gt.wnameLow))
- {
- sLog.outErrorDb("Wrong UTF8 name for id %u in `game_tele` table, ignoring.",id);
- continue;
- }
-
- wstrToLower( gt.wnameLow );
-
- m_GameTeleMap[id] = gt;
-
- ++count;
- }
- while (result->NextRow());
-
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %u game tele's", count );
-}
-
-GameTele const* ObjectMgr::GetGameTele(std::string name) const
-{
- // explicit name case
- std::wstring wname;
- if(!Utf8toWStr(name,wname))
- return false;
-
- // converting string that we try to find to lower case
- wstrToLower( wname );
-
- for(GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
- if(itr->second.wnameLow == wname)
- return &itr->second;
-
- return NULL;
-}
-
-bool ObjectMgr::AddGameTele(GameTele& tele)
-{
- // find max id
- uint32 new_id = 0;
- for(GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
- if(itr->first > new_id)
- new_id = itr->first;
-
- // use next
- ++new_id;
-
- if(!Utf8toWStr(tele.name,tele.wnameLow))
- return false;
-
- wstrToLower( tele.wnameLow );
-
- m_GameTeleMap[new_id] = tele;
-
- return WorldDatabase.PExecuteLog("INSERT INTO game_tele (id,position_x,position_y,position_z,orientation,map,name) VALUES (%u,%f,%f,%f,%f,%d,'%s')",
- new_id,tele.position_x,tele.position_y,tele.position_z,tele.orientation,tele.mapId,tele.name.c_str());
-}
-
-bool ObjectMgr::DeleteGameTele(std::string name)
-{
- // explicit name case
- std::wstring wname;
- if(!Utf8toWStr(name,wname))
- return false;
-
- // converting string that we try to find to lower case
- wstrToLower( wname );
-
- for(GameTeleMap::iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
- {
- if(itr->second.wnameLow == wname)
- {
- WorldDatabase.PExecuteLog("DELETE FROM game_tele WHERE name = '%s'",itr->second.name.c_str());
- m_GameTeleMap.erase(itr);
- return true;
- }
- }
-
- return false;
-}
-
-void ObjectMgr::LoadTrainerSpell()
-{
- // For reload case
- for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
- 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 )
- {
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outErrorDb(">> Loaded `npc_trainer`, table is empty!");
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- uint32 count = 0;
- do
- {
- bar.step();
-
- Field* fields = result->Fetch();
-
- uint32 entry = fields[0].GetUInt32();
- uint32 spell = fields[1].GetUInt32();
-
- CreatureInfo const* cInfo = GetCreatureTemplate(entry);
-
- if(!cInfo)
- {
- sLog.outErrorDb("Table `npc_trainer` have entry for not existed creature template (Entry: %u), ignore", entry);
- continue;
- }
-
- if(!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
- {
- 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;
- }
-
- SpellEntry const *spellinfo = sSpellStore.LookupEntry(spell);
- if(!spellinfo)
- {
- sLog.outErrorDb("Table `npc_trainer` for Trainer (Entry: %u ) has non existing spell %u, ignore", entry,spell);
- continue;
- }
-
- if(!SpellMgr::IsSpellValid(spellinfo))
- {
- sLog.outErrorDb("Table `npc_trainer` for Trainer (Entry: %u) has broken learning spell %u, ignore", entry, spell);
- continue;
- }
-
- TrainerSpell* pTrainerSpell = new TrainerSpell();
- pTrainerSpell->spell = spell;
- pTrainerSpell->spellcost = fields[2].GetUInt32();
- pTrainerSpell->reqskill = fields[3].GetUInt32();
- pTrainerSpell->reqskillvalue = fields[4].GetUInt32();
- pTrainerSpell->reqlevel = fields[5].GetUInt32();
-
- if(!pTrainerSpell->reqlevel)
- pTrainerSpell->reqlevel = spellinfo->spellLevel;
-
-
- TrainerSpellData& data = m_mCacheTrainerSpellMap[entry];
-
- if(SpellMgr::IsProfessionSpell(spell))
- data.trainerType = 2;
-
- data.spellList.push_back(pTrainerSpell);
- ++count;
-
- } while (result->NextRow());
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded Trainers %d", count );
-}
-
-void ObjectMgr::LoadVendors()
-{
- // For reload case
- for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
- 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 )
- {
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outErrorDb(">> Loaded `npc_vendor`, table is empty!");
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- uint32 count = 0;
- do
- {
- bar.step();
- Field* fields = result->Fetch();
-
- uint32 entry = fields[0].GetUInt32();
- uint32 item_id = fields[1].GetUInt32();
- uint32 maxcount = fields[2].GetUInt32();
- uint32 incrtime = fields[3].GetUInt32();
- uint32 ExtendedCost = fields[4].GetUInt32();
-
- if(!IsVendorItemValid(entry,item_id,maxcount,incrtime,ExtendedCost,NULL,&skip_vendors))
- continue;
-
- VendorItemData& vList = m_mCacheVendorItemMap[entry];
-
- vList.AddItem(item_id,maxcount,incrtime,ExtendedCost);
- ++count;
-
- } while (result->NextRow());
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %d Vendors ", count );
-}
-
-void ObjectMgr::LoadNpcTextId()
-{
-
- m_mCacheNpcTextIdMap.clear();
-
- QueryResult* result = WorldDatabase.PQuery("SELECT npc_guid, textid FROM npc_gossip");
- if( !result )
- {
- barGoLink bar( 1 );
-
- bar.step();
-
- sLog.outString();
- sLog.outErrorDb(">> Loaded `npc_gossip`, table is empty!");
- return;
- }
-
- barGoLink bar( result->GetRowCount() );
-
- uint32 count = 0;
- uint32 guid,textid;
- do
- {
- bar.step();
-
- Field* fields = result->Fetch();
-
- guid = fields[0].GetUInt32();
- textid = fields[1].GetUInt32();
-
- if (!GetCreatureData(guid))
- {
- sLog.outErrorDb("Table `npc_gossip` have not existed creature (GUID: %u) entry, ignore. ",guid);
- continue;
- }
- if (!GetGossipText(textid))
- {
- sLog.outErrorDb("Table `npc_gossip` for creature (GUID: %u) have wrong Textid (%u), ignore. ", guid, textid);
- continue;
- }
-
- m_mCacheNpcTextIdMap[guid] = textid ;
- ++count;
-
- } while (result->NextRow());
- delete result;
-
- sLog.outString();
- sLog.outString( ">> Loaded %d NpcTextId ", count );
-}
-
-void ObjectMgr::AddVendorItem( uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 extendedcost )
-{
- VendorItemData& vList = m_mCacheVendorItemMap[entry];
- vList.AddItem(item,maxcount,incrtime,extendedcost);
-
- WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')",entry, item, maxcount,incrtime,extendedcost);
-}
-
-bool ObjectMgr::RemoveVendorItem( uint32 entry,uint32 item )
-{
- CacheVendorItemMap::iterator iter = m_mCacheVendorItemMap.find(entry);
- if(iter == m_mCacheVendorItemMap.end())
- return false;
-
- if(!iter->second.FindItem(item))
- return false;
-
- iter->second.RemoveItem(item);
- WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",entry, item);
- return true;
-}
-
-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)
- {
- if(pl)
- ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
- else
- sLog.outErrorDb("Table `npc_vendor` have data for not existed creature template (Entry: %u), ignore", vendor_entry);
- return false;
- }
-
- if(!(cInfo->npcflag & UNIT_NPC_FLAG_VENDOR))
- {
- 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;
- }
-
- if(!GetItemPrototype(item_id))
- {
- if(pl)
- ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id);
- else
- sLog.outErrorDb("Table `npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore",vendor_entry,item_id);
- return false;
- }
-
- if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
- {
- if(pl)
- ChatHandler(pl).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST,ExtendedCost);
- else
- sLog.outErrorDb("Table `npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore",item_id,ExtendedCost,vendor_entry);
- return false;
- }
-
- if(maxcount > 0 && incrtime == 0)
- {
- if(pl)
- ChatHandler(pl).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount);
- else
- sLog.outErrorDb( "Table `npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, item_id, vendor_entry);
- return false;
- }
- else if(maxcount==0 && incrtime > 0)
- {
- if(pl)
- ChatHandler(pl).PSendSysMessage("MaxCount==0 but IncrTime<>=0");
- else
- sLog.outErrorDb( "Table `npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", item_id, vendor_entry);
- return false;
- }
-
- VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
- if(!vItems)
- return true; // later checks for non-empty lists
-
- if(vItems->FindItem(item_id))
- {
- if(pl)
- ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST,item_id);
- else
- sLog.outErrorDb( "Table `npc_vendor` has duplicate items %u for vendor (Entry: %u), ignore", item_id, vendor_entry);
- return false;
- }
-
- if(vItems->GetItemCount() >= MAX_VENDOR_ITEMS)
- {
- if(pl)
- ChatHandler(pl).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
- else
- sLog.outErrorDb( "Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vItems->GetItemCount(), MAX_VENDOR_ITEMS, vendor_entry);
- return false;
- }
-
- return true;
-}
-
-// Functions for scripting access
-const char* GetAreaTriggerScriptNameById(uint32 id)
-{
- return objmgr.GetAreaTriggerScriptName(id);
-}
-
-bool LoadMangosStrings(DatabaseType& db, char const* table,int32 start_value, int32 end_value)
-{
- if(start_value >= 0 || start_value <= end_value) // start/end reversed for negative values
- {
- sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), use (%d - %d) instead.",table,start_value,end_value,-1,std::numeric_limits<int32>::min());
- start_value = -1;
- end_value = std::numeric_limits<int32>::min();
- }
-
- // for scripting localized strings allowed use _only_ negative entries
- return objmgr.LoadMangosStrings(db,table,end_value,start_value);
-}
+/*
+ * 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 "Database/SQLStorage.h"
+
+#include "Log.h"
+#include "MapManager.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "UpdateMask.h"
+#include "World.h"
+#include "WorldSession.h"
+#include "Group.h"
+#include "Guild.h"
+#include "ArenaTeam.h"
+#include "Transports.h"
+#include "ProgressBar.h"
+#include "Policies/SingletonImp.h"
+#include "Language.h"
+#include "GameEvent.h"
+#include "Spell.h"
+#include "Chat.h"
+#include "InstanceSaveMgr.h"
+#include "SpellAuras.h"
+#include "Util.h"
+
+INSTANTIATE_SINGLETON_1(ObjectMgr);
+
+ScriptMapMap sQuestEndScripts;
+ScriptMapMap sQuestStartScripts;
+ScriptMapMap sSpellScripts;
+ScriptMapMap sGameObjectScripts;
+ScriptMapMap sEventScripts;
+
+bool normalizePlayerName(std::string& name)
+{
+ if(name.empty())
+ return false;
+
+ wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME+1];
+ size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
+
+ if(!Utf8toWStr(name,&wstr_buf[0],wstr_len))
+ return false;
+
+ wstr_buf[0] = wcharToUpper(wstr_buf[0]);
+ for(size_t i = 1; i < wstr_len; ++i)
+ wstr_buf[i] = wcharToLower(wstr_buf[i]);
+
+ if(!WStrToUtf8(wstr_buf,wstr_len,name))
+ return false;
+
+ return true;
+}
+
+LanguageDesc lang_description[LANGUAGES_COUNT] =
+{
+ { LANG_ADDON, 0, 0 },
+ { LANG_UNIVERSAL, 0, 0 },
+ { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
+ { LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
+ { LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
+ { LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
+ { LANG_COMMON, 668, SKILL_LANG_COMMON },
+ { LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
+ { LANG_TITAN, 816, SKILL_LANG_TITAN },
+ { LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
+ { LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
+ { LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
+ { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
+ { LANG_TROLL, 7341, SKILL_LANG_TROLL },
+ { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
+ { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
+ { LANG_ZOMBIE, 0, 0 },
+ { LANG_GNOMISH_BINARY, 0, 0 },
+ { LANG_GOBLIN_BINARY, 0, 0 }
+};
+
+LanguageDesc const* GetLanguageDescByID(uint32 lang)
+{
+ for(int i = 0; i < LANGUAGES_COUNT; ++i)
+ {
+ if(uint32(lang_description[i].lang_id) == lang)
+ return &lang_description[i];
+ }
+
+ return NULL;
+}
+
+ObjectMgr::ObjectMgr()
+{
+ m_hiCharGuid = 1;
+ m_hiCreatureGuid = 1;
+ m_hiPetGuid = 1;
+ m_hiItemGuid = 1;
+ m_hiGoGuid = 1;
+ m_hiDoGuid = 1;
+ m_hiCorpseGuid = 1;
+
+ m_hiPetNumber = 1;
+
+ mGuildBankTabPrice.resize(GUILD_BANK_MAX_TABS);
+ mGuildBankTabPrice[0] = 100;
+ mGuildBankTabPrice[1] = 250;
+ mGuildBankTabPrice[2] = 500;
+ mGuildBankTabPrice[3] = 1000;
+ mGuildBankTabPrice[4] = 2500;
+ mGuildBankTabPrice[5] = 5000;
+
+ // Only zero condition left, others will be added while loading DB tables
+ mConditions.resize(1);
+}
+
+ObjectMgr::~ObjectMgr()
+{
+ for( QuestMap::iterator i = mQuestTemplates.begin( ); i != mQuestTemplates.end( ); ++ i )
+ {
+ delete i->second;
+ }
+ mQuestTemplates.clear( );
+
+ for( GossipTextMap::iterator i = mGossipText.begin( ); i != mGossipText.end( ); ++ i )
+ {
+ delete i->second;
+ }
+ mGossipText.clear( );
+
+ mAreaTriggers.clear();
+
+ for(PetLevelInfoMap::iterator i = petInfo.begin( ); i != petInfo.end( ); ++ i )
+ {
+ delete[] i->second;
+ }
+ petInfo.clear();
+
+ // free only if loaded
+ for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
+ delete[] playerClassInfo[class_].levelInfo;
+
+ for (int race = 0; race < MAX_RACES; ++race)
+ for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
+ delete[] playerInfo[race][class_].levelInfo;
+
+ // free group and guild objects
+ for (GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
+ delete (*itr);
+ for (GuildSet::iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); ++itr)
+ delete (*itr);
+
+ for(ItemMap::iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr)
+ delete itr->second;
+
+ for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
+ itr->second.Clear();
+
+ for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
+ itr->second.Clear();
+}
+
+Group * ObjectMgr::GetGroupByLeader(const uint64 &guid) const
+{
+ for(GroupSet::const_iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
+ if ((*itr)->GetLeaderGUID() == guid)
+ return *itr;
+
+ return NULL;
+}
+
+Guild * ObjectMgr::GetGuildById(const uint32 GuildId) const
+{
+ for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
+ if ((*itr)->GetId() == GuildId)
+ return *itr;
+
+ return NULL;
+}
+
+Guild * ObjectMgr::GetGuildByName(std::string guildname) const
+{
+ for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
+ if ((*itr)->GetName() == guildname)
+ return *itr;
+
+ return NULL;
+}
+
+std::string ObjectMgr::GetGuildNameById(const uint32 GuildId) const
+{
+ for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
+ if ((*itr)->GetId() == GuildId)
+ return (*itr)->GetName();
+
+ return "";
+}
+
+Guild* ObjectMgr::GetGuildByLeader(const uint64 &guid) const
+{
+ for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); ++itr)
+ if( (*itr)->GetLeader() == guid)
+ return *itr;
+
+ return NULL;
+}
+
+ArenaTeam* ObjectMgr::GetArenaTeamById(const uint32 ArenaTeamId) const
+{
+ for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
+ if ((*itr)->GetId() == ArenaTeamId)
+ return *itr;
+
+ return NULL;
+}
+
+ArenaTeam* ObjectMgr::GetArenaTeamByName(std::string arenateamname) const
+{
+ for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
+ if ((*itr)->GetName() == arenateamname)
+ return *itr;
+
+ return NULL;
+}
+
+ArenaTeam* ObjectMgr::GetArenaTeamByCapitan(uint64 const& guid) const
+{
+ for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
+ if ((*itr)->GetCaptain() == guid)
+ return *itr;
+
+ return NULL;
+}
+
+AuctionHouseObject * ObjectMgr::GetAuctionsMap( uint32 location )
+{
+ switch ( location )
+ {
+ case 6: //horde
+ return & mHordeAuctions;
+ break;
+ case 2: //alliance
+ return & mAllianceAuctions;
+ break;
+ default: //neutral
+ return & mNeutralAuctions;
+ }
+}
+
+uint32 ObjectMgr::GetAuctionCut(uint32 location, uint32 highBid)
+{
+ if (location == 7 && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
+ return (uint32) (0.15f * highBid * sWorld.getRate(RATE_AUCTION_CUT));
+ else
+ return (uint32) (0.05f * highBid * sWorld.getRate(RATE_AUCTION_CUT));
+}
+
+uint32 ObjectMgr::GetAuctionDeposit(uint32 location, uint32 time, Item *pItem)
+{
+ float percentance; // in 0..1
+ if ( location == 7 && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
+ percentance = 0.75f;
+ else
+ percentance = 0.15f;
+
+ percentance *= sWorld.getRate(RATE_AUCTION_DEPOSIT);
+
+ return uint32( percentance * pItem->GetProto()->SellPrice * pItem->GetCount() * (time / MIN_AUCTION_TIME ) );
+}
+
+/// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c
+uint32 ObjectMgr::GetAuctionOutBid(uint32 currentBid)
+{
+ uint32 outbid = (currentBid / 100) * 5;
+ if (!outbid)
+ outbid = 1;
+ return outbid;
+}
+
+//does not clear ram
+void ObjectMgr::SendAuctionWonMail( AuctionEntry *auction )
+{
+ Item *pItem = GetAItem(auction->item_guidlow);
+ if(!pItem)
+ return;
+
+ uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
+ Player *bidder = GetPlayer(bidder_guid);
+
+ uint32 bidder_accId = 0;
+
+ // data for gm.log
+ if( sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ {
+ uint32 bidder_security = 0;
+ std::string bidder_name;
+ if (bidder)
+ {
+ bidder_accId = bidder->GetSession()->GetAccountId();
+ bidder_security = bidder->GetSession()->GetSecurity();
+ bidder_name = bidder->GetName();
+ }
+ else
+ {
+ bidder_accId = GetPlayerAccountIdByGUID(bidder_guid);
+ bidder_security = GetSecurityByAccount(bidder_accId);
+
+ if(bidder_security > SEC_PLAYER ) // not do redundant DB requests
+ {
+ if(!GetPlayerNameByGUID(bidder_guid,bidder_name))
+ bidder_name = GetMangosStringForDBCLocale(LANG_UNKNOWN);
+ }
+ }
+
+ if( bidder_security > SEC_PLAYER )
+ {
+ std::string owner_name;
+ if(!GetPlayerNameByGUID(auction->owner,owner_name))
+ owner_name = GetMangosStringForDBCLocale(LANG_UNKNOWN);
+
+ uint32 owner_accid = GetPlayerAccountIdByGUID(auction->owner);
+
+ sLog.outCommand("GM %s (Account: %u) won item in auction: %s (Entry: %u Count: %u) and pay money: %u. Original owner %s (Account: %u)",
+ bidder_name.c_str(),bidder_accId,pItem->GetProto()->Name1,pItem->GetEntry(),pItem->GetCount(),auction->bid,owner_name.c_str(),owner_accid);
+ }
+ }
+ else if(!bidder)
+ bidder_accId = GetPlayerAccountIdByGUID(bidder_guid);
+
+ // receiver exist
+ if(bidder || bidder_accId)
+ {
+ std::ostringstream msgAuctionWonSubject;
+ msgAuctionWonSubject << auction->item_template << ":0:" << AUCTION_WON;
+
+ std::ostringstream msgAuctionWonBody;
+ msgAuctionWonBody.width(16);
+ msgAuctionWonBody << std::right << std::hex << auction->owner;
+ msgAuctionWonBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
+ sLog.outDebug( "AuctionWon body string : %s", msgAuctionWonBody.str().c_str() );
+
+ //prepare mail data... :
+ uint32 itemTextId = this->CreateItemText( msgAuctionWonBody.str() );
+
+ // set owner to bidder (to prevent delete item with sender char deleting)
+ // owner in `data` will set at mail receive and item extracting
+ CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow());
+ CharacterDatabase.CommitTransaction();
+
+ MailItemsInfo mi;
+ mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
+
+ if (bidder)
+ bidder->GetSession()->SendAuctionBidderNotification( auction->location, auction->Id, bidder_guid, 0, 0, auction->item_template);
+ else
+ RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
+
+ // will delete item or place to receiver mail list
+ WorldSession::SendMailTo(bidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->bidder, msgAuctionWonSubject.str(), itemTextId, &mi, 0, 0, MAIL_CHECK_MASK_AUCTION);
+ }
+ // receiver not exist
+ else
+ {
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", pItem->GetGUIDLow());
+ RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
+ delete pItem;
+ }
+}
+
+void ObjectMgr::SendAuctionSalePendingMail( AuctionEntry * auction )
+{
+ uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
+ Player *owner = GetPlayer(owner_guid);
+
+ // owner exist (online or offline)
+ if(owner || GetPlayerAccountIdByGUID(owner_guid))
+ {
+ std::ostringstream msgAuctionSalePendingSubject;
+ msgAuctionSalePendingSubject << auction->item_template << ":0:" << AUCTION_SALE_PENDING;
+
+ std::ostringstream msgAuctionSalePendingBody;
+ uint32 auctionCut = GetAuctionCut(auction->location, auction->bid);
+
+ time_t distrTime = time(NULL) + HOUR;
+
+ msgAuctionSalePendingBody.width(16);
+ msgAuctionSalePendingBody << std::right << std::hex << auction->bidder;
+ msgAuctionSalePendingBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
+ msgAuctionSalePendingBody << ":" << auction->deposit << ":" << auctionCut << ":0:";
+ msgAuctionSalePendingBody << secsToTimeBitFields(distrTime);
+
+ sLog.outDebug("AuctionSalePending body string : %s", msgAuctionSalePendingBody.str().c_str());
+
+ uint32 itemTextId = this->CreateItemText( msgAuctionSalePendingBody.str() );
+
+ WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->owner, msgAuctionSalePendingSubject.str(), itemTextId, NULL, 0, 0, MAIL_CHECK_MASK_AUCTION);
+ }
+}
+
+//call this method to send mail to auction owner, when auction is successful, it does not clear ram
+void ObjectMgr::SendAuctionSuccessfulMail( AuctionEntry * auction )
+{
+ uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
+ Player *owner = GetPlayer(owner_guid);
+
+ uint32 owner_accId = 0;
+ if(!owner)
+ owner_accId = GetPlayerAccountIdByGUID(owner_guid);
+
+ // owner exist
+ if(owner || owner_accId)
+ {
+ std::ostringstream msgAuctionSuccessfulSubject;
+ msgAuctionSuccessfulSubject << auction->item_template << ":0:" << AUCTION_SUCCESSFUL;
+
+ std::ostringstream auctionSuccessfulBody;
+ uint32 auctionCut = GetAuctionCut(auction->location, auction->bid);
+
+ auctionSuccessfulBody.width(16);
+ auctionSuccessfulBody << std::right << std::hex << auction->bidder;
+ auctionSuccessfulBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
+ auctionSuccessfulBody << ":" << auction->deposit << ":" << auctionCut;
+
+ sLog.outDebug("AuctionSuccessful body string : %s", auctionSuccessfulBody.str().c_str());
+
+ uint32 itemTextId = this->CreateItemText( auctionSuccessfulBody.str() );
+
+ uint32 profit = auction->bid + auction->deposit - auctionCut;
+
+ if (owner)
+ {
+ //send auction owner notification, bidder must be current!
+ owner->GetSession()->SendAuctionOwnerNotification( auction );
+ }
+
+ WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->owner, msgAuctionSuccessfulSubject.str(), itemTextId, NULL, profit, 0, MAIL_CHECK_MASK_AUCTION, HOUR);
+ }
+}
+
+//does not clear ram
+void ObjectMgr::SendAuctionExpiredMail( AuctionEntry * auction )
+{ //return an item in auction to its owner by mail
+ Item *pItem = GetAItem(auction->item_guidlow);
+ if(!pItem)
+ {
+ sLog.outError("Auction item (GUID: %u) not found, and lost.",auction->item_guidlow);
+ return;
+ }
+
+ uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
+ Player *owner = GetPlayer(owner_guid);
+
+ uint32 owner_accId = 0;
+ if(!owner)
+ owner_accId = GetPlayerAccountIdByGUID(owner_guid);
+
+ // owner exist
+ if(owner || owner_accId)
+ {
+ std::ostringstream subject;
+ subject << auction->item_template << ":0:" << AUCTION_EXPIRED;
+
+ if ( owner )
+ owner->GetSession()->SendAuctionOwnerNotification( auction );
+ else
+ RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
+
+ MailItemsInfo mi;
+ mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
+
+ // will delete item or place to receiver mail list
+ WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, GUID_LOPART(owner_guid), subject.str(), 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+
+ }
+ // owner not found
+ else
+ {
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'",pItem->GetGUIDLow());
+ RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !!
+ delete pItem;
+ }
+}
+
+CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id)
+{
+ return sCreatureStorage.LookupEntry<CreatureInfo>(id);
+}
+
+void ObjectMgr::LoadCreatureLocales()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry,name_loc1,subname_loc1,name_loc2,subname_loc2,name_loc3,subname_loc3,name_loc4,subname_loc4,name_loc5,subname_loc5,name_loc6,subname_loc6,name_loc7,subname_loc7,name_loc8,subname_loc8 FROM locales_creature");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ CreatureLocale& data = mCreatureLocaleMap[entry];
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[1+2*(i-1)].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Name.size() <= idx)
+ data.Name.resize(idx+1);
+
+ data.Name[idx] = str;
+ }
+ }
+ str = fields[1+2*(i-1)+1].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.SubName.size() <= idx)
+ data.SubName.resize(idx+1);
+
+ data.SubName[idx] = str;
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u creature locale strings", mCreatureLocaleMap.size() );
+}
+
+void ObjectMgr::LoadCreatureTemplates()
+{
+ sCreatureStorage.Load();
+
+ sLog.outString( ">> Loaded %u creature definitions", sCreatureStorage.RecordCount );
+ sLog.outString();
+
+ std::set<uint32> heroicEntries; // already loaded heroic value in creatures
+ std::set<uint32> hasHeroicEntries; // already loaded creatures with heroic entry values
+
+ // check data correctness
+ for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
+ {
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
+ if(!cInfo)
+ continue;
+
+ if(cInfo->HeroicEntry)
+ {
+ CreatureInfo const* heroicInfo = GetCreatureTemplate(cInfo->HeroicEntry);
+ if(!heroicInfo)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u not exist.",cInfo->HeroicEntry,cInfo->HeroicEntry);
+ continue;
+ }
+
+ if(heroicEntries.find(i)!=heroicEntries.end())
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed as heroic but have value in `heroic_entry`.",i);
+ continue;
+ }
+
+ if(heroicEntries.find(cInfo->HeroicEntry)!=heroicEntries.end())
+ {
+ sLog.outErrorDb("Creature (Entry: %u) already listed as heroic for another entry.",cInfo->HeroicEntry);
+ continue;
+ }
+
+ if(hasHeroicEntries.find(cInfo->HeroicEntry)!=hasHeroicEntries.end())
+ {
+ sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u have heroic entry also.",i,cInfo->HeroicEntry,cInfo->HeroicEntry);
+ continue;
+ }
+
+ if(cInfo->npcflag != heroicInfo->npcflag)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `npcflag` in heroic mode.",i);
+ continue;
+ }
+
+ if(cInfo->classNum != heroicInfo->classNum)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `classNum` in heroic mode.",i);
+ continue;
+ }
+
+ if(cInfo->race != heroicInfo->race)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `race` in heroic mode.",i);
+ continue;
+ }
+
+ if(cInfo->trainer_type != heroicInfo->trainer_type)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_type` in heroic mode.",i);
+ continue;
+ }
+
+ if(cInfo->trainer_spell != heroicInfo->trainer_spell)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_spell` in heroic mode.",i);
+ continue;
+ }
+
+ hasHeroicEntries.insert(i);
+ heroicEntries.insert(cInfo->HeroicEntry);
+ }
+
+ FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A);
+ if(!factionTemplate)
+ sLog.outErrorDb("Creature (Entry: %u) has non-existing faction_A template (%u)", cInfo->Entry, cInfo->faction_A);
+
+ factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_H);
+ if(!factionTemplate)
+ sLog.outErrorDb("Creature (Entry: %u) has non-existing faction_H template (%u)", cInfo->Entry, cInfo->faction_H);
+
+ CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_A);
+ if (!minfo)
+ sLog.outErrorDb("Creature (Entry: %u) has non-existing modelId_A (%u)", cInfo->Entry, cInfo->DisplayID_A);
+ minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_H);
+ if (!minfo)
+ sLog.outErrorDb("Creature (Entry: %u) has non-existing modelId_H (%u)", cInfo->Entry, cInfo->DisplayID_H);
+
+ if(cInfo->dmgschool >= MAX_SPELL_SCHOOL)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`",cInfo->Entry,cInfo->dmgschool);
+ const_cast<CreatureInfo*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
+ }
+
+ if(cInfo->baseattacktime == 0)
+ const_cast<CreatureInfo*>(cInfo)->baseattacktime = BASE_ATTACK_TIME;
+
+ if(cInfo->rangeattacktime == 0)
+ const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;
+
+ if((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
+ sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u",cInfo->Entry,cInfo->trainer_type);
+
+ if(cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly",cInfo->Entry,cInfo->InhabitType);
+ const_cast<CreatureInfo*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
+ }
+
+ if(cInfo->PetSpellDataId)
+ {
+ CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
+ if(!spellDataId)
+ sLog.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId);
+ }
+
+ if(cInfo->MovementType >= MAX_DB_MOTION_TYPE)
+ {
+ sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.",cInfo->Entry,cInfo->MovementType);
+ const_cast<CreatureInfo*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
+ }
+
+ if(cInfo->equipmentId > 0) // 0 no equipment
+ {
+ if(!GetEquipmentInfo(cInfo->equipmentId))
+ {
+ sLog.outErrorDb("Table `creature_template` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", cInfo->Entry, cInfo->equipmentId);
+ const_cast<CreatureInfo*>(cInfo)->equipmentId = 0;
+ }
+ }
+
+ /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
+ if(cInfo->scale <= 0.0f)
+ {
+ CreatureDisplayInfoEntry const* ScaleEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A);
+ const_cast<CreatureInfo*>(cInfo)->scale = ScaleEntry ? ScaleEntry->scale : 1.0f;
+ }
+ }
+}
+
+void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr)
+{
+ // Now add the auras, format "spellid effectindex spellid effectindex..."
+ char *p,*s;
+ std::vector<int> val;
+ s=p=(char*)reinterpret_cast<char const*>(addon->auras);
+ if(p)
+ {
+ while (p[0]!=0)
+ {
+ ++p;
+ if (p[0]==' ')
+ {
+ val.push_back(atoi(s));
+ s=++p;
+ }
+ }
+ if (p!=s)
+ val.push_back(atoi(s));
+
+ // free char* loaded memory
+ delete[] (char*)reinterpret_cast<char const*>(addon->auras);
+
+ // wrong list
+ if (val.size()%2)
+ {
+ addon->auras = NULL;
+ sLog.outErrorDb("Creature (%s: %u) has wrong `auras` data in `%s`.",guidEntryStr,addon->guidOrEntry,table);
+ return;
+ }
+ }
+
+ // empty list
+ if(val.empty())
+ {
+ addon->auras = NULL;
+ return;
+ }
+
+ // replace by new strucutres array
+ const_cast<CreatureDataAddonAura*&>(addon->auras) = new CreatureDataAddonAura[val.size()/2+1];
+
+ int i=0;
+ for(int j=0;j<val.size()/2;++j)
+ {
+ CreatureDataAddonAura& cAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
+ cAura.spell_id = (uint32)val[2*j+0];
+ cAura.effect_idx = (uint32)val[2*j+1];
+ if ( cAura.effect_idx > 2 )
+ {
+ sLog.outErrorDb("Creature (%s: %u) has wrong effect %u for spell %u in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
+ continue;
+ }
+ SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura.spell_id);
+ if (!AdditionalSpellInfo)
+ {
+ sLog.outErrorDb("Creature (%s: %u) has wrong spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.spell_id,table);
+ continue;
+ }
+
+ if (!AdditionalSpellInfo->Effect[cAura.effect_idx] || !AdditionalSpellInfo->EffectApplyAuraName[cAura.effect_idx])
+ {
+ sLog.outErrorDb("Creature (%s: %u) has not aura effect %u of spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
+ continue;
+ }
+
+ ++i;
+ }
+
+ // fill terminator element (after last added)
+ CreatureDataAddonAura& endAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
+ endAura.spell_id = 0;
+ endAura.effect_idx = 0;
+}
+
+void ObjectMgr::LoadCreatureAddons()
+{
+ sCreatureInfoAddonStorage.Load();
+
+ sLog.outString( ">> Loaded %u creature template addons", sCreatureInfoAddonStorage.RecordCount );
+ sLog.outString();
+
+ // check data correctness and convert 'auras'
+ for(uint32 i = 1; i < sCreatureInfoAddonStorage.MaxEntry; ++i)
+ {
+ CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i);
+ if(!addon)
+ continue;
+
+ ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_template_addon", "Entry");
+
+ if(!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry))
+ sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `creature_template_addon`",addon->guidOrEntry);
+ }
+
+ sCreatureDataAddonStorage.Load();
+
+ sLog.outString( ">> Loaded %u creature addons", sCreatureDataAddonStorage.RecordCount );
+ sLog.outString();
+
+ // check data correctness and convert 'auras'
+ for(uint32 i = 1; i < sCreatureDataAddonStorage.MaxEntry; ++i)
+ {
+ CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(i);
+ if(!addon)
+ continue;
+
+ ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_addon", "GUIDLow");
+
+ if(mCreatureDataMap.find(addon->guidOrEntry)==mCreatureDataMap.end())
+ sLog.outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`",addon->guidOrEntry);
+ }
+}
+
+EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry)
+{
+ return sEquipmentStorage.LookupEntry<EquipmentInfo>(entry);
+}
+
+void ObjectMgr::LoadEquipmentTemplates()
+{
+ sEquipmentStorage.Load();
+
+ sLog.outString( ">> Loaded %u equipment template", sEquipmentStorage.RecordCount );
+ sLog.outString();
+}
+
+CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid)
+{
+ return sCreatureModelStorage.LookupEntry<CreatureModelInfo>(modelid);
+}
+
+uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data)
+{
+ // Load creature model (display id)
+ uint32 display_id;
+ if (!data || data->displayid == 0) // use defaults from the template
+ {
+ // DisplayID_A is used if no team is given
+ if (team == HORDE)
+ display_id = (cinfo->DisplayID_H2 != 0 && urand(0,1) == 0) ? cinfo->DisplayID_H2 : cinfo->DisplayID_H;
+ else
+ display_id = (cinfo->DisplayID_A2 != 0 && urand(0,1) == 0) ? cinfo->DisplayID_A2 : cinfo->DisplayID_A;
+ }
+ else // overriden in creature data
+ display_id = data->displayid;
+
+ return display_id;
+}
+
+CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_id)
+{
+ CreatureModelInfo const *minfo = GetCreatureModelInfo(display_id);
+ if(!minfo)
+ return NULL;
+
+ // If a model for another gender exists, 50% chance to use it
+ if(minfo->modelid_other_gender != 0 && urand(0,1) == 0)
+ {
+ CreatureModelInfo const *minfo_tmp = GetCreatureModelInfo(minfo->modelid_other_gender);
+ if(!minfo_tmp)
+ {
+ sLog.outErrorDb("Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", minfo->modelid, minfo->modelid_other_gender);
+ return minfo; // not fatal, just use the previous one
+ }
+ else
+ return minfo_tmp;
+ }
+ else
+ return minfo;
+}
+
+void ObjectMgr::LoadCreatureModelInfo()
+{
+ sCreatureModelStorage.Load();
+
+ sLog.outString( ">> Loaded %u creature model based info", sCreatureModelStorage.RecordCount );
+ sLog.outString();
+}
+
+void ObjectMgr::LoadCreatures()
+{
+ uint32 count = 0;
+ // 0 1 2 3
+ QueryResult *result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid,"
+ // 4 5 6 7 8 9 10 11
+ "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint,"
+ // 12 13 14 15 16 17
+ "curhealth, curmana, DeathState, MovementType, spawnMask, event "
+ "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
+ return;
+ }
+
+ // build single time for check creature data
+ std::set<uint32> heroicCreatures;
+ for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
+ if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
+ if(cInfo->HeroicEntry)
+ heroicCreatures.insert(cInfo->HeroicEntry);
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 guid = fields[0].GetUInt32();
+
+ CreatureData& data = mCreatureDataMap[guid];
+
+ data.id = fields[ 1].GetUInt32();
+ data.mapid = fields[ 2].GetUInt32();
+ data.displayid = fields[ 3].GetUInt32();
+ data.equipmentId = fields[ 4].GetUInt32();
+ data.posX = fields[ 5].GetFloat();
+ data.posY = fields[ 6].GetFloat();
+ data.posZ = fields[ 7].GetFloat();
+ data.orientation = fields[ 8].GetFloat();
+ data.spawntimesecs = fields[ 9].GetUInt32();
+ data.spawndist = fields[10].GetFloat();
+ data.currentwaypoint= fields[11].GetUInt32();
+ data.curhealth = fields[12].GetUInt32();
+ data.curmana = fields[13].GetUInt32();
+ data.is_dead = fields[14].GetBool();
+ data.movementType = fields[15].GetUInt8();
+ data.spawnMask = fields[16].GetUInt8();
+ int16 gameEvent = fields[17].GetInt16();
+
+ CreatureInfo const* cInfo = GetCreatureTemplate(data.id);
+ if(!cInfo)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u) with not existed creature entry %u, skipped.",guid,data.id );
+ continue;
+ }
+
+ if(heroicCreatures.find(data.id)!=heroicCreatures.end())
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u) that listed as heroic template in `creature_template_substitution`, skipped.",guid,data.id );
+ continue;
+ }
+
+ if(data.equipmentId > 0) // -1 no equipment, 0 use default
+ {
+ if(!GetEquipmentInfo(data.equipmentId))
+ {
+ sLog.outErrorDb("Table `creature` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", data.id, data.equipmentId);
+ data.equipmentId = -1;
+ }
+ }
+
+ if(cInfo->RegenHealth && data.curhealth < cInfo->minhealth)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenHealth`=1 and low current health (%u), `creature_template`.`minhealth`=%u.",guid,data.id,data.curhealth, cInfo->minhealth );
+ data.curhealth = cInfo->minhealth;
+ }
+
+ if(data.curmana < cInfo->minmana)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with low current mana (%u), `creature_template`.`minmana`=%u.",guid,data.id,data.curmana, cInfo->minmana );
+ data.curmana = cInfo->minmana;
+ }
+
+ if(data.spawndist < 0.0f)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.",guid,data.id );
+ data.spawndist = 0.0f;
+ }
+ else if(data.movementType == RANDOM_MOTION_TYPE)
+ {
+ if(data.spawndist == 0.0f)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=1 (random movement) but with `spawndist`=0, replace by idle movement type (0).",guid,data.id );
+ data.movementType = IDLE_MOTION_TYPE;
+ }
+ }
+ else if(data.movementType == IDLE_MOTION_TYPE)
+ {
+ if(data.spawndist != 0.0f)
+ {
+ sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.",guid,data.id );
+ data.spawndist = 0.0f;
+ }
+ }
+
+ if (gameEvent==0) // if not this is to be managed by GameEvent System
+ AddCreatureToGrid(guid, &data);
+ ++count;
+
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u creatures", mCreatureDataMap.size() );
+}
+
+void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
+{
+ uint8 mask = data->spawnMask;
+ for(uint8 i = 0; mask != 0; i++, mask >>= 1)
+ {
+ if(mask & 1)
+ {
+ CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
+ cell_guids.creatures.insert(guid);
+ }
+ }
+}
+
+void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data)
+{
+ uint8 mask = data->spawnMask;
+ for(uint8 i = 0; mask != 0; i++, mask >>= 1)
+ {
+ if(mask & 1)
+ {
+ CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
+ cell_guids.creatures.erase(guid);
+ }
+ }
+}
+
+void ObjectMgr::LoadGameobjects()
+{
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5 6
+ QueryResult *result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation,"
+ // 7 8 9 10 11 12 13 14 15
+ "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, event "
+ "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 guid = fields[0].GetUInt32();
+
+ GameObjectData& data = mGameObjectDataMap[guid];
+
+ data.id = fields[ 1].GetUInt32();
+ data.mapid = fields[ 2].GetUInt32();
+ data.posX = fields[ 3].GetFloat();
+ data.posY = fields[ 4].GetFloat();
+ data.posZ = fields[ 5].GetFloat();
+ data.orientation = fields[ 6].GetFloat();
+ data.rotation0 = fields[ 7].GetFloat();
+ data.rotation1 = fields[ 8].GetFloat();
+ data.rotation2 = fields[ 9].GetFloat();
+ data.rotation3 = fields[10].GetFloat();
+ data.spawntimesecs = fields[11].GetInt32();
+ data.animprogress = fields[12].GetUInt32();
+ data.go_state = fields[13].GetUInt32();
+ data.spawnMask = fields[14].GetUInt8();
+ int16 gameEvent = fields[15].GetInt16();
+
+ GameObjectInfo const* gInfo = GetGameObjectInfo(data.id);
+ if(!gInfo)
+ {
+ sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u) with not existed gameobject entry %u, skipped.",guid,data.id );
+ continue;
+ }
+
+ if (gameEvent==0) // if not this is to be managed by GameEvent System
+ AddGameobjectToGrid(guid, &data);
+ ++count;
+
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u gameobjects", mGameObjectDataMap.size());
+}
+
+void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
+{
+ uint8 mask = data->spawnMask;
+ for(uint8 i = 0; mask != 0; i++, mask >>= 1)
+ {
+ if(mask & 1)
+ {
+ CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
+ cell_guids.gameobjects.insert(guid);
+ }
+ }
+}
+
+void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data)
+{
+ uint8 mask = data->spawnMask;
+ for(uint8 i = 0; mask != 0; i++, mask >>= 1)
+ {
+ if(mask & 1)
+ {
+ CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
+ uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
+
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
+ cell_guids.gameobjects.erase(guid);
+ }
+ }
+}
+
+void ObjectMgr::LoadCreatureRespawnTimes()
+{
+ // remove outdated data
+ WorldDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
+
+ uint32 count = 0;
+
+ QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM creature_respawn");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded 0 creature respawn time.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 loguid = fields[0].GetUInt32();
+ uint64 respawn_time = fields[1].GetUInt64();
+ uint32 instance = fields[2].GetUInt32();
+
+ mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = time_t(respawn_time);
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString( ">> Loaded %u creature respawn times", mCreatureRespawnTimes.size() );
+ sLog.outString();
+}
+
+void ObjectMgr::LoadGameobjectRespawnTimes()
+{
+ // remove outdated data
+ WorldDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
+
+ uint32 count = 0;
+
+ QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM gameobject_respawn");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString(">> Loaded 0 gameobject respawn time.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 loguid = fields[0].GetUInt32();
+ uint64 respawn_time = fields[1].GetUInt64();
+ uint32 instance = fields[2].GetUInt32();
+
+ mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = time_t(respawn_time);
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString( ">> Loaded %u gameobject respawn times", mGORespawnTimes.size() );
+ sLog.outString();
+}
+
+// name must be checked to correctness (if received) before call this function
+uint64 ObjectMgr::GetPlayerGUIDByName(std::string name) const
+{
+ uint64 guid = 0;
+
+ CharacterDatabase.escape_string(name);
+
+ // Player name safe to sending to DB (checked at login) and this function using
+ QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE name = '%s'", name.c_str());
+ if(result)
+ {
+ guid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+
+ delete result;
+ }
+
+ return guid;
+}
+
+bool ObjectMgr::GetPlayerNameByGUID(const uint64 &guid, std::string &name) const
+{
+ // prevent DB access for online player
+ if(Player* player = GetPlayer(guid))
+ {
+ name = player->GetName();
+ return true;
+ }
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
+
+ if(result)
+ {
+ name = (*result)[0].GetCppString();
+ delete result;
+ return true;
+ }
+
+ return false;
+}
+
+uint32 ObjectMgr::GetPlayerTeamByGUID(const uint64 &guid) const
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT race FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
+
+ if(result)
+ {
+ uint8 race = (*result)[0].GetUInt8();
+ delete result;
+ return Player::TeamForRace(race);
+ }
+
+ return 0;
+}
+
+uint32 ObjectMgr::GetPlayerAccountIdByGUID(const uint64 &guid) const
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
+ if(result)
+ {
+ uint32 acc = (*result)[0].GetUInt32();
+ delete result;
+ return acc;
+ }
+
+ return 0;
+}
+
+uint32 ObjectMgr::GetSecurityByAccount(uint32 acc_id) const
+{
+ QueryResult *result = loginDatabase.PQuery("SELECT gmlevel FROM account WHERE id = '%u'", acc_id);
+ if(result)
+ {
+ uint32 sec = (*result)[0].GetUInt32();
+ delete result;
+ return sec;
+ }
+
+ return 0;
+}
+
+bool ObjectMgr::GetAccountNameByAccount(uint32 acc_id, std::string &name) const
+{
+ QueryResult *result = loginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", acc_id);
+ if(result)
+ {
+ name = (*result)[0].GetCppString();
+ delete result;
+ return true;
+ }
+
+ return false;
+}
+
+uint32 ObjectMgr::GetAccountByAccountName(std::string name) const
+{
+ loginDatabase.escape_string(name);
+ QueryResult *result = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", name.c_str());
+ if(result)
+ {
+ uint32 id = (*result)[0].GetUInt32();
+ delete result;
+ return id;
+ }
+
+ return 0;
+}
+
+void ObjectMgr::LoadAuctions()
+{
+ QueryResult *result = CharacterDatabase.Query("SELECT COUNT(*) FROM auctionhouse");
+ if( !result )
+ return;
+
+ Field *fields = result->Fetch();
+ uint32 AuctionCount=fields[0].GetUInt32();
+ delete result;
+
+ if(!AuctionCount)
+ return;
+
+ result = CharacterDatabase.Query( "SELECT id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit,location FROM auctionhouse" );
+ if( !result )
+ return;
+
+ barGoLink bar( AuctionCount );
+
+ AuctionEntry *aItem;
+
+ do
+ {
+ fields = result->Fetch();
+
+ bar.step();
+
+ aItem = new AuctionEntry;
+ aItem->Id = fields[0].GetUInt32();
+ aItem->auctioneer = fields[1].GetUInt32();
+ aItem->item_guidlow = fields[2].GetUInt32();
+ aItem->item_template = fields[3].GetUInt32();
+ aItem->owner = fields[4].GetUInt32();
+ aItem->buyout = fields[5].GetUInt32();
+ aItem->time = fields[6].GetUInt32();
+ aItem->bidder = fields[7].GetUInt32();
+ aItem->bid = fields[8].GetUInt32();
+ aItem->startbid = fields[9].GetUInt32();
+ aItem->deposit = fields[10].GetUInt32();
+ aItem->location = fields[11].GetUInt8();
+ //check if sold item exists
+ if ( this->GetAItem( aItem->item_guidlow ) )
+ {
+ GetAuctionsMap( aItem->location )->AddAuction(aItem);
+ }
+ else
+ {
+ CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",aItem->Id);
+ sLog.outError("Auction %u has not a existing item : %u", aItem->Id, aItem->item_guidlow);
+ delete aItem;
+ }
+ } while (result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u auctions", AuctionCount );
+ sLog.outString();
+}
+
+void ObjectMgr::LoadItemLocales()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry,name_loc1,description_loc1,name_loc2,description_loc2,name_loc3,description_loc3,name_loc4,description_loc4,name_loc5,description_loc5,name_loc6,description_loc6,name_loc7,description_loc7,name_loc8,description_loc8 FROM locales_item");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 Item locale strings. DB table `locales_item` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ ItemLocale& data = mItemLocaleMap[entry];
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[1+2*(i-1)].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Name.size() <= idx)
+ data.Name.resize(idx+1);
+
+ data.Name[idx] = str;
+ }
+ }
+
+ str = fields[1+2*(i-1)+1].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Description.size() <= idx)
+ data.Description.resize(idx+1);
+
+ data.Description[idx] = str;
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u Item locale strings", mItemLocaleMap.size() );
+}
+
+void ObjectMgr::LoadItemPrototypes()
+{
+ sItemStorage.Load ();
+ sLog.outString( ">> Loaded %u item prototypes", sItemStorage.RecordCount );
+ sLog.outString();
+
+ // check data correctness
+ for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i)
+ {
+ ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype >(i);
+ ItemEntry const *dbcitem = sItemStore.LookupEntry(i);
+ if(!proto)
+ {
+ /* to many errors, and possible not all items really used in game
+ if (dbcitem)
+ sLog.outErrorDb("Item (Entry: %u) doesn't exists in DB, but must exist.",i);
+ */
+ continue;
+ }
+
+ if(dbcitem)
+ {
+ if(proto->InventoryType != dbcitem->InventoryType)
+ {
+ sLog.outErrorDb("Item (Entry: %u) not correct %u inventory type, must be %u (still using DB value).",i,proto->InventoryType,dbcitem->InventoryType);
+ // It safe let use InventoryType from DB
+ }
+
+ if(proto->DisplayInfoID != dbcitem->DisplayId)
+ {
+ sLog.outErrorDb("Item (Entry: %u) not correct %u display id, must be %u (using it).",i,proto->DisplayInfoID,dbcitem->DisplayId);
+ const_cast<ItemPrototype*>(proto)->DisplayInfoID = dbcitem->DisplayId;
+ }
+ if(proto->Sheath != dbcitem->Sheath)
+ {
+ sLog.outErrorDb("Item (Entry: %u) not correct %u sheath, must be %u (using it).",i,proto->Sheath,dbcitem->Sheath);
+ const_cast<ItemPrototype*>(proto)->Sheath = dbcitem->Sheath;
+ }
+ }
+ else
+ {
+ sLog.outErrorDb("Item (Entry: %u) not correct (not listed in list of existed items).",i);
+ }
+
+ if(proto->Class >= MAX_ITEM_CLASS)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)",i,proto->Class);
+ const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_JUNK;
+ }
+
+ if(proto->SubClass >= MaxItemSubclassValues[proto->Class])
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u",i,proto->SubClass,proto->Class);
+ const_cast<ItemPrototype*>(proto)->SubClass = 0;// exist for all item classes
+ }
+
+ if(proto->Quality >= MAX_ITEM_QUALITY)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong Quality value (%u)",i,proto->Quality);
+ const_cast<ItemPrototype*>(proto)->Quality = ITEM_QUALITY_NORMAL;
+ }
+
+ if(proto->BuyCount <= 0)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).",i,proto->BuyCount);
+ const_cast<ItemPrototype*>(proto)->BuyCount = 1;
+ }
+
+ if(proto->InventoryType >= MAX_INVTYPE)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)",i,proto->InventoryType);
+ const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
+ }
+
+ if(proto->RequiredSkill >= MAX_SKILL_TYPE)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)",i,proto->RequiredSkill);
+ const_cast<ItemPrototype*>(proto)->RequiredSkill = 0;
+ }
+
+ if(!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE))
+ {
+ sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped.",i,proto->AllowableClass);
+ }
+
+ if(!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE))
+ {
+ sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped.",i,proto->AllowableRace);
+ }
+
+ if(proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell))
+ {
+ sLog.outErrorDb("Item (Entry: %u) have wrong (non-existed) spell in RequiredSpell (%u)",i,proto->RequiredSpell);
+ const_cast<ItemPrototype*>(proto)->RequiredSpell = 0;
+ }
+
+ if(proto->RequiredReputationRank >= MAX_REPUTATION_RANK)
+ sLog.outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.",i,proto->RequiredReputationRank);
+
+ if(proto->RequiredReputationFaction)
+ {
+ if(!sFactionStore.LookupEntry(proto->RequiredReputationFaction))
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)",i,proto->RequiredReputationFaction);
+ const_cast<ItemPrototype*>(proto)->RequiredReputationFaction = 0;
+ }
+
+ if(proto->RequiredReputationRank == MIN_REPUTATION_RANK)
+ sLog.outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.",i);
+ }
+ else if(proto->RequiredReputationRank > MIN_REPUTATION_RANK)
+ sLog.outErrorDb("Item (Entry: %u) has RequiredReputationFaction ==0 but RequiredReputationRank > 0, rank setting is useless.",i);
+
+ if(proto->Stackable==0)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%u), replace by default 1.",i,proto->Stackable);
+ const_cast<ItemPrototype*>(proto)->Stackable = 1;
+ }
+ else if(proto->Stackable > 255)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (255).",i,proto->Stackable);
+ const_cast<ItemPrototype*>(proto)->Stackable = 255;
+ }
+
+ for (int j = 0; j < 10; j++)
+ {
+ // for ItemStatValue != 0
+ if(proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)",i,j+1,proto->ItemStat[j].ItemStatType);
+ const_cast<ItemPrototype*>(proto)->ItemStat[j].ItemStatType = 0;
+ }
+ }
+
+ for (int j = 0; j < 5; j++)
+ {
+ if(proto->Damage[j].DamageType >= MAX_SPELL_SCHOOL)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong dmg_type%d (%u)",i,j+1,proto->Damage[j].DamageType);
+ const_cast<ItemPrototype*>(proto)->Damage[j].DamageType = 0;
+ }
+ }
+
+ // special format
+ if(proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN)
+ {
+ // spell_1
+ if(proto->Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format",i,0+1,proto->Spells[0].SpellTrigger);
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+
+ // spell_2 have learning spell
+ if(proto->Spells[1].SpellTrigger != ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format.",i,1+1,proto->Spells[1].SpellTrigger);
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+ else if(!proto->Spells[1].SpellId)
+ {
+ sLog.outErrorDb("Item (Entry: %u) not has expected spell in spellid_%d in special learning format.",i,1+1);
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+ else
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[1].SpellId);
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId);
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+ // allowed only in special format
+ else if(proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId);
+ const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+ }
+
+ // spell_3*,spell_4*,spell_5* is empty
+ for (int j = 2; j < 5; j++)
+ {
+ if(proto->Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)",i,j+1,proto->Spells[j].SpellTrigger);
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+ else if(proto->Spells[j].SpellId != 0)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong spell in spellid_%d (%u) for learning special format",i,j+1,proto->Spells[j].SpellId);
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
+ }
+ }
+ }
+ // normal spell list
+ else
+ {
+ for (int j = 0; j < 5; j++)
+ {
+ if(proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)",i,j+1,proto->Spells[j].SpellTrigger);
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
+ }
+
+ if(proto->Spells[j].SpellId)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[j].SpellId);
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId);
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
+ }
+ // allowed only in special format
+ else if(proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId);
+ const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
+ }
+ }
+ }
+ }
+
+ if(proto->Bonding >= MAX_BIND_TYPE)
+ sLog.outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)",i,proto->Bonding);
+
+ if(proto->PageText && !sPageTextStore.LookupEntry<PageText>(proto->PageText))
+ sLog.outErrorDb("Item (Entry: %u) has non existing first page (Id:%u)", i,proto->PageText);
+
+ if(proto->LockID && !sLockStore.LookupEntry(proto->LockID))
+ sLog.outErrorDb("Item (Entry: %u) has wrong LockID (%u)",i,proto->LockID);
+
+ if(proto->Sheath >= MAX_SHEATHETYPE)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong Sheath (%u)",i,proto->Sheath);
+ const_cast<ItemPrototype*>(proto)->Sheath = SHEATHETYPE_NONE;
+ }
+
+ if(proto->RandomProperty && !sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(proto->RandomProperty)))
+ {
+ sLog.outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)",i,proto->RandomProperty);
+ const_cast<ItemPrototype*>(proto)->RandomProperty = 0;
+ }
+
+ if(proto->RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(proto->RandomSuffix)))
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong RandomSuffix (%u)",i,proto->RandomSuffix);
+ const_cast<ItemPrototype*>(proto)->RandomSuffix = 0;
+ }
+
+ if(proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet))
+ {
+ sLog.outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)",i,proto->ItemSet);
+ const_cast<ItemPrototype*>(proto)->ItemSet = 0;
+ }
+
+ if(proto->Area && !GetAreaEntryByAreaID(proto->Area))
+ sLog.outErrorDb("Item (Entry: %u) has wrong Area (%u)",i,proto->Area);
+
+ if(proto->Map && !sMapStore.LookupEntry(proto->Map))
+ sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)",i,proto->Map);
+
+ if(proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory))
+ sLog.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)",i,proto->TotemCategory);
+
+ for (int j = 0; j < 3; j++)
+ {
+ if(proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong socketColor_%d (%u)",i,j+1,proto->Socket[j].Color);
+ const_cast<ItemPrototype*>(proto)->Socket[j].Color = 0;
+ }
+ }
+
+ if(proto->GemProperties && !sGemPropertiesStore.LookupEntry(proto->GemProperties))
+ sLog.outErrorDb("Item (Entry: %u) has wrong GemProperties (%u)",i,proto->GemProperties);
+
+ if(proto->FoodType >= MAX_PET_DIET)
+ {
+ sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)",i,proto->FoodType);
+ const_cast<ItemPrototype*>(proto)->FoodType = 0;
+ }
+ }
+
+ // this DBC used currently only for check item templates in DB.
+ sItemStore.Clear();
+}
+
+void ObjectMgr::LoadAuctionItems()
+{
+ QueryResult *result = CharacterDatabase.Query( "SELECT itemguid,item_template FROM auctionhouse" );
+
+ if( !result )
+ return;
+
+ barGoLink bar( result->GetRowCount() );
+
+ uint32 count = 0;
+
+ Field *fields;
+ do
+ {
+ bar.step();
+
+ fields = result->Fetch();
+ uint32 item_guid = fields[0].GetUInt32();
+ uint32 item_template = fields[1].GetUInt32();
+
+ ItemPrototype const *proto = GetItemPrototype(item_template);
+
+ if(!proto)
+ {
+ sLog.outError( "ObjectMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid,item_template);
+ continue;
+ }
+
+ Item *item = NewItemOrBag(proto);
+
+ if(!item->LoadFromDB(item_guid,0))
+ {
+ delete item;
+ continue;
+ }
+ AddAItem(item);
+
+ ++count;
+ }
+ while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u auction items", count );
+}
+
+void ObjectMgr::LoadPetLevelInfo()
+{
+ // Loading levels data
+ {
+ // 0 1 2 3 4 5 6 7 8 9
+ QueryResult *result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level pet stats definitions", count );
+ sLog.outErrorDb( "Error loading `pet_levelstats` table or empty table.");
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 creature_id = fields[0].GetUInt32();
+ if(!sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
+ {
+ sLog.outErrorDb("Wrong creature id %u in `pet_levelstats` table, ignoring.",creature_id);
+ continue;
+ }
+
+ uint32 current_level = fields[1].GetUInt32();
+ if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ if(current_level > 255) // hardcoded level maximum
+ sLog.outErrorDb("Wrong (> 255) level %u in `pet_levelstats` table, ignoring.",current_level);
+ else
+ sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `pet_levelstats` table, ignoring.",current_level);
+ continue;
+ }
+ else if(current_level < 1)
+ {
+ sLog.outErrorDb("Wrong (<1) level %u in `pet_levelstats` table, ignoring.",current_level);
+ continue;
+ }
+
+ PetLevelInfo*& pInfoMapEntry = petInfo[creature_id];
+
+ if(pInfoMapEntry==NULL)
+ pInfoMapEntry = new PetLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
+
+ // data for level 1 stored in [0] array element, ...
+ PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level-1];
+
+ pLevelInfo->health = fields[2].GetUInt16();
+ pLevelInfo->mana = fields[3].GetUInt16();
+ pLevelInfo->armor = fields[9].GetUInt16();
+
+ for (int i = 0; i < MAX_STATS; i++)
+ {
+ pLevelInfo->stats[i] = fields[i+4].GetUInt16();
+ }
+
+ bar.step();
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level pet stats definitions", count );
+ }
+
+ // Fill gaps and check integrity
+ for (PetLevelInfoMap::iterator itr = petInfo.begin(); itr != petInfo.end(); ++itr)
+ {
+ PetLevelInfo* pInfo = itr->second;
+
+ // fatal error if no level 1 data
+ if(!pInfo || pInfo[0].health == 0 )
+ {
+ sLog.outErrorDb("Creature %u does not have pet stats data for Level 1!",itr->first);
+ exit(1);
+ }
+
+ // fill level gaps
+ for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
+ {
+ if(pInfo[level].health == 0)
+ {
+ sLog.outErrorDb("Creature %u has no data for Level %i pet stats data, using data of Level %i.",itr->first,level+1, level);
+ pInfo[level] = pInfo[level-1];
+ }
+ }
+ }
+}
+
+PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint32 level) const
+{
+ if(level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
+
+ PetLevelInfoMap::const_iterator itr = petInfo.find(creature_id);
+ if(itr == petInfo.end())
+ return NULL;
+
+ return &itr->second[level-1]; // data for level 1 stored in [0] array element, ...
+}
+
+void ObjectMgr::LoadPlayerInfo()
+{
+ // Load playercreate
+ {
+ // 0 1 2 3 4 5 6
+ QueryResult *result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z FROM playercreateinfo");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create definitions", count );
+ sLog.outErrorDb( "Error loading `playercreateinfo` table or empty table.");
+ exit(1);
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_race = fields[0].GetUInt32();
+ uint32 current_class = fields[1].GetUInt32();
+ uint32 mapId = fields[2].GetUInt32();
+ uint32 zoneId = fields[3].GetUInt32();
+ float positionX = fields[4].GetFloat();
+ float positionY = fields[5].GetFloat();
+ float positionZ = fields[6].GetFloat();
+
+ if(current_race >= MAX_RACES)
+ {
+ sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.",current_race);
+ continue;
+ }
+
+ ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
+ if(!rEntry)
+ {
+ sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.",current_race);
+ continue;
+ }
+
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.",current_class);
+ continue;
+ }
+
+ if(!sChrClassesStore.LookupEntry(current_class))
+ {
+ sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.",current_class);
+ continue;
+ }
+
+ // accept DB data only for valid position (and non instanceable)
+ if( !MapManager::IsValidMapCoord(mapId,positionX,positionY,positionZ) )
+ {
+ sLog.outErrorDb("Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.",current_class,current_race);
+ continue;
+ }
+
+ if( sMapStore.LookupEntry(mapId)->Instanceable() )
+ {
+ sLog.outErrorDb("Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.",current_class,current_race);
+ continue;
+ }
+
+ PlayerInfo* pInfo = &playerInfo[current_race][current_class];
+
+ pInfo->mapId = mapId;
+ pInfo->zoneId = zoneId;
+ pInfo->positionX = positionX;
+ pInfo->positionY = positionY;
+ pInfo->positionZ = positionZ;
+
+ pInfo->displayId_m = rEntry->model_m;
+ pInfo->displayId_f = rEntry->model_f;
+
+ bar.step();
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create definitions", count );
+ }
+
+ // Load playercreate items
+ {
+ // 0 1 2 3
+ QueryResult *result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create items", count );
+ sLog.outErrorDb( "Error loading `playercreateinfo_item` table or empty table.");
+ }
+ else
+ {
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_race = fields[0].GetUInt32();
+ if(current_race >= MAX_RACES)
+ {
+ sLog.outErrorDb("Wrong race %u in `playercreateinfo_item` table, ignoring.",current_race);
+ continue;
+ }
+
+ uint32 current_class = fields[1].GetUInt32();
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `playercreateinfo_item` table, ignoring.",current_class);
+ continue;
+ }
+
+ PlayerInfo* pInfo = &playerInfo[current_race][current_class];
+
+ uint32 item_id = fields[2].GetUInt32();
+
+ if(!GetItemPrototype(item_id))
+ {
+ sLog.outErrorDb("Item id %u (race %u class %u) in `playercreateinfo_item` table but not listed in `item_template`, ignoring.",item_id,current_race,current_class);
+ continue;
+ }
+
+ uint32 amount = fields[3].GetUInt32();
+
+ if(!amount)
+ {
+ sLog.outErrorDb("Item id %u (class %u race %u) have amount==0 in `playercreateinfo_item` table, ignoring.",item_id,current_race,current_class);
+ continue;
+ }
+
+ pInfo->item.push_back(PlayerCreateInfoItem( item_id, amount));
+
+ bar.step();
+ ++count;
+ }
+ while(result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create items", count );
+ }
+ }
+
+ // Load playercreate spells
+ {
+
+ QueryResult *result = NULL;
+ if(sWorld.getConfig(CONFIG_START_ALL_SPELLS))
+ result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell_custom");
+ else
+ result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create spells", count );
+ sLog.outErrorDb( "Error loading player starting spells or empty table.");
+ }
+ else
+ {
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_race = fields[0].GetUInt32();
+ if(current_race >= MAX_RACES)
+ {
+ sLog.outErrorDb("Wrong race %u in `playercreateinfo_spell` table, ignoring.",current_race);
+ continue;
+ }
+
+ uint32 current_class = fields[1].GetUInt32();
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `playercreateinfo_spell` table, ignoring.",current_class);
+ continue;
+ }
+
+ PlayerInfo* pInfo = &playerInfo[current_race][current_class];
+ pInfo->spell.push_back(CreateSpellPair(fields[2].GetUInt16(), fields[3].GetUInt8()));
+
+ bar.step();
+ ++count;
+ }
+ while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create spells", count );
+ }
+ }
+
+ // Load playercreate actions
+ {
+ // 0 1 2 3 4 5
+ QueryResult *result = WorldDatabase.Query("SELECT race, class, button, action, type, misc FROM playercreateinfo_action");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create actions", count );
+ sLog.outErrorDb( "Error loading `playercreateinfo_action` table or empty table.");
+ }
+ else
+ {
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_race = fields[0].GetUInt32();
+ if(current_race >= MAX_RACES)
+ {
+ sLog.outErrorDb("Wrong race %u in `playercreateinfo_action` table, ignoring.",current_race);
+ continue;
+ }
+
+ uint32 current_class = fields[1].GetUInt32();
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `playercreateinfo_action` table, ignoring.",current_class);
+ continue;
+ }
+
+ PlayerInfo* pInfo = &playerInfo[current_race][current_class];
+ pInfo->action[0].push_back(fields[2].GetUInt16());
+ pInfo->action[1].push_back(fields[3].GetUInt16());
+ pInfo->action[2].push_back(fields[4].GetUInt16());
+ pInfo->action[3].push_back(fields[5].GetUInt16());
+
+ bar.step();
+ ++count;
+ }
+ while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u player create actions", count );
+ }
+ }
+
+ // Loading levels data (class only dependent)
+ {
+ // 0 1 2 3
+ QueryResult *result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level health/mana definitions", count );
+ sLog.outErrorDb( "Error loading `player_classlevelstats` table or empty table.");
+ exit(1);
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_class = fields[0].GetUInt32();
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `player_classlevelstats` table, ignoring.",current_class);
+ continue;
+ }
+
+ uint32 current_level = fields[1].GetUInt32();
+ if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ if(current_level > 255) // hardcoded level maximum
+ sLog.outErrorDb("Wrong (> 255) level %u in `player_classlevelstats` table, ignoring.",current_level);
+ else
+ sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_classlevelstats` table, ignoring.",current_level);
+ continue;
+ }
+
+ PlayerClassInfo* pClassInfo = &playerClassInfo[current_class];
+
+ if(!pClassInfo->levelInfo)
+ pClassInfo->levelInfo = new PlayerClassLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
+
+ PlayerClassLevelInfo* pClassLevelInfo = &pClassInfo->levelInfo[current_level-1];
+
+ pClassLevelInfo->basehealth = fields[2].GetUInt16();
+ pClassLevelInfo->basemana = fields[3].GetUInt16();
+
+ bar.step();
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level health/mana definitions", count );
+ }
+
+ // Fill gaps and check integrity
+ for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
+ {
+ // skip non existed classes
+ if(!sChrClassesStore.LookupEntry(class_))
+ continue;
+
+ PlayerClassInfo* pClassInfo = &playerClassInfo[class_];
+
+ // fatal error if no level 1 data
+ if(!pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0 )
+ {
+ sLog.outErrorDb("Class %i Level 1 does not have health/mana data!",class_);
+ exit(1);
+ }
+
+ // fill level gaps
+ for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
+ {
+ if(pClassInfo->levelInfo[level].basehealth == 0)
+ {
+ sLog.outErrorDb("Class %i Level %i does not have health/mana data. Using stats data of level %i.",class_,level+1, level);
+ pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level-1];
+ }
+ }
+ }
+
+ // Loading levels data (class/race dependent)
+ {
+ // 0 1 2 3 4 5 6 7
+ QueryResult *result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
+
+ uint32 count = 0;
+
+ if (!result)
+ {
+ barGoLink bar( 1 );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level stats definitions", count );
+ sLog.outErrorDb( "Error loading `player_levelstats` table or empty table.");
+ exit(1);
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ uint32 current_race = fields[0].GetUInt32();
+ if(current_race >= MAX_RACES)
+ {
+ sLog.outErrorDb("Wrong race %u in `player_levelstats` table, ignoring.",current_race);
+ continue;
+ }
+
+ uint32 current_class = fields[1].GetUInt32();
+ if(current_class >= MAX_CLASSES)
+ {
+ sLog.outErrorDb("Wrong class %u in `player_levelstats` table, ignoring.",current_class);
+ continue;
+ }
+
+ uint32 current_level = fields[2].GetUInt32();
+ if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ if(current_level > 255) // hardcoded level maximum
+ sLog.outErrorDb("Wrong (> 255) level %u in `player_levelstats` table, ignoring.",current_level);
+ else
+ sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_levelstats` table, ignoring.",current_level);
+ continue;
+ }
+
+ PlayerInfo* pInfo = &playerInfo[current_race][current_class];
+
+ if(!pInfo->levelInfo)
+ pInfo->levelInfo = new PlayerLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
+
+ PlayerLevelInfo* pLevelInfo = &pInfo->levelInfo[current_level-1];
+
+ for (int i = 0; i < MAX_STATS; i++)
+ {
+ pLevelInfo->stats[i] = fields[i+3].GetUInt8();
+ }
+
+ bar.step();
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u level stats definitions", count );
+ }
+
+ // Fill gaps and check integrity
+ for (int race = 0; race < MAX_RACES; ++race)
+ {
+ // skip non existed races
+ if(!sChrRacesStore.LookupEntry(race))
+ continue;
+
+ for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
+ {
+ // skip non existed classes
+ if(!sChrClassesStore.LookupEntry(class_))
+ continue;
+
+ PlayerInfo* pInfo = &playerInfo[race][class_];
+
+ // skip non loaded combinations
+ if(!pInfo->displayId_m || !pInfo->displayId_f)
+ continue;
+
+ // skip expansion races if not playing with expansion
+ if (sWorld.getConfig(CONFIG_EXPANSION) < 1 && (race == RACE_BLOODELF || race == RACE_DRAENEI))
+ continue;
+
+ // fatal error if no level 1 data
+ if(!pInfo->levelInfo || pInfo->levelInfo[0].stats[0] == 0 )
+ {
+ sLog.outErrorDb("Race %i Class %i Level 1 does not have stats data!",race,class_);
+ exit(1);
+ }
+
+ // fill level gaps
+ for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
+ {
+ if(pInfo->levelInfo[level].stats[0] == 0)
+ {
+ sLog.outErrorDb("Race %i Class %i Level %i does not have stats data. Using stats data of level %i.",race,class_,level+1, level);
+ pInfo->levelInfo[level] = pInfo->levelInfo[level-1];
+ }
+ }
+ }
+ }
+}
+
+void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint32 level, PlayerClassLevelInfo* info) const
+{
+ if(level < 1 || class_ >= MAX_CLASSES)
+ return;
+
+ PlayerClassInfo const* pInfo = &playerClassInfo[class_];
+
+ if(level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
+
+ *info = pInfo->levelInfo[level-1];
+}
+
+void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint32 level, PlayerLevelInfo* info) const
+{
+ if(level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
+ return;
+
+ PlayerInfo const* pInfo = &playerInfo[race][class_];
+ if(pInfo->displayId_m==0 || pInfo->displayId_f==0)
+ return;
+
+ if(level <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ *info = pInfo->levelInfo[level-1];
+ else
+ BuildPlayerLevelInfo(race,class_,level,info);
+}
+
+void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
+{
+ // base data (last known level)
+ *info = playerInfo[race][_class].levelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1];
+
+ for(int lvl = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl)
+ {
+ switch(_class)
+ {
+ case CLASS_WARRIOR:
+ info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ break;
+ case CLASS_PALADIN:
+ info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
+ info->stats[STAT_SPIRIT] += (lvl > 7 ? 1: 0);
+ break;
+ case CLASS_HUNTER:
+ info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
+ info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
+ info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
+ break;
+ case CLASS_ROGUE:
+ info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
+ info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
+ break;
+ case CLASS_PRIEST:
+ info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
+ info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_SPIRIT] += (lvl > 3 ? 1: 0);
+ break;
+ case CLASS_SHAMAN:
+ info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
+ info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
+ info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
+ info->stats[STAT_SPIRIT] += (lvl > 4 ? 1: 0);
+ break;
+ case CLASS_MAGE:
+ info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
+ info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
+ info->stats[STAT_SPIRIT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
+ break;
+ case CLASS_WARLOCK:
+ info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
+ info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
+ info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
+ info->stats[STAT_SPIRIT] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
+ break;
+ case CLASS_DRUID:
+ info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
+ info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
+ info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
+ info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
+ info->stats[STAT_SPIRIT] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0));
+ }
+ }
+}
+
+void ObjectMgr::LoadGuilds()
+{
+ Guild *newguild;
+ uint32 count = 0;
+
+ QueryResult *result = CharacterDatabase.Query( "SELECT guildid FROM guild" );
+
+ if( !result )
+ {
+
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u guild definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+ ++count;
+
+ newguild = new Guild;
+ if(!newguild->LoadGuildFromDB(fields[0].GetUInt32()))
+ {
+ newguild->Disband();
+ delete newguild;
+ continue;
+ }
+ AddGuild(newguild);
+
+ }while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u guild definitions", count );
+}
+
+void ObjectMgr::LoadArenaTeams()
+{
+ uint32 count = 0;
+
+ QueryResult *result = CharacterDatabase.Query( "SELECT arenateamid FROM arena_team" );
+
+ if( !result )
+ {
+
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u arenateam definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+ ++count;
+
+ ArenaTeam *newarenateam = new ArenaTeam;
+ if(!newarenateam->LoadArenaTeamFromDB(fields[0].GetUInt32()))
+ {
+ delete newarenateam;
+ continue;
+ }
+ AddArenaTeam(newarenateam);
+ }while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u arenateam definitions", count );
+}
+
+void ObjectMgr::LoadGroups()
+{
+ // -- loading groups --
+ Group *group = NULL;
+ uint64 leaderGuid = 0;
+ uint32 count = 0;
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ QueryResult *result = CharacterDatabase.PQuery("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty, leaderGuid FROM groups");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u group definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+ ++count;
+ leaderGuid = MAKE_NEW_GUID(fields[15].GetUInt32(),0,HIGHGUID_PLAYER);
+
+ group = new Group;
+ if(!group->LoadGroupFromDB(leaderGuid, result, false))
+ {
+ group->Disband();
+ delete group;
+ continue;
+ }
+ AddGroup(group);
+ }while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u group definitions", count );
+
+ // -- loading members --
+ count = 0;
+ group = NULL;
+ leaderGuid = 0;
+ // 0 1 2 3
+ result = CharacterDatabase.PQuery("SELECT memberGuid, assistant, subgroup, leaderGuid FROM group_member ORDER BY leaderGuid");
+ if(!result)
+ {
+ barGoLink bar( 1 );
+ bar.step();
+ }
+ else
+ {
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+ count++;
+ leaderGuid = MAKE_NEW_GUID(fields[3].GetUInt32(), 0, HIGHGUID_PLAYER);
+ if(!group || group->GetLeaderGUID() != leaderGuid)
+ {
+ group = GetGroupByLeader(leaderGuid);
+ if(!group)
+ {
+ sLog.outErrorDb("Incorrect entry in group_member table : no group with leader %d for member %d!", fields[3].GetUInt32(), fields[0].GetUInt32());
+ CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
+ continue;
+ }
+ }
+
+ if(!group->LoadMemberFromDB(fields[0].GetUInt32(), fields[2].GetUInt8(), fields[1].GetBool()))
+ {
+ sLog.outErrorDb("Incorrect entry in group_member table : member %d cannot be added to player %d's group!", fields[0].GetUInt32(), fields[3].GetUInt32());
+ CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
+ }
+ }while( result->NextRow() );
+ delete result;
+ }
+
+ // clean groups
+ // TODO: maybe delete from the DB before loading in this case
+ for(GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end();)
+ {
+ if((*itr)->GetMembersCount() < 2)
+ {
+ (*itr)->Disband();
+ delete *itr;
+ mGroupSet.erase(itr++);
+ }
+ else
+ ++itr;
+ }
+
+ // -- loading instances --
+ count = 0;
+ group = NULL;
+ leaderGuid = 0;
+ result = CharacterDatabase.PQuery(
+ // 0 1 2 3 4 5
+ "SELECT leaderGuid, map, instance, permanent, difficulty, resettime, "
+ // 6
+ "(SELECT COUNT(*) FROM character_instance WHERE guid = leaderGuid AND instance = group_instance.instance AND permanent = 1 LIMIT 1) "
+ "FROM group_instance LEFT JOIN instance ON instance = id ORDER BY leaderGuid"
+ );
+
+ if(!result)
+ {
+ barGoLink bar( 1 );
+ bar.step();
+ }
+ else
+ {
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+ count++;
+ leaderGuid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ if(!group || group->GetLeaderGUID() != leaderGuid)
+ {
+ group = GetGroupByLeader(leaderGuid);
+ if(!group)
+ {
+ sLog.outErrorDb("Incorrect entry in group_instance table : no group with leader %d", fields[0].GetUInt32());
+ continue;
+ }
+ }
+
+ InstanceSave *save = sInstanceSaveManager.AddInstanceSave(fields[1].GetUInt32(), fields[2].GetUInt32(), fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true);
+ group->BindToInstance(save, fields[3].GetBool(), true);
+ }while( result->NextRow() );
+ delete result;
+ }
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u group-instance binds total", count );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u group members total", count );
+}
+
+void ObjectMgr::LoadQuests()
+{
+ // For reload case
+ for(QuestMap::const_iterator itr=mQuestTemplates.begin(); itr != mQuestTemplates.end(); ++itr)
+ delete itr->second;
+ mQuestTemplates.clear();
+
+ mExclusiveQuestGroups.clear();
+
+ // 0 1 2 3 4 5 6 7 8
+ QueryResult *result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue,"
+ // 9 10 11 12 13 14 15 16
+ "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
+ // 17 18 19 20 21 22 23 24 25 26
+ "QuestFlags, SpecialFlags, CharTitleId, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell,"
+ // 27 28 29 30 31 32 33 34 35 36
+ "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
+ // 37 38 39 40 41 42 43 44
+ "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4,"
+ // 45 46 47 48 49 50 51 52 53 54 54 55
+ "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, ReqSourceRef1, ReqSourceRef2, ReqSourceRef3, ReqSourceRef4,"
+ // 57 58 59 60 61 62 63 64
+ "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
+ // 65 66 67 68
+ "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
+ // 69 70 71 72 73 74
+ "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
+ // 75 76 77 78 79 80
+ "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
+ // 81 82 83 84 85 86 87 88
+ "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
+ // 89 90 91 92 93 94 95 96 97 98
+ "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
+ // 99 100 101 102 103 104 105 106 107 108
+ "RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
+ // 109 110 111 112 113 114 115 116 117 118
+ "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4,IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
+ // 119 120
+ "StartScript, CompleteScript"
+ " FROM quest_template");
+ if(result == NULL)
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded 0 quests definitions" );
+ sLog.outErrorDb("`quest_template` table is empty!");
+ return;
+ }
+
+ // create multimap previous quest for each existed quest
+ // some quests can have many previous maps set by NextQuestId in previous quest
+ // for example set of race quests can lead to single not race specific quest
+ barGoLink bar( result->GetRowCount() );
+ do
+ {
+ bar.step();
+ Field *fields = result->Fetch();
+
+ Quest * newQuest = new Quest(fields);
+ mQuestTemplates[newQuest->GetQuestId()] = newQuest;
+ } while( result->NextRow() );
+
+ delete result;
+
+ // Post processing
+ for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); iter++)
+ {
+ Quest * qinfo = iter->second;
+
+ // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
+
+ if( qinfo->GetQuestMethod() >= 3 )
+ {
+ sLog.outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.",qinfo->GetQuestId(),qinfo->GetQuestMethod());
+ }
+
+ if (qinfo->QuestFlags & ~QUEST_MANGOS_FLAGS_DB_ALLOWED)
+ {
+ sLog.outErrorDb("Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
+ qinfo->GetQuestId(),qinfo->QuestFlags,QUEST_MANGOS_FLAGS_DB_ALLOWED >> 16);
+ qinfo->QuestFlags &= QUEST_MANGOS_FLAGS_DB_ALLOWED;
+ }
+
+ if(qinfo->QuestFlags & QUEST_FLAGS_DAILY)
+ {
+ if(!(qinfo->QuestFlags & QUEST_MANGOS_FLAGS_REPEATABLE))
+ {
+ sLog.outErrorDb("Daily Quest %u not marked as repeatable in `SpecialFlags`, added.",qinfo->GetQuestId());
+ qinfo->QuestFlags |= QUEST_MANGOS_FLAGS_REPEATABLE;
+ }
+ }
+
+ if(qinfo->QuestFlags & QUEST_FLAGS_AUTO_REWARDED)
+ {
+ // at auto-reward can be rewarded only RewChoiceItemId[0]
+ for(int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j )
+ {
+ if(uint32 id = qinfo->RewChoiceItemId[j])
+ {
+ sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item from `RewChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ // no changes, quest ignore this data
+ }
+ }
+ }
+
+ // client quest log visual (area case)
+ if( qinfo->ZoneOrSort > 0 )
+ {
+ if(!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
+ {
+ sLog.outErrorDb("Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
+ qinfo->GetQuestId(),qinfo->ZoneOrSort);
+ // no changes, quest not dependent from this value but can have problems at client
+ }
+ }
+ // client quest log visual (sort case)
+ if( qinfo->ZoneOrSort < 0 )
+ {
+ QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
+ if( !qSort )
+ {
+ sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
+ qinfo->GetQuestId(),qinfo->ZoneOrSort);
+ // no changes, quest not dependent from this value but can have problems at client (note some may be 0, we must allow this so no check)
+ }
+ //check SkillOrClass value (class case).
+ if( ClassByQuestSort(-int32(qinfo->ZoneOrSort)) )
+ {
+ // SkillOrClass should not have class case when class case already set in ZoneOrSort.
+ if(qinfo->SkillOrClass < 0)
+ {
+ sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (class sort case) and `SkillOrClass` = %i (class case), redundant.",
+ qinfo->GetQuestId(),qinfo->ZoneOrSort,qinfo->SkillOrClass);
+ }
+ }
+ //check for proper SkillOrClass value (skill case)
+ if(int32 skill_id = SkillByQuestSort(-int32(qinfo->ZoneOrSort)))
+ {
+ // skill is positive value in SkillOrClass
+ if(qinfo->SkillOrClass != skill_id )
+ {
+ sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (skill sort case) but `SkillOrClass` does not have a corresponding value (%i).",
+ qinfo->GetQuestId(),qinfo->ZoneOrSort,skill_id);
+ //override, and force proper value here?
+ }
+ }
+ }
+
+ // SkillOrClass (class case)
+ if( qinfo->SkillOrClass < 0 )
+ {
+ if( !sChrClassesStore.LookupEntry(-int32(qinfo->SkillOrClass)) )
+ {
+ sLog.outErrorDb("Quest %u has `SkillOrClass` = %i (class case) but class (%i) does not exist",
+ qinfo->GetQuestId(),qinfo->SkillOrClass,-qinfo->SkillOrClass);
+ }
+ }
+ // SkillOrClass (skill case)
+ if( qinfo->SkillOrClass > 0 )
+ {
+ if( !sSkillLineStore.LookupEntry(qinfo->SkillOrClass) )
+ {
+ sLog.outErrorDb("Quest %u has `SkillOrClass` = %u (skill case) but skill (%i) does not exist",
+ qinfo->GetQuestId(),qinfo->SkillOrClass,qinfo->SkillOrClass);
+ }
+ }
+
+ if( qinfo->RequiredSkillValue )
+ {
+ if( qinfo->RequiredSkillValue > sWorld.GetConfigMaxSkillValue() )
+ {
+ sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but max possible skill is %u, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RequiredSkillValue,sWorld.GetConfigMaxSkillValue());
+ // no changes, quest can't be done for this requirement
+ }
+
+ if( qinfo->SkillOrClass <= 0 )
+ {
+ sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but `SkillOrClass` = %i (class case), value ignored.",
+ qinfo->GetQuestId(),qinfo->RequiredSkillValue,qinfo->SkillOrClass);
+ // no changes, quest can't be done for this requirement (fail at wrong skill id)
+ }
+ }
+ // else Skill quests can have 0 skill level, this is ok
+
+ if(qinfo->RepObjectiveFaction && !sFactionStore.LookupEntry(qinfo->RepObjectiveFaction))
+ {
+ sLog.outErrorDb("Quest %u has `RepObjectiveFaction` = %u but faction template %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RepObjectiveFaction,qinfo->RepObjectiveFaction);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RequiredMinRepFaction,qinfo->RequiredMinRepFaction);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RequiredMaxRepFaction,qinfo->RequiredMaxRepFaction);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > Player::Reputation_Cap)
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RequiredMinRepValue,Player::Reputation_Cap);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RequiredMaxRepValue,qinfo->RequiredMinRepValue);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(!qinfo->RepObjectiveFaction && qinfo->RepObjectiveValue > 0 )
+ {
+ sLog.outErrorDb("Quest %u has `RepObjectiveValue` = %d but `RepObjectiveFaction` is 0, value has no effect",
+ qinfo->GetQuestId(),qinfo->RepObjectiveValue);
+ // warning
+ }
+
+ if(!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue > 0 )
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
+ qinfo->GetQuestId(),qinfo->RequiredMinRepValue);
+ // warning
+ }
+
+ if(!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue > 0 )
+ {
+ sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
+ qinfo->GetQuestId(),qinfo->RequiredMaxRepValue);
+ // warning
+ }
+
+ if(qinfo->CharTitleId && !sCharTitlesStore.LookupEntry(qinfo->CharTitleId))
+ {
+ sLog.outErrorDb("Quest %u has `CharTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.",
+ qinfo->GetQuestId(),qinfo->GetCharTitleId(),qinfo->GetCharTitleId());
+ qinfo->CharTitleId = 0;
+ // quest can't reward this title
+ }
+
+ if(qinfo->SrcItemId)
+ {
+ if(!sItemStorage.LookupEntry<ItemPrototype>(qinfo->SrcItemId))
+ {
+ sLog.outErrorDb("Quest %u has `SrcItemId` = %u but item with entry %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->SrcItemId,qinfo->SrcItemId);
+ qinfo->SrcItemId = 0; // quest can't be done for this requirement
+ }
+ else if(qinfo->SrcItemCount==0)
+ {
+ sLog.outErrorDb("Quest %u has `SrcItemId` = %u but `SrcItemCount` = 0, set to 1 but need fix in DB.",
+ qinfo->GetQuestId(),qinfo->SrcItemId);
+ qinfo->SrcItemCount = 1; // update to 1 for allow quest work for backward comptibility with DB
+ }
+ }
+ else if(qinfo->SrcItemCount>0)
+ {
+ sLog.outErrorDb("Quest %u has `SrcItemId` = 0 but `SrcItemCount` = %u, useless value.",
+ qinfo->GetQuestId(),qinfo->SrcItemCount);
+ qinfo->SrcItemCount=0; // no quest work changes in fact
+ }
+
+ if(qinfo->SrcSpell)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->SrcSpell);
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u doesn't exist, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->SrcSpell,qinfo->SrcSpell);
+ qinfo->SrcSpell = 0; // quest can't be done for this requirement
+ }
+ else if(!SpellMgr::IsSpellValid(spellInfo))
+ {
+ sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u is broken, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->SrcSpell,qinfo->SrcSpell);
+ qinfo->SrcSpell = 0; // quest can't be done for this requirement
+ }
+ }
+
+ for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
+ {
+ uint32 id = qinfo->ReqItemId[j];
+ if(id)
+ {
+ if(qinfo->ReqItemCount[j]==0)
+ {
+ sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but `ReqItemCount%d` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ // no changes, quest can't be done for this requirement
+ }
+
+ qinfo->SetFlag(QUEST_MANGOS_FLAGS_DELIVER);
+
+ if(!sItemStorage.LookupEntry<ItemPrototype>(id))
+ {
+ sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,id);
+ qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
+ }
+ }
+ else if(qinfo->ReqItemCount[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `ReqItemId%d` = 0 but `ReqItemCount%d` = %u, quest can't be done.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->ReqItemCount[j]);
+ qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
+ }
+ }
+
+ for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j )
+ {
+ uint32 id = qinfo->ReqSourceId[j];
+ if(id)
+ {
+ if(!sItemStorage.LookupEntry<ItemPrototype>(id))
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but item with entry %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,id);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(!qinfo->ReqSourceCount[j])
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceCount%d` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest
+ }
+
+ if(!qinfo->ReqSourceRef[j])
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceRef%d` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest
+ }
+ }
+ else
+ {
+ if(qinfo->ReqSourceCount[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceCount%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceCount[j]);
+ // no changes, quest ignore this data
+ }
+
+ if(qinfo->ReqSourceRef[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceRef%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceRef[j]);
+ // no changes, quest ignore this data
+ }
+ }
+ }
+
+ for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j )
+ {
+ uint32 ref = qinfo->ReqSourceRef[j];
+ if(ref)
+ {
+ if(ref > QUEST_OBJECTIVES_COUNT)
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but max value in `ReqSourceRef%d` is %u, quest can't be done.",
+ qinfo->GetQuestId(),j+1,ref,j+1,QUEST_OBJECTIVES_COUNT);
+ // no changes, quest can't be done for this requirement
+ }
+ else
+ if(!qinfo->ReqItemId[ref-1] && !qinfo->ReqSpell[ref-1])
+ {
+ sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but `ReqItemId%u` = 0 and `ReqSpellCast%u` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,ref,ref,ref);
+ // no changes, quest can't be done for this requirement
+ }
+ else if(qinfo->ReqItemId[ref-1] && qinfo->ReqSpell[ref-1])
+ {
+ sLog.outErrorDb("Quest %u has `ReqItemId%u` = %u and `ReqSpellCast%u` = %u, quest can't have both fields <> 0, then can't be done.",
+ qinfo->GetQuestId(),ref,qinfo->ReqItemId[ref-1],ref,qinfo->ReqSpell[ref-1]);
+ // no changes, quest can't be done for this requirement
+ qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest
+ }
+ }
+ }
+
+ for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
+ {
+ uint32 id = qinfo->ReqSpell[j];
+ if(id)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,id);
+ // no changes, quest can't be done for this requirement
+ }
+
+ if(!qinfo->ReqCreatureOrGOId[j])
+ {
+ bool found = false;
+ for(int k = 0; k < 3; ++k)
+ {
+ if( spellInfo->Effect[k]==SPELL_EFFECT_QUEST_COMPLETE && uint32(spellInfo->EffectMiscValue[k])==qinfo->QuestId ||
+ spellInfo->Effect[k]==SPELL_EFFECT_SEND_EVENT)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if(found)
+ {
+ if(!qinfo->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
+ {
+ sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT for quest %u and ReqCreatureOrGOId%d = 0, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Quest flags or ReqCreatureOrGOId%d must be fixed, quest modified to enable objective.",spellInfo->Id,qinfo->QuestId,j+1,j+1);
+
+ // this will prevent quest completing without objective
+ const_cast<Quest*>(qinfo)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
+ }
+ }
+ else
+ {
+ sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u and ReqCreatureOrGOId%d = 0 but spell %u does not have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT effect for this quest, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1,id);
+ // no changes, quest can't be done for this requirement
+ }
+ }
+ }
+ }
+
+ for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
+ {
+ int32 id = qinfo->ReqCreatureOrGOId[j];
+ if(id < 0 && !sGOStorage.LookupEntry<GameObjectInfo>(-id))
+ {
+ sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but gameobject %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,uint32(-id));
+ qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
+ }
+
+ if(id > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(id))
+ {
+ sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but creature with entry %u does not exist, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,uint32(id));
+ qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
+ }
+
+ if(id)
+ {
+ // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
+
+ qinfo->SetFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO);
+
+ if(!qinfo->ReqCreatureOrGOCount[j])
+ {
+ sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %u but `ReqCreatureOrGOCount%d` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ // no changes, quest can be incorrectly done, but we already report this
+ }
+ }
+ else if(qinfo->ReqCreatureOrGOCount[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = 0 but `ReqCreatureOrGOCount%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->ReqCreatureOrGOCount[j]);
+ // no changes, quest ignore this data
+ }
+ }
+
+ for(int j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j )
+ {
+ uint32 id = qinfo->RewChoiceItemId[j];
+ if(id)
+ {
+ if(!sItemStorage.LookupEntry<ItemPrototype>(id))
+ {
+ sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
+ qinfo->GetQuestId(),j+1,id,id);
+ qinfo->RewChoiceItemId[j] = 0; // no changes, quest will not reward this
+ }
+
+ if(!qinfo->RewChoiceItemCount[j])
+ {
+ sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but `RewChoiceItemCount%d` = 0, quest can't be done.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ // no changes, quest can't be done
+ }
+ }
+ else if(qinfo->RewChoiceItemCount[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemCount%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->RewChoiceItemCount[j]);
+ // no changes, quest ignore this data
+ }
+ }
+
+ for(int j = 0; j < QUEST_REWARDS_COUNT; ++j )
+ {
+ uint32 id = qinfo->RewItemId[j];
+ if(id)
+ {
+ if(!sItemStorage.LookupEntry<ItemPrototype>(id))
+ {
+ sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
+ qinfo->GetQuestId(),j+1,id,id);
+ qinfo->RewItemId[j] = 0; // no changes, quest will not reward this item
+ }
+
+ if(!qinfo->RewItemCount[j])
+ {
+ sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but `RewItemCount%d` = 0, quest will not reward this item.",
+ qinfo->GetQuestId(),j+1,id,j+1);
+ // no changes
+ }
+ }
+ else if(qinfo->RewItemCount[j]>0)
+ {
+ sLog.outErrorDb("Quest %u has `RewItemId%d` = 0 but `RewItemCount%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->RewItemCount[j]);
+ // no changes, quest ignore this data
+ }
+ }
+
+ for(int j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
+ {
+ if(qinfo->RewRepFaction[j])
+ {
+ if(!qinfo->RewRepValue[j])
+ {
+ sLog.outErrorDb("Quest %u has `RewRepFaction%d` = %u but `RewRepValue%d` = 0, quest will not reward this reputation.",
+ qinfo->GetQuestId(),j+1,qinfo->RewRepValue[j],j+1);
+ // no changes
+ }
+
+ if(!sFactionStore.LookupEntry(qinfo->RewRepFaction[j]))
+ {
+ sLog.outErrorDb("Quest %u has `RewRepFaction%d` = %u but raw faction (faction.dbc) %u does not exist, quest will not reward reputation for this faction.",
+ qinfo->GetQuestId(),j+1,qinfo->RewRepFaction[j] ,qinfo->RewRepFaction[j] );
+ qinfo->RewRepFaction[j] = 0; // quest will not reward this
+ }
+ }
+ else if(qinfo->RewRepValue[j]!=0)
+ {
+ sLog.outErrorDb("Quest %u has `RewRepFaction%d` = 0 but `RewRepValue%d` = %u.",
+ qinfo->GetQuestId(),j+1,j+1,qinfo->RewRepValue[j]);
+ // no changes, quest ignore this data
+ }
+ }
+
+ if(qinfo->RewSpell)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpell);
+
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u does not exist, spell removed as display reward.",
+ qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
+ qinfo->RewSpell = 0; // no spell reward will display for this quest
+ }
+
+ else if(!SpellMgr::IsSpellValid(spellInfo))
+ {
+ sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
+ qinfo->RewSpell = 0; // no spell reward will display for this quest
+ }
+
+ }
+
+ if(qinfo->RewSpellCast)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpellCast);
+
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.",
+ qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
+ qinfo->RewSpellCast = 0; // no spell will be casted on player
+ }
+
+ else if(!SpellMgr::IsSpellValid(spellInfo))
+ {
+ sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest can't be done.",
+ qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
+ qinfo->RewSpellCast = 0; // no spell will be casted on player
+ }
+
+ }
+
+ if(qinfo->RewMailTemplateId)
+ {
+ if(!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
+ {
+ sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
+ qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId);
+ qinfo->RewMailTemplateId = 0; // no mail will send to player
+ qinfo->RewMailDelaySecs = 0; // no mail will send to player
+ }
+ }
+
+ if(qinfo->NextQuestInChain)
+ {
+ if(mQuestTemplates.find(qinfo->NextQuestInChain) == mQuestTemplates.end())
+ {
+ sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.",
+ qinfo->GetQuestId(),qinfo->NextQuestInChain ,qinfo->NextQuestInChain );
+ qinfo->NextQuestInChain = 0;
+ }
+ else
+ mQuestTemplates[qinfo->NextQuestInChain]->prevChainQuests.push_back(qinfo->GetQuestId());
+ }
+
+ // fill additional data stores
+ if(qinfo->PrevQuestId)
+ {
+ if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
+ {
+ sLog.outErrorDb("Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
+ }
+ else
+ {
+ qinfo->prevQuests.push_back(qinfo->PrevQuestId);
+ }
+ }
+
+ if(qinfo->NextQuestId)
+ {
+ if (mQuestTemplates.find(abs(qinfo->GetNextQuestId())) == mQuestTemplates.end())
+ {
+ sLog.outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
+ }
+ else
+ {
+ int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
+ mQuestTemplates[abs(qinfo->GetNextQuestId())]->prevQuests.push_back(signedQuestId);
+ }
+ }
+
+ if(qinfo->ExclusiveGroup)
+ mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
+ if(qinfo->LimitTime)
+ qinfo->SetFlag(QUEST_MANGOS_FLAGS_TIMED);
+ }
+
+ // check QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
+ for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
+ {
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(i);
+ if(!spellInfo)
+ continue;
+
+ for(int j = 0; j < 3; ++j)
+ {
+ if(spellInfo->Effect[j] != SPELL_EFFECT_QUEST_COMPLETE)
+ continue;
+
+ uint32 quest_id = spellInfo->EffectMiscValue[j];
+
+ Quest const* quest = GetQuestTemplate(quest_id);
+
+ // some quest referenced in spells not exist (outdataed spells)
+ if(!quest)
+ continue;
+
+ if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
+ {
+ sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u , but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.",spellInfo->Id,quest_id);
+
+ // this will prevent quest completing without objective
+ const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
+ }
+ }
+ }
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u quests definitions", mQuestTemplates.size() );
+}
+
+void ObjectMgr::LoadQuestLocales()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry,"
+ "Title_loc1,Details_loc1,Objectives_loc1,OfferRewardText_loc1,RequestItemsText_loc1,EndText_loc1,ObjectiveText1_loc1,ObjectiveText2_loc1,ObjectiveText3_loc1,ObjectiveText4_loc1,"
+ "Title_loc2,Details_loc2,Objectives_loc2,OfferRewardText_loc2,RequestItemsText_loc2,EndText_loc2,ObjectiveText1_loc2,ObjectiveText2_loc2,ObjectiveText3_loc2,ObjectiveText4_loc2,"
+ "Title_loc3,Details_loc3,Objectives_loc3,OfferRewardText_loc3,RequestItemsText_loc3,EndText_loc3,ObjectiveText1_loc3,ObjectiveText2_loc3,ObjectiveText3_loc3,ObjectiveText4_loc3,"
+ "Title_loc4,Details_loc4,Objectives_loc4,OfferRewardText_loc4,RequestItemsText_loc4,EndText_loc4,ObjectiveText1_loc4,ObjectiveText2_loc4,ObjectiveText3_loc4,ObjectiveText4_loc4,"
+ "Title_loc5,Details_loc5,Objectives_loc5,OfferRewardText_loc5,RequestItemsText_loc5,EndText_loc5,ObjectiveText1_loc5,ObjectiveText2_loc5,ObjectiveText3_loc5,ObjectiveText4_loc5,"
+ "Title_loc6,Details_loc6,Objectives_loc6,OfferRewardText_loc6,RequestItemsText_loc6,EndText_loc6,ObjectiveText1_loc6,ObjectiveText2_loc6,ObjectiveText3_loc6,ObjectiveText4_loc6,"
+ "Title_loc7,Details_loc7,Objectives_loc7,OfferRewardText_loc7,RequestItemsText_loc7,EndText_loc7,ObjectiveText1_loc7,ObjectiveText2_loc7,ObjectiveText3_loc7,ObjectiveText4_loc7,"
+ "Title_loc8,Details_loc8,Objectives_loc8,OfferRewardText_loc8,RequestItemsText_loc8,EndText_loc8,ObjectiveText1_loc8,ObjectiveText2_loc8,ObjectiveText3_loc8,ObjectiveText4_loc8"
+ " FROM locales_quest"
+ );
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ QuestLocale& data = mQuestLocaleMap[entry];
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[1+10*(i-1)].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Title.size() <= idx)
+ data.Title.resize(idx+1);
+
+ data.Title[idx] = str;
+ }
+ }
+ str = fields[1+10*(i-1)+1].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Details.size() <= idx)
+ data.Details.resize(idx+1);
+
+ data.Details[idx] = str;
+ }
+ }
+ str = fields[1+10*(i-1)+2].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Objectives.size() <= idx)
+ data.Objectives.resize(idx+1);
+
+ data.Objectives[idx] = str;
+ }
+ }
+ str = fields[1+10*(i-1)+3].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.OfferRewardText.size() <= idx)
+ data.OfferRewardText.resize(idx+1);
+
+ data.OfferRewardText[idx] = str;
+ }
+ }
+ str = fields[1+10*(i-1)+4].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.RequestItemsText.size() <= idx)
+ data.RequestItemsText.resize(idx+1);
+
+ data.RequestItemsText[idx] = str;
+ }
+ }
+ str = fields[1+10*(i-1)+5].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.EndText.size() <= idx)
+ data.EndText.resize(idx+1);
+
+ data.EndText[idx] = str;
+ }
+ }
+ for(int k = 0; k < 4; ++k)
+ {
+ str = fields[1+10*(i-1)+6+k].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.ObjectiveText[k].size() <= idx)
+ data.ObjectiveText[k].resize(idx+1);
+
+ data.ObjectiveText[k][idx] = str;
+ }
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u Quest locale strings", mQuestLocaleMap.size() );
+}
+
+void ObjectMgr::LoadPetCreateSpells()
+{
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry, Spell1, Spell2, Spell3, Spell4 FROM petcreateinfo_spell");
+ if(!result)
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded 0 pet create spells" );
+ sLog.outErrorDb("`petcreateinfo_spell` table is empty!");
+ return;
+ }
+
+ uint32 count = 0;
+
+ barGoLink bar( result->GetRowCount() );
+
+ mPetCreateSpell.clear();
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 creature_id = fields[0].GetUInt32();
+
+ if(!creature_id || !sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
+ continue;
+
+ PetCreateSpellEntry PetCreateSpell;
+ for(int i = 0; i < 4; i++)
+ {
+ PetCreateSpell.spellid[i] = fields[i + 1].GetUInt32();
+
+ if(PetCreateSpell.spellid[i] && !sSpellStore.LookupEntry(PetCreateSpell.spellid[i]))
+ sLog.outErrorDb("Spell %u listed in `petcreateinfo_spell` does not exist",PetCreateSpell.spellid[i]);
+ }
+
+ mPetCreateSpell[creature_id] = PetCreateSpell;
+
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u pet create spells", count );
+}
+
+void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
+{
+ if(sWorld.IsScriptScheduled()) // function don't must be called in time scripts use.
+ return;
+
+ sLog.outString( "%s :", tablename);
+
+ scripts.clear(); // need for reload support
+
+ QueryResult *result = WorldDatabase.PQuery( "SELECT id,delay,command,datalong,datalong2,datatext, x, y, z, o FROM %s", tablename );
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u script definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+ ScriptInfo tmp;
+ tmp.id = fields[0].GetUInt32();
+ tmp.delay = fields[1].GetUInt32();
+ tmp.command = fields[2].GetUInt32();
+ tmp.datalong = fields[3].GetUInt32();
+ tmp.datalong2 = fields[4].GetUInt32();
+ tmp.datatext = fields[5].GetCppString();
+ tmp.x = fields[6].GetFloat();
+ tmp.y = fields[7].GetFloat();
+ tmp.z = fields[8].GetFloat();
+ tmp.o = fields[9].GetFloat();
+
+ // generic command args check
+ switch(tmp.command)
+ {
+ case SCRIPT_COMMAND_TALK:
+ {
+ if(tmp.datalong > 3)
+ {
+ sLog.outErrorDb("Table `%s` has invalid talk type (datalong = %u) in SCRIPT_COMMAND_TALK for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+ break;
+ }
+
+ case SCRIPT_COMMAND_TELEPORT_TO:
+ {
+ if(!sMapStore.LookupEntry(tmp.datalong))
+ {
+ sLog.outErrorDb("Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+
+ if(!MaNGOS::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
+ {
+ sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",tablename,tmp.x,tmp.y,tmp.id);
+ continue;
+ }
+ break;
+ }
+
+ case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
+ {
+ if(!MaNGOS::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
+ {
+ sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",tablename,tmp.x,tmp.y,tmp.id);
+ continue;
+ }
+
+ if(!GetCreatureTemplate(tmp.datalong))
+ {
+ sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+ break;
+ }
+
+ case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
+ {
+ GameObjectData const* data = GetGOData(tmp.datalong);
+ if(!data)
+ {
+ sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+
+ GameObjectInfo const* info = GetGameObjectInfo(data->id);
+ if(!info)
+ {
+ sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,tmp.datalong,data->id,tmp.id);
+ continue;
+ }
+
+ if( info->type==GAMEOBJECT_TYPE_FISHINGNODE ||
+ info->type==GAMEOBJECT_TYPE_FISHINGHOLE ||
+ info->type==GAMEOBJECT_TYPE_DOOR ||
+ info->type==GAMEOBJECT_TYPE_BUTTON ||
+ info->type==GAMEOBJECT_TYPE_TRAP )
+ {
+ sLog.outErrorDb("Table `%s` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,info->id,tmp.id);
+ continue;
+ }
+ break;
+ }
+ case SCRIPT_COMMAND_OPEN_DOOR:
+ case SCRIPT_COMMAND_CLOSE_DOOR:
+ {
+ GameObjectData const* data = GetGOData(tmp.datalong);
+ if(!data)
+ {
+ sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u",tablename,tmp.datalong,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
+ continue;
+ }
+
+ GameObjectInfo const* info = GetGameObjectInfo(data->id);
+ if(!info)
+ {
+ sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u",tablename,tmp.datalong,data->id,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
+ continue;
+ }
+
+ if( info->type!=GAMEOBJECT_TYPE_DOOR)
+ {
+ sLog.outErrorDb("Table `%s` has gameobject type (%u) non supported by command %s for script id %u",tablename,info->id,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
+ continue;
+ }
+
+ break;
+ }
+ case SCRIPT_COMMAND_QUEST_EXPLORED:
+ {
+ Quest const* quest = GetQuestTemplate(tmp.datalong);
+ if(!quest)
+ {
+ sLog.outErrorDb("Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+
+ if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
+ {
+ sLog.outErrorDb("Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",tablename,tmp.datalong,tmp.id);
+
+ // this will prevent quest completing without objective
+ const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
+
+ // continue; - quest objective requiremet set and command can be allowed
+ }
+
+ if(float(tmp.datalong2) > DEFAULT_VISIBILITY_DISTANCE)
+ {
+ sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",tablename,tmp.datalong2,tmp.id);
+ continue;
+ }
+
+ if(tmp.datalong2 && float(tmp.datalong2) > DEFAULT_VISIBILITY_DISTANCE)
+ {
+ sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, max distance is %u or 0 for disable distance check",tablename,tmp.datalong2,tmp.id,uint32(DEFAULT_VISIBILITY_DISTANCE));
+ continue;
+ }
+
+ if(tmp.datalong2 && float(tmp.datalong2) < INTERACTION_DISTANCE)
+ {
+ sLog.outErrorDb("Table `%s` has too small distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, min distance is %u or 0 for disable distance check",tablename,tmp.datalong2,tmp.id,uint32(INTERACTION_DISTANCE));
+ continue;
+ }
+
+ break;
+ }
+
+ case SCRIPT_COMMAND_REMOVE_AURA:
+ case SCRIPT_COMMAND_CAST_SPELL:
+ {
+ if(!sSpellStore.LookupEntry(tmp.datalong))
+ {
+ sLog.outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u",tablename,tmp.datalong,tmp.id);
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (scripts.find(tmp.id) == scripts.end())
+ {
+ ScriptMap emptyMap;
+ scripts[tmp.id] = emptyMap;
+ }
+ scripts[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
+
+ ++count;
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u script definitions", count );
+}
+
+void ObjectMgr::LoadGameObjectScripts()
+{
+ LoadScripts(sGameObjectScripts, "gameobject_scripts");
+
+ // check ids
+ for(ScriptMapMap::const_iterator itr = sGameObjectScripts.begin(); itr != sGameObjectScripts.end(); ++itr)
+ {
+ if(!GetGOData(itr->first))
+ sLog.outErrorDb("Table `gameobject_scripts` has not existing gameobject (GUID: %u) as script id",itr->first);
+ }
+}
+
+void ObjectMgr::LoadQuestEndScripts()
+{
+ LoadScripts(sQuestEndScripts, "quest_end_scripts");
+
+ // check ids
+ for(ScriptMapMap::const_iterator itr = sQuestEndScripts.begin(); itr != sQuestEndScripts.end(); ++itr)
+ {
+ if(!GetQuestTemplate(itr->first))
+ sLog.outErrorDb("Table `quest_end_scripts` has not existing quest (Id: %u) as script id",itr->first);
+ }
+}
+
+void ObjectMgr::LoadQuestStartScripts()
+{
+ LoadScripts(sQuestStartScripts,"quest_start_scripts");
+
+ // check ids
+ for(ScriptMapMap::const_iterator itr = sQuestStartScripts.begin(); itr != sQuestStartScripts.end(); ++itr)
+ {
+ if(!GetQuestTemplate(itr->first))
+ sLog.outErrorDb("Table `quest_start_scripts` has not existing quest (Id: %u) as script id",itr->first);
+ }
+}
+
+void ObjectMgr::LoadSpellScripts()
+{
+ LoadScripts(sSpellScripts, "spell_scripts");
+
+ // check ids
+ for(ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first);
+
+ if(!spellInfo)
+ {
+ sLog.outErrorDb("Table `spell_scripts` has not existing spell (Id: %u) as script id",itr->first);
+ continue;
+ }
+
+ //check for correct spellEffect
+ bool found = false;
+ for(int i=0; i<3; ++i)
+ {
+ // skip empty effects
+ if( !spellInfo->Effect[i] )
+ continue;
+
+ if( spellInfo->Effect[i] == SPELL_EFFECT_SCRIPT_EFFECT )
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ sLog.outErrorDb("Table `spell_scripts` has unsupported spell (Id: %u) without SPELL_EFFECT_SCRIPT_EFFECT (%u) spell effect",itr->first,SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+}
+
+void ObjectMgr::LoadEventScripts()
+{
+ LoadScripts(sEventScripts, "event_scripts");
+
+ std::set<uint32> evt_scripts;
+ // Load all possible script entries from gameobjects
+ for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i)
+ {
+ GameObjectInfo const * goInfo = sGOStorage.LookupEntry<GameObjectInfo>(i);
+ if (goInfo)
+ {
+ switch(goInfo->type)
+ {
+ case GAMEOBJECT_TYPE_GOOBER:
+ if(goInfo->goober.eventId)
+ evt_scripts.insert(goInfo->goober.eventId);
+ break;
+ case GAMEOBJECT_TYPE_CHEST:
+ if(goInfo->chest.eventId)
+ evt_scripts.insert(goInfo->chest.eventId);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ // Load all possible script entries from spells
+ for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
+ {
+ SpellEntry const * spell = sSpellStore.LookupEntry(i);
+ if (spell)
+ {
+ for(int j=0; j<3; ++j)
+ {
+ if( spell->Effect[j] == SPELL_EFFECT_SEND_EVENT )
+ {
+ if (spell->EffectMiscValue[j])
+ evt_scripts.insert(spell->EffectMiscValue[j]);
+ }
+ }
+ }
+ }
+ // Then check if all scripts are in above list of possible script entries
+ for(ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
+ {
+ std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
+ if (itr2 == evt_scripts.end())
+ sLog.outErrorDb("Table `event_scripts` has script (Id: %u) not refering to any gameobject_template type 10 data2 field or type 3 data6 field or any spell effect %u", itr->first, SPELL_EFFECT_SEND_EVENT);
+ }
+}
+
+void ObjectMgr::LoadItemTexts()
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT id, text FROM item_text");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u item pages", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ Field* fields;
+ do
+ {
+ bar.step();
+
+ fields = result->Fetch();
+
+ mItemTexts[ fields[0].GetUInt32() ] = fields[1].GetCppString();
+
+ ++count;
+
+ } while ( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u item texts", count );
+}
+
+void ObjectMgr::LoadPageTexts()
+{
+ sPageTextStore.Free(); // for reload case
+
+ sPageTextStore.Load();
+ sLog.outString( ">> Loaded %u page texts", sPageTextStore.RecordCount );
+ sLog.outString();
+
+ for(uint32 i = 1; i < sPageTextStore.MaxEntry; ++i)
+ {
+ // check data correctness
+ PageText const* page = sPageTextStore.LookupEntry<PageText>(i);
+ if(!page)
+ continue;
+
+ if(page->Next_Page && !sPageTextStore.LookupEntry<PageText>(page->Next_Page))
+ {
+ sLog.outErrorDb("Page text (Id: %u) has not existing next page (Id:%u)", i,page->Next_Page);
+ continue;
+ }
+
+ // detect circular reference
+ std::set<uint32> checkedPages;
+ for(PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry<PageText>(pageItr->Next_Page))
+ {
+ if(!pageItr->Next_Page)
+ break;
+ checkedPages.insert(pageItr->Page_ID);
+ if(checkedPages.find(pageItr->Next_Page)!=checkedPages.end())
+ {
+ std::ostringstream ss;
+ ss<< "The text page(s) ";
+ for (std::set<uint32>::iterator itr= checkedPages.begin();itr!=checkedPages.end(); itr++)
+ ss << *itr << " ";
+ ss << "create(s) a circular reference, which can cause the server to freeze. Changing Next_Page of page "
+ << pageItr->Page_ID <<" to 0";
+ sLog.outErrorDb(ss.str().c_str());
+ const_cast<PageText*>(pageItr)->Next_Page = 0;
+ break;
+ }
+ }
+ }
+}
+
+void ObjectMgr::LoadPageTextLocales()
+{
+ QueryResult *result = WorldDatabase.PQuery("SELECT entry,text_loc1,text_loc2,text_loc3,text_loc4,text_loc5,text_loc6,text_loc7,text_loc8 FROM locales_page_text");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ PageTextLocale& data = mPageTextLocaleMap[entry];
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[i].GetCppString();
+ if(str.empty())
+ continue;
+
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Text.size() <= idx)
+ data.Text.resize(idx+1);
+
+ data.Text[idx] = str;
+ }
+ }
+
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u PageText locale strings", mPageTextLocaleMap.size() );
+}
+
+void ObjectMgr::LoadInstanceTemplate()
+{
+ sInstanceTemplate.Load();
+
+ for(uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++)
+ {
+ InstanceTemplate* temp = (InstanceTemplate*)GetInstanceTemplate(i);
+ if(!temp) continue;
+ const MapEntry* entry = sMapStore.LookupEntry(temp->map);
+ if(!entry)
+ {
+ sLog.outErrorDb("ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", temp->map);
+ continue;
+ }
+ else if(!entry->HasResetTime())
+ continue;
+
+ if(temp->reset_delay == 0)
+ {
+ // use defaults from the DBC
+ if(entry->SupportsHeroicMode())
+ {
+ temp->reset_delay = entry->resetTimeHeroic / DAY;
+ }
+ else if (entry->resetTimeRaid && entry->map_type == MAP_RAID)
+ {
+ temp->reset_delay = entry->resetTimeRaid / DAY;
+ }
+ }
+
+ // the reset_delay must be atleast one day
+ temp->reset_delay = std::max((uint32)1, (uint32)(temp->reset_delay * sWorld.getRate(RATE_INSTANCE_RESET_TIME)));
+ }
+
+ sLog.outString( ">> Loaded %u Instance Template definitions", sInstanceTemplate.RecordCount );
+ sLog.outString();
+}
+
+void ObjectMgr::AddGossipText(GossipText *pGText)
+{
+ ASSERT( pGText->Text_ID );
+ ASSERT( mGossipText.find(pGText->Text_ID) == mGossipText.end() );
+ mGossipText[pGText->Text_ID] = pGText;
+}
+
+GossipText *ObjectMgr::GetGossipText(uint32 Text_ID)
+{
+ GossipTextMap::const_iterator itr;
+ for (itr = mGossipText.begin(); itr != mGossipText.end(); itr++)
+ {
+ if(itr->second->Text_ID == Text_ID)
+ return itr->second;
+ }
+ return NULL;
+}
+
+void ObjectMgr::LoadGossipText()
+{
+ GossipText *pGText;
+ QueryResult *result = WorldDatabase.Query( "SELECT * FROM npc_text" );
+
+ int count = 0;
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u npc texts", count );
+ return;
+ }
+
+ int cic;
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ cic = 0;
+
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ pGText = new GossipText;
+ pGText->Text_ID = fields[cic++].GetUInt32();
+
+ for (int i=0; i< 8; i++)
+ {
+ pGText->Options[i].Text_0 = fields[cic++].GetCppString();
+ pGText->Options[i].Text_1 = fields[cic++].GetCppString();
+
+ pGText->Options[i].Language = fields[cic++].GetUInt32();
+ pGText->Options[i].Probability = fields[cic++].GetFloat();
+
+ pGText->Options[i].Emotes[0]._Delay = fields[cic++].GetUInt32();
+ pGText->Options[i].Emotes[0]._Emote = fields[cic++].GetUInt32();
+
+ pGText->Options[i].Emotes[1]._Delay = fields[cic++].GetUInt32();
+ pGText->Options[i].Emotes[1]._Emote = fields[cic++].GetUInt32();
+
+ pGText->Options[i].Emotes[2]._Delay = fields[cic++].GetUInt32();
+ pGText->Options[i].Emotes[2]._Emote = fields[cic++].GetUInt32();
+ }
+
+ if ( !pGText->Text_ID ) continue;
+ AddGossipText( pGText );
+
+ } while( result->NextRow() );
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u npc texts", count );
+ delete result;
+}
+
+void ObjectMgr::LoadNpcTextLocales()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry,"
+ "Text0_0_loc1,Text0_1_loc1,Text1_0_loc1,Text1_1_loc1,Text2_0_loc1,Text2_1_loc1,Text3_0_loc1,Text3_1_loc1,Text4_0_loc1,Text4_1_loc1,Text5_0_loc1,Text5_1_loc1,Text6_0_loc1,Text6_1_loc1,Text7_0_loc1,Text7_1_loc1,"
+ "Text0_0_loc2,Text0_1_loc2,Text1_0_loc2,Text1_1_loc2,Text2_0_loc2,Text2_1_loc2,Text3_0_loc2,Text3_1_loc1,Text4_0_loc2,Text4_1_loc2,Text5_0_loc2,Text5_1_loc2,Text6_0_loc2,Text6_1_loc2,Text7_0_loc2,Text7_1_loc2,"
+ "Text0_0_loc3,Text0_1_loc3,Text1_0_loc3,Text1_1_loc3,Text2_0_loc3,Text2_1_loc3,Text3_0_loc3,Text3_1_loc1,Text4_0_loc3,Text4_1_loc3,Text5_0_loc3,Text5_1_loc3,Text6_0_loc3,Text6_1_loc3,Text7_0_loc3,Text7_1_loc3,"
+ "Text0_0_loc4,Text0_1_loc4,Text1_0_loc4,Text1_1_loc4,Text2_0_loc4,Text2_1_loc4,Text3_0_loc4,Text3_1_loc1,Text4_0_loc4,Text4_1_loc4,Text5_0_loc4,Text5_1_loc4,Text6_0_loc4,Text6_1_loc4,Text7_0_loc4,Text7_1_loc4,"
+ "Text0_0_loc5,Text0_1_loc5,Text1_0_loc5,Text1_1_loc5,Text2_0_loc5,Text2_1_loc5,Text3_0_loc5,Text3_1_loc1,Text4_0_loc5,Text4_1_loc5,Text5_0_loc5,Text5_1_loc5,Text6_0_loc5,Text6_1_loc5,Text7_0_loc5,Text7_1_loc5,"
+ "Text0_0_loc6,Text0_1_loc6,Text1_0_loc6,Text1_1_loc6,Text2_0_loc6,Text2_1_loc6,Text3_0_loc6,Text3_1_loc1,Text4_0_loc6,Text4_1_loc6,Text5_0_loc6,Text5_1_loc6,Text6_0_loc6,Text6_1_loc6,Text7_0_loc6,Text7_1_loc6,"
+ "Text0_0_loc7,Text0_1_loc7,Text1_0_loc7,Text1_1_loc7,Text2_0_loc7,Text2_1_loc7,Text3_0_loc7,Text3_1_loc1,Text4_0_loc7,Text4_1_loc7,Text5_0_loc7,Text5_1_loc7,Text6_0_loc7,Text6_1_loc7,Text7_0_loc7,Text7_1_loc7, "
+ "Text0_0_loc8,Text0_1_loc8,Text1_0_loc8,Text1_1_loc8,Text2_0_loc8,Text2_1_loc8,Text3_0_loc8,Text3_1_loc1,Text4_0_loc8,Text4_1_loc8,Text5_0_loc8,Text5_1_loc8,Text6_0_loc8,Text6_1_loc8,Text7_0_loc8,Text7_1_loc8 "
+ " FROM locales_npc_text");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_npc_text` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ NpcTextLocale& data = mNpcTextLocaleMap[entry];
+
+ for(int i=1; i<MAX_LOCALE; ++i)
+ {
+ for(int j=0; j<8; ++j)
+ {
+ std::string str0 = fields[1+8*2*(i-1)+2*j].GetCppString();
+ if(!str0.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Text_0[j].size() <= idx)
+ data.Text_0[j].resize(idx+1);
+
+ data.Text_0[j][idx] = str0;
+ }
+ }
+ std::string str1 = fields[1+8*2*(i-1)+2*j+1].GetCppString();
+ if(!str1.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Text_1[j].size() <= idx)
+ data.Text_1[j].resize(idx+1);
+
+ data.Text_1[j][idx] = str1;
+ }
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u NpcText locale strings", mNpcTextLocaleMap.size() );
+}
+
+//not very fast function but it is called only once a day, or on starting-up
+void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
+{
+ time_t basetime = time(NULL);
+ sLog.outDebug("Returning mails current time: hour: %d, minute: %d, second: %d ", localtime(&basetime)->tm_hour, localtime(&basetime)->tm_min, localtime(&basetime)->tm_sec);
+ //delete all old mails without item and without body immediately, if starting server
+ if (!serverUp)
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE expire_time < '" I64FMTD "' AND has_items = '0' AND itemTextId = 0", (uint64)basetime);
+ // 0 1 2 3 4 5 6 7 8 9
+ QueryResult* result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,itemTextId,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" I64FMTD "'", (uint64)basetime);
+ if ( !result )
+ return; // any mails need to be returned or deleted
+ Field *fields;
+ //std::ostringstream delitems, delmails; //will be here for optimization
+ //bool deletemail = false, deleteitem = false;
+ //delitems << "DELETE FROM item_instance WHERE guid IN ( ";
+ //delmails << "DELETE FROM mail WHERE id IN ( "
+ do
+ {
+ fields = result->Fetch();
+ Mail *m = new Mail;
+ m->messageID = fields[0].GetUInt32();
+ m->messageType = fields[1].GetUInt8();
+ m->sender = fields[2].GetUInt32();
+ m->receiver = fields[3].GetUInt32();
+ m->itemTextId = fields[4].GetUInt32();
+ bool has_items = fields[5].GetBool();
+ m->expire_time = (time_t)fields[6].GetUInt64();
+ m->deliver_time = 0;
+ m->COD = fields[7].GetUInt32();
+ m->checked = fields[8].GetUInt32();
+ m->mailTemplateId = fields[9].GetInt16();
+
+ Player *pl = 0;
+ if (serverUp)
+ pl = GetPlayer((uint64)m->receiver);
+ if (pl && pl->m_mailsLoaded)
+ { //this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
+ //his in mailbox and he has already listed his mails )
+ delete m;
+ continue;
+ }
+ //delete or return mail:
+ if (has_items)
+ {
+ QueryResult *resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", m->messageID);
+ if(resultItems)
+ {
+ do
+ {
+ Field *fields2 = resultItems->Fetch();
+
+ uint32 item_guid_low = fields2[0].GetUInt32();
+ uint32 item_template = fields2[1].GetUInt32();
+
+ m->AddItem(item_guid_low, item_template);
+ }
+ while (resultItems->NextRow());
+
+ delete resultItems;
+ }
+ //if it is mail from AH, it shouldn't be returned, but deleted
+ if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_AUCTION | MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
+ {
+ // mail open and then not returned
+ for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
+ }
+ else
+ {
+ //mail will be returned:
+ CharacterDatabase.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" I64FMTD "', deliver_time = '" I64FMTD "',cod = '0', checked = '%u' WHERE id = '%u'", m->receiver, m->sender, (uint64)(basetime + 30*DAY), (uint64)basetime, MAIL_CHECK_MASK_RETURNED, m->messageID);
+ delete m;
+ continue;
+ }
+ }
+
+ if (m->itemTextId)
+ CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", m->itemTextId);
+
+ //deletemail = true;
+ //delmails << m->messageID << ", ";
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
+ delete m;
+ } while (result->NextRow());
+ delete result;
+}
+
+void ObjectMgr::LoadQuestAreaTriggers()
+{
+ mQuestAreaTriggerMap.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.Query( "SELECT id,quest FROM areatrigger_involvedrelation" );
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u quest trigger points", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 trigger_ID = fields[0].GetUInt32();
+ uint32 quest_ID = fields[1].GetUInt32();
+
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
+ if(!atEntry)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",trigger_ID);
+ continue;
+ }
+
+ Quest const* quest = GetQuestTemplate(quest_ID);
+
+ if(!quest)
+ {
+ sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u",trigger_ID,quest_ID);
+ continue;
+ }
+
+ if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
+ {
+ sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.",trigger_ID,quest_ID);
+
+ // this will prevent quest completing without objective
+ const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
+
+ // continue; - quest modified to required obkective and trigger can be allowed.
+ }
+
+ mQuestAreaTriggerMap[trigger_ID] = quest_ID;
+
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u quest trigger points", count );
+}
+
+void ObjectMgr::LoadTavernAreaTriggers()
+{
+ mTavernAreaTriggerSet.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u tavern triggers", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 Trigger_ID = fields[0].GetUInt32();
+
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
+ if(!atEntry)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
+ continue;
+ }
+
+ mTavernAreaTriggerSet.insert(Trigger_ID);
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u tavern triggers", count );
+}
+
+void ObjectMgr::LoadAreaTriggerScripts()
+{
+ mAreaTriggerScripts.clear(); // need for reload case
+ QueryResult *result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u areatrigger scripts", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 Trigger_ID = fields[0].GetUInt32();
+ std::string scriptName = fields[1].GetCppString();
+
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
+ if(!atEntry)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
+ continue;
+ }
+ mAreaTriggerScripts[Trigger_ID] = scriptName;
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u areatrigger scripts", count );
+}
+uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid )
+{
+ bool found = false;
+ float dist;
+ uint32 id = 0;
+
+ for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
+ {
+ TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
+ if(node && node->map_id == mapid)
+ {
+ float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z);
+ if(found)
+ {
+ if(dist2 < dist)
+ {
+ dist = dist2;
+ id = i;
+ }
+ }
+ else
+ {
+ found = true;
+ dist = dist2;
+ id = i;
+ }
+ }
+ }
+
+ return id;
+}
+
+void ObjectMgr::GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost)
+{
+ TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
+ if(src_i==sTaxiPathSetBySource.end())
+ {
+ path = 0;
+ cost = 0;
+ return;
+ }
+
+ TaxiPathSetForSource& pathSet = src_i->second;
+
+ TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
+ if(dest_i==pathSet.end())
+ {
+ path = 0;
+ cost = 0;
+ return;
+ }
+
+ cost = dest_i->second.price;
+ path = dest_i->second.ID;
+}
+
+uint16 ObjectMgr::GetTaxiMount( uint32 id, uint32 team )
+{
+ uint16 mount_entry = 0;
+ uint16 mount_id = 0;
+
+ TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
+ if(node)
+ {
+ if (team == ALLIANCE)
+ {
+ mount_entry = node->alliance_mount_type;
+ CreatureInfo const *ci = GetCreatureTemplate(mount_entry);
+ if(ci)
+ mount_id = ci->DisplayID_A;
+ }
+ if (team == HORDE)
+ {
+ mount_entry = node->horde_mount_type;
+ CreatureInfo const *ci = GetCreatureTemplate(mount_entry);
+ if(ci)
+ mount_id = ci->DisplayID_H;
+ }
+ }
+
+ CreatureModelInfo const *minfo = GetCreatureModelInfo(mount_id);
+ if(!minfo)
+ {
+ sLog.outErrorDb("Taxi mount (Entry: %u) for taxi node (Id: %u) for team %u has model %u not found in table `creature_model_info`, can't load. ",
+ mount_entry,id,team,mount_id);
+
+ return false;
+ }
+ if(minfo->modelid_other_gender!=0)
+ mount_id = urand(0,1) ? mount_id : minfo->modelid_other_gender;
+
+ return mount_id;
+}
+
+void ObjectMgr::GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds)
+{
+ if(path >= sTaxiPathNodesByPath.size())
+ return;
+
+ TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
+
+ pathnodes.Resize(nodeList.size());
+ mapIds.resize(nodeList.size());
+
+ for(size_t i = 0; i < nodeList.size(); ++i)
+ {
+ pathnodes[ i ].x = nodeList[i].x;
+ pathnodes[ i ].y = nodeList[i].y;
+ pathnodes[ i ].z = nodeList[i].z;
+
+ mapIds[i] = nodeList[i].mapid;
+ }
+}
+
+void ObjectMgr::GetTransportPathNodes( uint32 path, TransportPath &pathnodes )
+{
+ if(path >= sTaxiPathNodesByPath.size())
+ return;
+
+ TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
+
+ pathnodes.Resize(nodeList.size());
+
+ for(size_t i = 0; i < nodeList.size(); ++i)
+ {
+ pathnodes[ i ].mapid = nodeList[i].mapid;
+ pathnodes[ i ].x = nodeList[i].x;
+ pathnodes[ i ].y = nodeList[i].y;
+ pathnodes[ i ].z = nodeList[i].z;
+ pathnodes[ i ].actionFlag = nodeList[i].actionFlag;
+ pathnodes[ i ].delay = nodeList[i].delay;
+ }
+}
+
+void ObjectMgr::LoadGraveyardZones()
+{
+ mGraveYardMap.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.Query("SELECT id,ghost_zone,faction FROM game_graveyard_zone");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u graveyard-zone links", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 safeLocId = fields[0].GetUInt32();
+ uint32 zoneId = fields[1].GetUInt32();
+ uint32 team = fields[2].GetUInt32();
+
+ WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
+ if(!entry)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.",safeLocId);
+ continue;
+ }
+
+ AreaTableEntry const *areaEntry = GetAreaEntryByAreaID(zoneId);
+ if(!areaEntry)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing zone id (%u), skipped.",zoneId);
+ continue;
+ }
+
+ if(areaEntry->zone != 0)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record subzone id (%u) instead of zone, skipped.",zoneId);
+ continue;
+ }
+
+ if(team!=0 && team!=HORDE && team!=ALLIANCE)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record for non player faction (%u), skipped.",team);
+ continue;
+ }
+
+ if(entry->map_id != areaEntry->mapid && team != 0)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record for ghost zone (%u) at map %u and graveyard (%u) at map %u for team %u, but in case maps are different, player faction setting is ignored. Use faction 0 instead.",zoneId,areaEntry->mapid, safeLocId, entry->map_id, team);
+ team = 0;
+ }
+
+ if(!AddGraveYardLink(safeLocId,zoneId,team,false))
+ sLog.outErrorDb("Table `game_graveyard_zone` has a duplicate record for Garveyard (ID: %u) and Zone (ID: %u), skipped.",safeLocId,zoneId);
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u graveyard-zone links", count );
+}
+
+WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team)
+{
+ // search for zone associated closest graveyard
+ uint32 zoneId = MapManager::Instance().GetZoneId(MapId,x,y);
+
+ // Simulate std. algorithm:
+ // found some graveyard associated to (ghost_zone,ghost_map)
+ //
+ // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
+ // then check faction
+ // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
+ // then skip check faction
+ GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
+ GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
+ if(graveLow==graveUp)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team);
+ return NULL;
+ }
+
+ bool foundNear = false;
+ float distNear;
+ WorldSafeLocsEntry const* entryNear = NULL;
+ WorldSafeLocsEntry const* entryFar = NULL;
+
+ for(GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
+ {
+ GraveYardData const& data = itr->second;
+
+ WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
+ if(!entry)
+ {
+ sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.",data.safeLocId);
+ continue;
+ }
+
+ // remember first graveyard at another map and ignore other
+ if(MapId != entry->map_id)
+ {
+ if(!entryFar)
+ entryFar = entry;
+ continue;
+ }
+
+ // skip enemy faction graveyard at same map (normal area, city, or battleground)
+ // team == 0 case can be at call from .neargrave
+ if(data.team != 0 && team != 0 && data.team != team)
+ continue;
+
+ // find now nearest graveyard at same map
+ float dist2 = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y)+(entry->z - z)*(entry->z - z);
+ if(foundNear)
+ {
+ if(dist2 < distNear)
+ {
+ distNear = dist2;
+ entryNear = entry;
+ }
+ }
+ else
+ {
+ foundNear = true;
+ distNear = dist2;
+ entryNear = entry;
+ }
+ }
+
+ if(entryNear)
+ return entryNear;
+
+ return entryFar;
+}
+
+GraveYardData const* ObjectMgr::FindGraveYardData(uint32 id, uint32 zoneId)
+{
+ GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
+ GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
+
+ for(GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
+ {
+ if(itr->second.safeLocId==id)
+ return &itr->second;
+ }
+
+ return NULL;
+}
+
+bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool inDB)
+{
+ if(FindGraveYardData(id,zoneId))
+ return false;
+
+ // add link to loaded data
+ GraveYardData data;
+ data.safeLocId = id;
+ data.team = team;
+
+ mGraveYardMap.insert(GraveYardMap::value_type(zoneId,data));
+
+ // add link to DB
+ if(inDB)
+ {
+ WorldDatabase.PExecuteLog("INSERT INTO game_graveyard_zone ( id,ghost_zone,faction) "
+ "VALUES ('%u', '%u','%u')",id,zoneId,team);
+ }
+
+ return true;
+}
+
+void ObjectMgr::LoadAreaTriggerTeleports()
+{
+ mAreaTriggers.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12
+ QueryResult *result = WorldDatabase.Query("SELECT id, required_level, required_item, required_item2, heroic_key, heroic_key2, required_quest_done, required_failed_text, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
+ if( !result )
+ {
+
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u area trigger teleport definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ ++count;
+
+ uint32 Trigger_ID = fields[0].GetUInt32();
+
+ AreaTrigger at;
+
+ at.requiredLevel = fields[1].GetUInt8();
+ at.requiredItem = fields[2].GetUInt32();
+ at.requiredItem2 = fields[3].GetUInt32();
+ at.heroicKey = fields[4].GetUInt32();
+ at.heroicKey2 = fields[5].GetUInt32();
+ at.requiredQuest = fields[6].GetUInt32();
+ at.requiredFailedText = fields[7].GetCppString();
+ at.target_mapId = fields[8].GetUInt32();
+ at.target_X = fields[9].GetFloat();
+ at.target_Y = fields[10].GetFloat();
+ at.target_Z = fields[11].GetFloat();
+ at.target_Orientation = fields[12].GetFloat();
+
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
+ if(!atEntry)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
+ continue;
+ }
+
+ if(at.requiredItem)
+ {
+ ItemPrototype const *pProto = GetItemPrototype(at.requiredItem);
+ if(!pProto)
+ {
+ sLog.outError("Key item %u does not exist for trigger %u, removing key requirement.", at.requiredItem, Trigger_ID);
+ at.requiredItem = 0;
+ }
+ }
+ if(at.requiredItem2)
+ {
+ ItemPrototype const *pProto = GetItemPrototype(at.requiredItem2);
+ if(!pProto)
+ {
+ sLog.outError("Second item %u not exist for trigger %u, remove key requirement.", at.requiredItem2, Trigger_ID);
+ at.requiredItem2 = 0;
+ }
+ }
+
+ if(at.heroicKey)
+ {
+ ItemPrototype const *pProto = GetItemPrototype(at.heroicKey);
+ if(!pProto)
+ {
+ sLog.outError("Heroic key item %u not exist for trigger %u, remove key requirement.", at.heroicKey, Trigger_ID);
+ at.heroicKey = 0;
+ }
+ }
+
+ if(at.heroicKey2)
+ {
+ ItemPrototype const *pProto = GetItemPrototype(at.heroicKey2);
+ if(!pProto)
+ {
+ sLog.outError("Heroic second key item %u not exist for trigger %u, remove key requirement.", at.heroicKey2, Trigger_ID);
+ at.heroicKey2 = 0;
+ }
+ }
+
+ if(at.requiredQuest)
+ {
+ if(!mQuestTemplates[at.requiredQuest])
+ {
+ sLog.outErrorDb("Required Quest %u not exist for trigger %u, remove quest done requirement.",at.requiredQuest,Trigger_ID);
+ at.requiredQuest = 0;
+ }
+ }
+
+ MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
+ if(!mapEntry)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.",Trigger_ID,at.target_mapId);
+ continue;
+ }
+
+ if(at.target_X==0 && at.target_Y==0 && at.target_Z==0)
+ {
+ sLog.outErrorDb("Area trigger (ID:%u) target coordinates not provided.",Trigger_ID);
+ continue;
+ }
+
+ mAreaTriggers[Trigger_ID] = at;
+
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u area trigger teleport definitions", count );
+}
+
+AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const
+{
+ const MapEntry *mapEntry = sMapStore.LookupEntry(Map);
+ if(!mapEntry) return NULL;
+ for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); itr++)
+ {
+ if(itr->second.target_mapId == mapEntry->parent_map)
+ {
+ AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
+ if(atEntry && atEntry->mapid == Map)
+ return &itr->second;
+ }
+ }
+ return NULL;
+}
+
+void ObjectMgr::SetHighestGuids()
+{
+ QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guid) FROM characters" );
+ if( result )
+ {
+ m_hiCharGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+
+ result = WorldDatabase.Query( "SELECT MAX(guid) FROM creature" );
+ if( result )
+ {
+ m_hiCreatureGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+
+ result = CharacterDatabase.Query( "SELECT MAX(id) FROM character_pet" );
+ if( result )
+ {
+ m_hiPetGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+
+ result = CharacterDatabase.Query( "SELECT MAX(guid) FROM item_instance" );
+ if( result )
+ {
+ m_hiItemGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+
+ // Cleanup other tables from not existed guids (>=m_hiItemGuid)
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", m_hiItemGuid);
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", m_hiItemGuid);
+ CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE itemguid >= '%u'", m_hiItemGuid);
+ CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", m_hiItemGuid);
+
+ result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject" );
+ if( result )
+ {
+ m_hiGoGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+
+ result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse" );
+ if( result )
+ {
+ m_auctionid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+ else
+ {
+ m_auctionid = 0;
+ }
+ result = CharacterDatabase.Query( "SELECT MAX(id) FROM mail" );
+ if( result )
+ {
+ m_mailid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+ else
+ {
+ m_mailid = 0;
+ }
+ result = CharacterDatabase.Query( "SELECT MAX(id) FROM item_text" );
+ if( result )
+ {
+ m_ItemTextId = (*result)[0].GetUInt32();
+
+ delete result;
+ }
+ else
+ m_ItemTextId = 0;
+
+ result = CharacterDatabase.Query( "SELECT MAX(guid) FROM corpse" );
+ if( result )
+ {
+ m_hiCorpseGuid = (*result)[0].GetUInt32()+1;
+
+ delete result;
+ }
+}
+
+uint32 ObjectMgr::GenerateAuctionID()
+{
+ ++m_auctionid;
+ if(m_auctionid>=0xFFFFFFFF)
+ {
+ sLog.outError("Auctions ids overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_auctionid;
+}
+
+uint32 ObjectMgr::GenerateMailID()
+{
+ ++m_mailid;
+ if(m_mailid>=0xFFFFFFFF)
+ {
+ sLog.outError("Mail ids overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_mailid;
+}
+
+uint32 ObjectMgr::GenerateItemTextID()
+{
+ ++m_ItemTextId;
+ if(m_ItemTextId>=0xFFFFFFFF)
+ {
+ sLog.outError("Item text ids overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_ItemTextId;
+}
+
+uint32 ObjectMgr::CreateItemText(std::string text)
+{
+ uint32 newItemTextId = GenerateItemTextID();
+ //insert new itempage to container
+ mItemTexts[ newItemTextId ] = text;
+ //save new itempage
+ CharacterDatabase.escape_string(text);
+ //any Delete query needed, itemTextId is maximum of all ids
+ std::ostringstream query;
+ query << "INSERT INTO item_text (id,text) VALUES ( '" << newItemTextId << "', '" << text << "')";
+ CharacterDatabase.Execute(query.str().c_str()); //needs to be run this way, because mail body may be more than 1024 characters
+ return newItemTextId;
+}
+
+uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh)
+{
+ switch(guidhigh)
+ {
+ case HIGHGUID_ITEM:
+ ++m_hiItemGuid;
+ if(m_hiItemGuid>=0xFFFFFFFF)
+ {
+ sLog.outError("Item guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiItemGuid;
+ case HIGHGUID_UNIT:
+ ++m_hiCreatureGuid;
+ if(m_hiCreatureGuid>=0x00FFFFFF)
+ {
+ sLog.outError("Creature guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiCreatureGuid;
+ case HIGHGUID_PET:
+ ++m_hiPetGuid;
+ if(m_hiPetGuid>=0x00FFFFFF)
+ {
+ sLog.outError("Pet guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiPetGuid;
+ case HIGHGUID_PLAYER:
+ ++m_hiCharGuid;
+ if(m_hiCharGuid>=0xFFFFFFFF)
+ {
+ sLog.outError("Players guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiCharGuid;
+ case HIGHGUID_GAMEOBJECT:
+ ++m_hiGoGuid;
+ if(m_hiGoGuid>=0x00FFFFFF)
+ {
+ sLog.outError("Gameobject guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiGoGuid;
+ case HIGHGUID_CORPSE:
+ ++m_hiCorpseGuid;
+ if(m_hiCorpseGuid>=0xFFFFFFFF)
+ {
+ sLog.outError("Corpse guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiCorpseGuid;
+ case HIGHGUID_DYNAMICOBJECT:
+ ++m_hiDoGuid;
+ if(m_hiDoGuid>=0xFFFFFFFF)
+ {
+ sLog.outError("DynamicObject guid overflow!! Can't continue, shuting down server. ");
+ sWorld.m_stopEvent = true;
+ }
+ return m_hiDoGuid;
+ default:
+ ASSERT(0);
+ }
+
+ ASSERT(0);
+ return 0;
+}
+
+void ObjectMgr::LoadGameObjectLocales()
+{
+ QueryResult *result = WorldDatabase.Query("SELECT entry,"
+ "name_loc1,name_loc2,name_loc3,name_loc4,name_loc5,name_loc6,name_loc7,name_loc8,"
+ "castbarcaption_loc1,castbarcaption_loc2,castbarcaption_loc3,castbarcaption_loc4,"
+ "castbarcaption_loc5,castbarcaption_loc6,castbarcaption_loc7,castbarcaption_loc8 FROM locales_gameobject");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ sLog.outString(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 entry = fields[0].GetUInt32();
+
+ GameObjectLocale& data = mGameObjectLocaleMap[entry];
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[i].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.Name.size() <= idx)
+ data.Name.resize(idx+1);
+
+ data.Name[idx] = str;
+ }
+ }
+ }
+
+ for(int i = MAX_LOCALE; i < MAX_LOCALE*2-1; ++i)
+ {
+ std::string str = fields[i].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ if(data.CastBarCaption.size() <= idx)
+ data.CastBarCaption.resize(idx+1);
+
+ data.CastBarCaption[idx] = str;
+ }
+ }
+ }
+
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u gameobject locale strings", mGameObjectLocaleMap.size() );
+}
+
+void ObjectMgr::LoadGameobjectInfo()
+{
+ sGOStorage.Load();
+
+ // some checks
+ for(uint32 id = 1; id < sGOStorage.MaxEntry; id++)
+ {
+ GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
+ if(!goInfo)
+ continue;
+
+ switch(goInfo->type)
+ {
+ case GAMEOBJECT_TYPE_DOOR: //0
+ {
+ if(goInfo->door.lockId)
+ {
+ if(!sLockStore.LookupEntry(goInfo->door.lockId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.",
+ id,goInfo->type,goInfo->door.lockId,goInfo->door.lockId);
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_BUTTON: //1
+ {
+ if(goInfo->button.lockId)
+ {
+ if(!sLockStore.LookupEntry(goInfo->button.lockId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.",
+ id,goInfo->type,goInfo->button.lockId,goInfo->button.lockId);
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_CHEST: //3
+ {
+ if(goInfo->chest.lockId)
+ {
+ if(!sLockStore.LookupEntry(goInfo->chest.lockId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but lock (Id: %u) not found.",
+ id,goInfo->type,goInfo->chest.lockId,goInfo->chest.lockId);
+ }
+ if(goInfo->chest.linkedTrapId) // linked trap
+ {
+ if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->chest.linkedTrapId))
+ {
+ if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
+ id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
+ }
+ /* disable check for while
+ else
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
+ id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId);
+ */
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_TRAP: //6
+ {
+ /* disable check for while
+ if(goInfo->trap.spellId) // spell
+ {
+ if(!sSpellStore.LookupEntry(goInfo->trap.spellId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.",
+ id,goInfo->type,goInfo->trap.spellId,goInfo->trap.spellId);
+ }
+ */
+ break;
+ }
+ case GAMEOBJECT_TYPE_CHAIR: //7
+ if(goInfo->chair.height > 2)
+ {
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but correct chair height in range 0..2.",
+ id,goInfo->type,goInfo->chair.height);
+
+ // prevent client and server unexpected work
+ const_cast<GameObjectInfo*>(goInfo)->chair.height = 0;
+ }
+ break;
+ case GAMEOBJECT_TYPE_SPELL_FOCUS: //8
+ {
+ if(goInfo->spellFocus.focusId)
+ {
+ if(!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
+ id,goInfo->type,goInfo->spellFocus.focusId,goInfo->spellFocus.focusId);
+ }
+
+ if(goInfo->spellFocus.linkedTrapId) // linked trap
+ {
+ if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->spellFocus.linkedTrapId))
+ {
+ if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
+ id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
+ }
+ /* disable check for while
+ else
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
+ id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId);
+ */
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_GOOBER: //10
+ {
+ if(goInfo->goober.pageId) // pageId
+ {
+ if(!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
+ id,goInfo->type,goInfo->goober.pageId,goInfo->goober.pageId);
+ }
+ /* disable check for while
+ if(goInfo->goober.spellId) // spell
+ {
+ if(!sSpellStore.LookupEntry(goInfo->goober.spellId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but Spell (Entry %u) not exist.",
+ id,goInfo->type,goInfo->goober.spellId,goInfo->goober.spellId);
+ }
+ */
+ if(goInfo->goober.linkedTrapId) // linked trap
+ {
+ if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->goober.linkedTrapId))
+ {
+ if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
+ id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
+ }
+ /* disable check for while
+ else
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
+ id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId);
+ */
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_MO_TRANSPORT: //15
+ {
+ if(goInfo->moTransport.taxiPathId)
+ {
+ if(goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty())
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
+ id,goInfo->type,goInfo->moTransport.taxiPathId,goInfo->moTransport.taxiPathId);
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
+ {
+ /* disabled
+ if(goInfo->summoningRitual.spellId)
+ {
+ if(!sSpellStore.LookupEntry(goInfo->summoningRitual.spellId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but Spell (Entry %u) not exist.",
+ id,goInfo->type,goInfo->summoningRitual.spellId,goInfo->summoningRitual.spellId);
+ }
+ */
+ break;
+ }
+ case GAMEOBJECT_TYPE_SPELLCASTER: //22
+ {
+ if(goInfo->spellcaster.spellId) // spell
+ {
+ if(!sSpellStore.LookupEntry(goInfo->spellcaster.spellId))
+ sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.",
+ id,goInfo->type,goInfo->spellcaster.spellId,goInfo->spellcaster.spellId);
+ }
+ break;
+ }
+ }
+ }
+
+ sLog.outString( ">> Loaded %u game object templates", sGOStorage.RecordCount );
+ sLog.outString();
+}
+
+void ObjectMgr::LoadExplorationBaseXP()
+{
+ uint32 count = 0;
+ QueryResult *result = WorldDatabase.Query("SELECT level,basexp FROM exploration_basexp");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u BaseXP definitions", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+ uint32 level = fields[0].GetUInt32();
+ uint32 basexp = fields[1].GetUInt32();
+ mBaseXPTable[level] = basexp;
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u BaseXP definitions", count );
+}
+
+uint32 ObjectMgr::GetBaseXP(uint32 level)
+{
+ return mBaseXPTable[level] ? mBaseXPTable[level] : 0;
+}
+
+void ObjectMgr::LoadPetNames()
+{
+ uint32 count = 0;
+ QueryResult *result = WorldDatabase.Query("SELECT word,entry,half FROM pet_name_generation");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u pet name parts", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+ std::string word = fields[0].GetString();
+ uint32 entry = fields[1].GetUInt32();
+ bool half = fields[2].GetBool();
+ if(half)
+ PetHalfName1[entry].push_back(word);
+ else
+ PetHalfName0[entry].push_back(word);
+ ++count;
+ }
+ while (result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u pet name parts", count );
+}
+
+void ObjectMgr::LoadPetNumber()
+{
+ QueryResult* result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
+ if(result)
+ {
+ Field *fields = result->Fetch();
+ m_hiPetNumber = fields[0].GetUInt32()+1;
+ delete result;
+ }
+
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded the max pet number: %d", m_hiPetNumber-1);
+}
+
+std::string ObjectMgr::GeneratePetName(uint32 entry)
+{
+ std::vector<std::string> & list0 = PetHalfName0[entry];
+ std::vector<std::string> & list1 = PetHalfName1[entry];
+
+ if(list0.empty() || list1.empty())
+ {
+ CreatureInfo const *cinfo = GetCreatureTemplate(entry);
+ char* petname = GetPetName(cinfo->family, sWorld.GetDefaultDbcLocale());
+ if(!petname)
+ petname = cinfo->Name;
+ return std::string(petname);
+ }
+
+ return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
+}
+
+uint32 ObjectMgr::GeneratePetNumber()
+{
+ return ++m_hiPetNumber;
+}
+
+void ObjectMgr::LoadCorpses()
+{
+ uint32 count = 0;
+ // 0 1 2 3 4 5 6 7 8 10
+ QueryResult *result = CharacterDatabase.PQuery("SELECT position_x, position_y, position_z, orientation, map, data, time, corpse_type, instance, guid FROM corpse WHERE corpse_type <> 0");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u corpses", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 guid = fields[result->GetFieldCount()-1].GetUInt32();
+
+ Corpse *corpse = new Corpse;
+ if(!corpse->LoadFromDB(guid,fields))
+ {
+ delete corpse;
+ continue;
+ }
+
+ ObjectAccessor::Instance().AddCorpse(corpse);
+
+ ++count;
+ }
+ while (result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u corpses", count );
+}
+
+void ObjectMgr::LoadReputationOnKill()
+{
+ uint32 count = 0;
+
+ // 0 1 2
+ QueryResult *result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2,"
+ // 3 4 5 6 7 8 9
+ "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
+ "FROM creature_onkill_reputation");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 creature_id = fields[0].GetUInt32();
+
+ ReputationOnKillEntry repOnKill;
+ repOnKill.repfaction1 = fields[1].GetUInt32();
+ repOnKill.repfaction2 = fields[2].GetUInt32();
+ repOnKill.is_teamaward1 = fields[3].GetBool();
+ repOnKill.reputation_max_cap1 = fields[4].GetUInt32();
+ repOnKill.repvalue1 = fields[5].GetInt32();
+ repOnKill.is_teamaward2 = fields[6].GetBool();
+ repOnKill.reputation_max_cap2 = fields[7].GetUInt32();
+ repOnKill.repvalue2 = fields[8].GetInt32();
+ repOnKill.team_dependent = fields[9].GetUInt8();
+
+ if(!GetCreatureTemplate(creature_id))
+ {
+ sLog.outErrorDb("Table `creature_onkill_reputation` have data for not existed creature entry (%u), skipped",creature_id);
+ continue;
+ }
+
+ if(repOnKill.repfaction1)
+ {
+ FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(repOnKill.repfaction1);
+ if(!factionEntry1)
+ {
+ sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`",repOnKill.repfaction1);
+ continue;
+ }
+ }
+
+ if(repOnKill.repfaction2)
+ {
+ FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(repOnKill.repfaction2);
+ if(!factionEntry2)
+ {
+ sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`",repOnKill.repfaction2);
+ continue;
+ }
+ }
+
+ mRepOnKill[creature_id] = repOnKill;
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u creature award reputation definitions", count);
+}
+
+void ObjectMgr::LoadWeatherZoneChances()
+{
+ uint32 count = 0;
+
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12
+ QueryResult *result = WorldDatabase.Query("SELECT zone, spring_rain_chance, spring_snow_chance, spring_storm_chance, summer_rain_chance, summer_snow_chance, summer_storm_chance, fall_rain_chance, fall_snow_chance, fall_storm_chance, winter_rain_chance, winter_snow_chance, winter_storm_chance FROM game_weather");
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 zone_id = fields[0].GetUInt32();
+
+ WeatherZoneChances& wzc = mWeatherZoneMap[zone_id];
+
+ for(int season = 0; season < WEATHER_SEASONS; ++season)
+ {
+ wzc.data[season].rainChance = fields[season * (MAX_WEATHER_TYPE-1) + 1].GetUInt32();
+ wzc.data[season].snowChance = fields[season * (MAX_WEATHER_TYPE-1) + 2].GetUInt32();
+ wzc.data[season].stormChance = fields[season * (MAX_WEATHER_TYPE-1) + 3].GetUInt32();
+
+ if(wzc.data[season].rainChance > 100)
+ {
+ wzc.data[season].rainChance = 25;
+ sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%",zone_id,season);
+ }
+
+ if(wzc.data[season].snowChance > 100)
+ {
+ wzc.data[season].snowChance = 25;
+ sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%",zone_id,season);
+ }
+
+ if(wzc.data[season].stormChance > 100)
+ {
+ wzc.data[season].stormChance = 25;
+ sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%",zone_id,season);
+ }
+ }
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u weather definitions", count);
+}
+
+void ObjectMgr::SaveCreatureRespawnTime(uint32 loguid, uint32 instance, time_t t)
+{
+ mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = t;
+ WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance);
+ if(t)
+ WorldDatabase.PExecute("INSERT INTO creature_respawn VALUES ( '%u', '" I64FMTD "', '%u' )", loguid, uint64(t), instance);
+}
+
+void ObjectMgr::DeleteCreatureData(uint32 guid)
+{
+ // remove mapid*cellid -> guid_set map
+ CreatureData const* data = GetCreatureData(guid);
+ if(data)
+ RemoveCreatureFromGrid(guid, data);
+
+ mCreatureDataMap.erase(guid);
+}
+
+void ObjectMgr::SaveGORespawnTime(uint32 loguid, uint32 instance, time_t t)
+{
+ mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = t;
+ WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance);
+ if(t)
+ WorldDatabase.PExecute("INSERT INTO gameobject_respawn VALUES ( '%u', '" I64FMTD "', '%u' )", loguid, uint64(t), instance);
+}
+
+void ObjectMgr::DeleteRespawnTimeForInstance(uint32 instance)
+{
+ RespawnTimes::iterator next;
+
+ for(RespawnTimes::iterator itr = mGORespawnTimes.begin(); itr != mGORespawnTimes.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+
+ if(GUID_HIPART(itr->first)==instance)
+ mGORespawnTimes.erase(itr);
+ }
+
+ for(RespawnTimes::iterator itr = mCreatureRespawnTimes.begin(); itr != mCreatureRespawnTimes.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+
+ if(GUID_HIPART(itr->first)==instance)
+ mCreatureRespawnTimes.erase(itr);
+ }
+
+ WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'", instance);
+ WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", instance);
+}
+
+void ObjectMgr::DeleteGOData(uint32 guid)
+{
+ // remove mapid*cellid -> guid_set map
+ GameObjectData const* data = GetGOData(guid);
+ if(data)
+ RemoveGameobjectFromGrid(guid, data);
+
+ mGameObjectDataMap.erase(guid);
+}
+
+void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance)
+{
+ // corpses are always added to spawn mode 0 and they are spawned by their instance id
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid,0)][cellid];
+ cell_guids.corpses[player_guid] = instance;
+}
+
+void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid)
+{
+ // corpses are always added to spawn mode 0 and they are spawned by their instance id
+ CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid,0)][cellid];
+ cell_guids.corpses.erase(player_guid);
+}
+
+void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map,char const* table)
+{
+ map.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT id,quest FROM %s",table);
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded 0 quest relations from %s. DB table `%s` is empty.",table,table);
+ return;
+ }
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ uint32 id = fields[0].GetUInt32();
+ uint32 quest = fields[1].GetUInt32();
+
+ if(mQuestTemplates.find(quest) == mQuestTemplates.end())
+ {
+ sLog.outErrorDb("Table `%s: Quest %u listed for entry %u does not exist.",table,quest,id);
+ continue;
+ }
+
+ map.insert(QuestRelations::value_type(id,quest));
+
+ ++count;
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString(">> Loaded %u quest relations from %s", count,table);
+}
+
+void ObjectMgr::LoadGameobjectQuestRelations()
+{
+ LoadQuestRelationsHelper(mGOQuestRelations,"gameobject_questrelation");
+
+ for(QuestRelations::iterator itr = mGOQuestRelations.begin(); itr != mGOQuestRelations.end(); ++itr)
+ {
+ GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
+ if(!goInfo)
+ sLog.outErrorDb("Table `gameobject_questrelation` have data for not existed gameobject entry (%u) and existed quest %u",itr->first,itr->second);
+ else if(goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
+ sLog.outErrorDb("Table `gameobject_questrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER",itr->first,itr->second);
+ }
+}
+
+void ObjectMgr::LoadGameobjectInvolvedRelations()
+{
+ LoadQuestRelationsHelper(mGOQuestInvolvedRelations,"gameobject_involvedrelation");
+
+ for(QuestRelations::iterator itr = mGOQuestInvolvedRelations.begin(); itr != mGOQuestInvolvedRelations.end(); ++itr)
+ {
+ GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
+ if(!goInfo)
+ sLog.outErrorDb("Table `gameobject_involvedrelation` have data for not existed gameobject entry (%u) and existed quest %u",itr->first,itr->second);
+ else if(goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
+ sLog.outErrorDb("Table `gameobject_involvedrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER",itr->first,itr->second);
+ }
+}
+
+void ObjectMgr::LoadCreatureQuestRelations()
+{
+ LoadQuestRelationsHelper(mCreatureQuestRelations,"creature_questrelation");
+
+ for(QuestRelations::iterator itr = mCreatureQuestRelations.begin(); itr != mCreatureQuestRelations.end(); ++itr)
+ {
+ CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
+ if(!cInfo)
+ sLog.outErrorDb("Table `creature_questrelation` have data for not existed creature entry (%u) and existed quest %u",itr->first,itr->second);
+ else if(!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
+ sLog.outErrorDb("Table `creature_questrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER",itr->first,itr->second);
+ }
+}
+
+void ObjectMgr::LoadCreatureInvolvedRelations()
+{
+ LoadQuestRelationsHelper(mCreatureQuestInvolvedRelations,"creature_involvedrelation");
+
+ for(QuestRelations::iterator itr = mCreatureQuestInvolvedRelations.begin(); itr != mCreatureQuestInvolvedRelations.end(); ++itr)
+ {
+ CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
+ if(!cInfo)
+ sLog.outErrorDb("Table `creature_involvedrelation` have data for not existed creature entry (%u) and existed quest %u",itr->first,itr->second);
+ else if(!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
+ sLog.outErrorDb("Table `creature_involvedrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER",itr->first,itr->second);
+ }
+}
+
+void ObjectMgr::LoadReservedPlayersNames()
+{
+ m_ReservedNames.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.PQuery("SELECT name FROM reserved_name");
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u reserved player names", count );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ Field* fields;
+ do
+ {
+ bar.step();
+ fields = result->Fetch();
+ std::string name= fields[0].GetCppString();
+ if(normalizePlayerName(name))
+ {
+ m_ReservedNames.insert(name);
+ ++count;
+ }
+ } while ( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u reserved player names", count );
+}
+
+enum LanguageType
+{
+ LT_BASIC_LATIN = 0x0000,
+ LT_EXTENDEN_LATIN = 0x0001,
+ LT_CYRILLIC = 0x0002,
+ LT_EAST_ASIA = 0x0004,
+ LT_ANY = 0xFFFF
+};
+
+static LanguageType GetRealmLanguageType(bool create)
+{
+ switch(sWorld.getConfig(CONFIG_REALM_ZONE))
+ {
+ case REALM_ZONE_UNKNOWN: // any language
+ case REALM_ZONE_DEVELOPMENT:
+ case REALM_ZONE_TEST_SERVER:
+ case REALM_ZONE_QA_SERVER:
+ return LT_ANY;
+ case REALM_ZONE_UNITED_STATES: // extended-Latin
+ case REALM_ZONE_OCEANIC:
+ case REALM_ZONE_LATIN_AMERICA:
+ case REALM_ZONE_ENGLISH:
+ case REALM_ZONE_GERMAN:
+ case REALM_ZONE_FRENCH:
+ case REALM_ZONE_SPANISH:
+ return LT_EXTENDEN_LATIN;
+ case REALM_ZONE_KOREA: // East-Asian
+ case REALM_ZONE_TAIWAN:
+ case REALM_ZONE_CHINA:
+ return LT_EAST_ASIA;
+ case REALM_ZONE_RUSSIAN: // Cyrillic
+ return LT_CYRILLIC;
+ default:
+ return create ? LT_BASIC_LATIN : LT_ANY; // basic-Latin at create, any at login
+ }
+}
+
+bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
+{
+ if(strictMask==0) // any language, ignore realm
+ {
+ if(isExtendedLatinString(wstr,numericOrSpace))
+ return true;
+ if(isCyrillicString(wstr,numericOrSpace))
+ return true;
+ if(isEastAsianString(wstr,numericOrSpace))
+ return true;
+ return false;
+ }
+
+ if(strictMask & 0x2) // realm zone specific
+ {
+ LanguageType lt = GetRealmLanguageType(create);
+ if(lt & LT_EXTENDEN_LATIN)
+ if(isExtendedLatinString(wstr,numericOrSpace))
+ return true;
+ if(lt & LT_CYRILLIC)
+ if(isCyrillicString(wstr,numericOrSpace))
+ return true;
+ if(lt & LT_EAST_ASIA)
+ if(isEastAsianString(wstr,numericOrSpace))
+ return true;
+ }
+
+ if(strictMask & 0x1) // basic latin
+ {
+ if(isBasicLatinString(wstr,numericOrSpace))
+ return true;
+ }
+
+ return false;
+}
+
+bool ObjectMgr::IsValidName( std::string name, bool create )
+{
+ std::wstring wname;
+ if(!Utf8toWStr(name,wname))
+ return false;
+
+ if(wname.size() < 1 || wname.size() > MAX_PLAYER_NAME)
+ return false;
+
+ uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PLAYER_NAMES);
+
+ return isValidString(wname,strictMask,false,create);
+}
+
+bool ObjectMgr::IsValidCharterName( std::string name )
+{
+ std::wstring wname;
+ if(!Utf8toWStr(name,wname))
+ return false;
+
+ if(wname.size() < 1)
+ return false;
+
+ uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_CHARTER_NAMES);
+
+ return isValidString(wname,strictMask,true);
+}
+
+bool ObjectMgr::IsValidPetName( std::string name )
+{
+ std::wstring wname;
+ if(!Utf8toWStr(name,wname))
+ return false;
+
+ if(wname.size() < 1)
+ return false;
+
+ uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PET_NAMES);
+
+ return isValidString(wname,strictMask,false);
+}
+
+int ObjectMgr::GetIndexForLocale( LocaleConstant loc )
+{
+ if(loc==LOCALE_enUS)
+ return -1;
+
+ for(size_t i=0;i < m_LocalForIndex.size(); ++i)
+ if(m_LocalForIndex[i]==loc)
+ return i;
+
+ return -1;
+}
+
+LocaleConstant ObjectMgr::GetLocaleForIndex(int i)
+{
+ if (i<0 || i>=m_LocalForIndex.size())
+ return LOCALE_enUS;
+
+ return m_LocalForIndex[i];
+}
+
+int ObjectMgr::GetOrNewIndexForLocale( LocaleConstant loc )
+{
+ if(loc==LOCALE_enUS)
+ return -1;
+
+ for(size_t i=0;i < m_LocalForIndex.size(); ++i)
+ if(m_LocalForIndex[i]==loc)
+ return i;
+
+ m_LocalForIndex.push_back(loc);
+ return m_LocalForIndex.size()-1;
+}
+
+void ObjectMgr::LoadBattleMastersEntry()
+{
+ mBattleMastersMap.clear(); // need for reload case
+
+ QueryResult *result = WorldDatabase.Query( "SELECT entry,bg_template FROM battlemaster_entry" );
+
+ uint32 count = 0;
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+ bar.step();
+
+ sLog.outString();
+ sLog.outString( ">> Loaded 0 battlemaster entries - table is empty!" );
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ ++count;
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 entry = fields[0].GetUInt32();
+ uint32 bgTypeId = fields[1].GetUInt32();
+
+ mBattleMastersMap[entry] = bgTypeId;
+
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u battlemaster entries", count );
+}
+
+void ObjectMgr::LoadGameObjectForQuests()
+{
+ mGameObjectForQuestSet.clear(); // need for reload case
+
+ uint32 count = 0;
+
+ // collect GO entries for GO that must activated
+ for(uint32 go_entry = 1; go_entry < sGOStorage.MaxEntry; ++go_entry)
+ {
+ GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(go_entry);
+ if(!goInfo)
+ continue;
+
+ switch(goInfo->type)
+ {
+ // scan GO chest with loot including quest items
+ case GAMEOBJECT_TYPE_CHEST:
+ {
+ uint32 loot_id = GameObject::GetLootId(goInfo);
+
+ // find quest loot for GO
+ if(LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
+ {
+ mGameObjectForQuestSet.insert(go_entry);
+ ++count;
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_GOOBER:
+ {
+ if(goInfo->goober.questId) //quests objects
+ {
+ mGameObjectForQuestSet.insert(go_entry);
+ count++;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u GameObject for quests", count );
+}
+
+bool ObjectMgr::LoadMangosStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value)
+{
+ // cleanup affected map part for reloading case
+ for(MangosStringLocaleMap::iterator itr = mMangosStringLocaleMap.begin(); itr != mMangosStringLocaleMap.end();)
+ {
+ if(itr->first >= min_value && itr->first <= max_value)
+ {
+ MangosStringLocaleMap::iterator itr2 = itr;
+ ++itr;
+ mMangosStringLocaleMap.erase(itr2);
+ }
+ else
+ ++itr;
+ }
+
+ QueryResult *result = db.PQuery("SELECT entry,content_default,content_loc1,content_loc2,content_loc3,content_loc4,content_loc5,content_loc6,content_loc7,content_loc8 FROM %s",table);
+
+ if(!result)
+ {
+ barGoLink bar(1);
+
+ bar.step();
+
+ sLog.outString("");
+ if(min_value > 0) // error only in case internal strings
+ sLog.outErrorDb(">> Loaded 0 mangos strings. DB table `%s` is empty. Cannot continue.",table);
+ else
+ sLog.outString(">> Loaded 0 string templates. DB table `%s` is empty.",table);
+ return false;
+ }
+
+ uint32 count = 0;
+
+ barGoLink bar(result->GetRowCount());
+
+ do
+ {
+ Field *fields = result->Fetch();
+ bar.step();
+
+ int32 entry = fields[0].GetInt32();
+
+ if(entry==0)
+ {
+ sLog.outErrorDb("Table `%s` contain reserved entry 0, ignored.",table);
+ continue;
+ }
+ else if(entry < min_value || entry > max_value)
+ {
+ int32 start = min_value > 0 ? min_value : max_value;
+ int32 end = min_value > 0 ? max_value : min_value;
+ sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,start,end);
+ continue;
+ }
+
+ MangosStringLocale& data = mMangosStringLocaleMap[entry];
+
+ if(data.Content.size() > 0)
+ {
+ sLog.outErrorDb("Table `%s` contain data for already loaded entry %i (from another table?), ignored.",table,entry);
+ continue;
+ }
+
+ data.Content.resize(1);
+ ++count;
+
+ // 0 -> default, idx in to idx+1
+ data.Content[0] = fields[1].GetCppString();
+
+ for(int i = 1; i < MAX_LOCALE; ++i)
+ {
+ std::string str = fields[i+1].GetCppString();
+ if(!str.empty())
+ {
+ int idx = GetOrNewIndexForLocale(LocaleConstant(i));
+ if(idx >= 0)
+ {
+ // 0 -> default, idx in to idx+1
+ if(data.Content.size() <= idx+1)
+ data.Content.resize(idx+2);
+
+ data.Content[idx+1] = str;
+ }
+ }
+ }
+ } while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ if(min_value > 0) // internal mangos strings
+ sLog.outString( ">> Loaded %u MaNGOS strings from table %s", count,table);
+ else
+ sLog.outString( ">> Loaded %u string templates from %s", count,table);
+
+ return true;
+}
+
+const char *ObjectMgr::GetMangosString(int32 entry, int locale_idx) const
+{
+ // locale_idx==-1 -> default, locale_idx >= 0 in to idx+1
+ // Content[0] always exist if exist MangosStringLocale
+ if(MangosStringLocale const *msl = GetMangosStringLocale(entry))
+ {
+ if(msl->Content.size() > locale_idx+1 && !msl->Content[locale_idx+1].empty())
+ return msl->Content[locale_idx+1].c_str();
+ else
+ return msl->Content[0].c_str();
+ }
+
+ if(entry > 0)
+ sLog.outErrorDb("Entry %i not found in `mangos_string` table.",entry);
+ else
+ sLog.outErrorDb("Mangos string entry %i not found in DB.",entry);
+ return "<error>";
+}
+
+void ObjectMgr::LoadFishingBaseSkillLevel()
+{
+ mFishingBaseForArea.clear(); // for relaod case
+
+ uint32 count = 0;
+ QueryResult *result = WorldDatabase.Query("SELECT entry,skill FROM skill_fishing_base_level");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded `skill_fishing_base_level`, table is empty!");
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+ uint32 entry = fields[0].GetUInt32();
+ int32 skill = fields[1].GetInt32();
+
+ AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry);
+ if(!fArea)
+ {
+ sLog.outErrorDb("AreaId %u defined in `skill_fishing_base_level` does not exist",entry);
+ continue;
+ }
+
+ mFishingBaseForArea[entry] = skill;
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u areas for fishing base skill level", count );
+}
+
+// Searches for the same condition already in Conditions store
+// Returns Id if found, else adds it to Conditions and returns Id
+uint16 ObjectMgr::GetConditionId( ConditionType condition, uint32 value1, uint32 value2 )
+{
+ PlayerCondition lc = PlayerCondition(condition, value1, value2);
+ for (uint16 i=0; i < mConditions.size(); ++i)
+ {
+ if (lc == mConditions[i])
+ return i;
+ }
+
+ mConditions.push_back(lc);
+
+ if(mConditions.size() > 0xFFFF)
+ {
+ sLog.outError("Conditions store overflow! Current and later loaded conditions will ignored!");
+ return 0;
+ }
+
+ return mConditions.size() - 1;
+}
+
+bool ObjectMgr::CheckDeclinedNames( std::wstring mainpart, DeclinedName const& names )
+{
+ for(int i =0; i < MAX_DECLINED_NAME_CASES; ++i)
+ {
+ std::wstring wname;
+ if(!Utf8toWStr(names.name[i],wname))
+ return false;
+
+ if(mainpart!=GetMainPartOfName(wname,i+1))
+ return false;
+ }
+ return true;
+}
+
+const char* ObjectMgr::GetAreaTriggerScriptName(uint32 id)
+{
+ AreaTriggerScriptMap::const_iterator i = mAreaTriggerScripts.find(id);
+ if(i!= mAreaTriggerScripts.end())
+ return i->second.c_str();
+ return "";
+}
+
+// Checks if player meets the condition
+bool PlayerCondition::Meets(Player const * player) const
+{
+ if( !player )
+ return false; // player not present, return false
+
+ switch (condition)
+ {
+ case CONDITION_NONE:
+ return true; // empty condition, always met
+ case CONDITION_AURA:
+ return player->HasAura(value1, value2);
+ case CONDITION_ITEM:
+ return player->HasItemCount(value1, value2);
+ case CONDITION_ITEM_EQUIPPED:
+ return player->GetItemOrItemWithGemEquipped(value1) != NULL;
+ case CONDITION_ZONEID:
+ return player->GetZoneId() == value1;
+ case CONDITION_REPUTATION_RANK:
+ {
+ FactionEntry const* faction = sFactionStore.LookupEntry(value1);
+ return faction && player->GetReputationRank(faction) >= value2;
+ }
+ case CONDITION_TEAM:
+ return player->GetTeam() == value1;
+ case CONDITION_SKILL:
+ return player->HasSkill(value1) && player->GetBaseSkillValue(value1) >= value2;
+ case CONDITION_QUESTREWARDED:
+ return player->GetQuestRewardStatus(value1);
+ case CONDITION_QUESTTAKEN:
+ {
+ QuestStatus status = player->GetQuestStatus(value1);
+ return (status == QUEST_STATUS_INCOMPLETE);
+ }
+ case CONDITION_AD_COMMISSION_AURA:
+ {
+ Unit::AuraMap const& auras = player->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ if((itr->second->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetSpellProto()->SpellVisual==3580)
+ return true;
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+// Verification of condition values validity
+bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 value2)
+{
+ if( condition >= MAX_CONDITION) // Wrong condition type
+ {
+ sLog.outErrorDb("Condition has bad type of %u, skipped ", condition );
+ return false;
+ }
+
+ switch (condition)
+ {
+ case CONDITION_AURA:
+ {
+ if(!sSpellStore.LookupEntry(value1))
+ {
+ sLog.outErrorDb("Aura condition requires to have non existing spell (Id: %d), skipped", value1);
+ return false;
+ }
+ if(value2 > 2)
+ {
+ sLog.outErrorDb("Aura condition requires to have non existing effect index (%u) (must be 0..2), skipped", value2);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_ITEM:
+ {
+ ItemPrototype const *proto = objmgr.GetItemPrototype(value1);
+ if(!proto)
+ {
+ sLog.outErrorDb("Item condition requires to have non existing item (%u), skipped", value1);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_ITEM_EQUIPPED:
+ {
+ ItemPrototype const *proto = objmgr.GetItemPrototype(value1);
+ if(!proto)
+ {
+ sLog.outErrorDb("ItemEquipped condition requires to have non existing item (%u) equipped, skipped", value1);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_ZONEID:
+ {
+ AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(value1);
+ if(!areaEntry)
+ {
+ sLog.outErrorDb("Zone condition requires to be in non existing area (%u), skipped", value1);
+ return false;
+ }
+ if(areaEntry->zone != 0)
+ {
+ sLog.outErrorDb("Zone condition requires to be in area (%u) which is a subzone but zone expected, skipped", value1);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_REPUTATION_RANK:
+ {
+ FactionEntry const* factionEntry = sFactionStore.LookupEntry(value1);
+ if(!factionEntry)
+ {
+ sLog.outErrorDb("Reputation condition requires to have reputation non existing faction (%u), skipped", value1);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_TEAM:
+ {
+ if (value1 != ALLIANCE && value1 != HORDE)
+ {
+ sLog.outErrorDb("Team condition specifies unknown team (%u), skipped", value1);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_SKILL:
+ {
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(value1);
+ if (!pSkill)
+ {
+ sLog.outErrorDb("Skill condition specifies non-existing skill (%u), skipped", value1);
+ return false;
+ }
+ if (value2 < 1 || value2 > sWorld.GetConfigMaxSkillValue() )
+ {
+ sLog.outErrorDb("Skill condition specifies invalid skill value (%u), skipped", value2);
+ return false;
+ }
+ break;
+ }
+ case CONDITION_QUESTREWARDED:
+ case CONDITION_QUESTTAKEN:
+ {
+ Quest const *Quest = objmgr.GetQuestTemplate(value1);
+ if (!Quest)
+ {
+ sLog.outErrorDb("Quest condition specifies non-existing quest (%u), skipped", value1);
+ return false;
+ }
+ if(value2)
+ sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2);
+ break;
+ }
+ case CONDITION_AD_COMMISSION_AURA:
+ {
+ if(value1)
+ sLog.outErrorDb("Quest condition has useless data in value1 (%u)!", value1);
+ if(value2)
+ sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2);
+ break;
+ }
+ }
+ return true;
+}
+
+SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial)
+{
+ switch(pSkill->categoryId)
+ {
+ case SKILL_CATEGORY_LANGUAGES: return SKILL_RANGE_LANGUAGE;
+ case SKILL_CATEGORY_WEAPON:
+ if(pSkill->id!=SKILL_FIST_WEAPONS)
+ return SKILL_RANGE_LEVEL;
+ else
+ return SKILL_RANGE_MONO;
+ case SKILL_CATEGORY_ARMOR:
+ case SKILL_CATEGORY_CLASS:
+ if(pSkill->id != SKILL_POISONS && pSkill->id != SKILL_LOCKPICKING)
+ return SKILL_RANGE_MONO;
+ else
+ return SKILL_RANGE_LEVEL;
+ case SKILL_CATEGORY_SECONDARY:
+ case SKILL_CATEGORY_PROFESSION:
+ // not set skills for professions and racial abilities
+ if(IsProfessionSkill(pSkill->id))
+ return SKILL_RANGE_RANK;
+ else if(racial)
+ return SKILL_RANGE_NONE;
+ else
+ return SKILL_RANGE_MONO;
+ default:
+ case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc
+ case SKILL_CATEGORY_NOT_DISPLAYED: //only GENEREC(DND)
+ return SKILL_RANGE_NONE;
+ }
+}
+
+void ObjectMgr::LoadGameTele()
+{
+ m_GameTeleMap.clear(); // for relaod case
+
+ uint32 count = 0;
+ QueryResult *result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded `game_tele`, table is empty!");
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ bar.step();
+
+ Field *fields = result->Fetch();
+
+ uint32 id = fields[0].GetUInt32();
+
+ GameTele gt;
+
+ gt.position_x = fields[1].GetFloat();
+ gt.position_y = fields[2].GetFloat();
+ gt.position_z = fields[3].GetFloat();
+ gt.orientation = fields[4].GetFloat();
+ gt.mapId = fields[5].GetUInt32();
+ gt.name = fields[6].GetCppString();
+
+ if(!MapManager::IsValidMapCoord(gt.mapId,gt.position_x,gt.position_y,gt.position_z,gt.orientation))
+ {
+ sLog.outErrorDb("Wrong position for id %u (name: %s) in `game_tele` table, ignoring.",id,gt.name.c_str());
+ continue;
+ }
+
+ if(!Utf8toWStr(gt.name,gt.wnameLow))
+ {
+ sLog.outErrorDb("Wrong UTF8 name for id %u in `game_tele` table, ignoring.",id);
+ continue;
+ }
+
+ wstrToLower( gt.wnameLow );
+
+ m_GameTeleMap[id] = gt;
+
+ ++count;
+ }
+ while (result->NextRow());
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %u game tele's", count );
+}
+
+GameTele const* ObjectMgr::GetGameTele(std::string name) const
+{
+ // explicit name case
+ std::wstring wname;
+ if(!Utf8toWStr(name,wname))
+ return false;
+
+ // converting string that we try to find to lower case
+ wstrToLower( wname );
+
+ for(GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
+ if(itr->second.wnameLow == wname)
+ return &itr->second;
+
+ return NULL;
+}
+
+bool ObjectMgr::AddGameTele(GameTele& tele)
+{
+ // find max id
+ uint32 new_id = 0;
+ for(GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
+ if(itr->first > new_id)
+ new_id = itr->first;
+
+ // use next
+ ++new_id;
+
+ if(!Utf8toWStr(tele.name,tele.wnameLow))
+ return false;
+
+ wstrToLower( tele.wnameLow );
+
+ m_GameTeleMap[new_id] = tele;
+
+ return WorldDatabase.PExecuteLog("INSERT INTO game_tele (id,position_x,position_y,position_z,orientation,map,name) VALUES (%u,%f,%f,%f,%f,%d,'%s')",
+ new_id,tele.position_x,tele.position_y,tele.position_z,tele.orientation,tele.mapId,tele.name.c_str());
+}
+
+bool ObjectMgr::DeleteGameTele(std::string name)
+{
+ // explicit name case
+ std::wstring wname;
+ if(!Utf8toWStr(name,wname))
+ return false;
+
+ // converting string that we try to find to lower case
+ wstrToLower( wname );
+
+ for(GameTeleMap::iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
+ {
+ if(itr->second.wnameLow == wname)
+ {
+ WorldDatabase.PExecuteLog("DELETE FROM game_tele WHERE name = '%s'",itr->second.name.c_str());
+ m_GameTeleMap.erase(itr);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ObjectMgr::LoadTrainerSpell()
+{
+ // For reload case
+ for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
+ 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 )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded `npc_trainer`, table is empty!");
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ uint32 count = 0;
+ do
+ {
+ bar.step();
+
+ Field* fields = result->Fetch();
+
+ uint32 entry = fields[0].GetUInt32();
+ uint32 spell = fields[1].GetUInt32();
+
+ CreatureInfo const* cInfo = GetCreatureTemplate(entry);
+
+ if(!cInfo)
+ {
+ sLog.outErrorDb("Table `npc_trainer` have entry for not existed creature template (Entry: %u), ignore", entry);
+ continue;
+ }
+
+ if(!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
+ {
+ 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;
+ }
+
+ SpellEntry const *spellinfo = sSpellStore.LookupEntry(spell);
+ if(!spellinfo)
+ {
+ sLog.outErrorDb("Table `npc_trainer` for Trainer (Entry: %u ) has non existing spell %u, ignore", entry,spell);
+ continue;
+ }
+
+ if(!SpellMgr::IsSpellValid(spellinfo))
+ {
+ sLog.outErrorDb("Table `npc_trainer` for Trainer (Entry: %u) has broken learning spell %u, ignore", entry, spell);
+ continue;
+ }
+
+ TrainerSpell* pTrainerSpell = new TrainerSpell();
+ pTrainerSpell->spell = spell;
+ pTrainerSpell->spellcost = fields[2].GetUInt32();
+ pTrainerSpell->reqskill = fields[3].GetUInt32();
+ pTrainerSpell->reqskillvalue = fields[4].GetUInt32();
+ pTrainerSpell->reqlevel = fields[5].GetUInt32();
+
+ if(!pTrainerSpell->reqlevel)
+ pTrainerSpell->reqlevel = spellinfo->spellLevel;
+
+
+ TrainerSpellData& data = m_mCacheTrainerSpellMap[entry];
+
+ if(SpellMgr::IsProfessionSpell(spell))
+ data.trainerType = 2;
+
+ data.spellList.push_back(pTrainerSpell);
+ ++count;
+
+ } while (result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded Trainers %d", count );
+}
+
+void ObjectMgr::LoadVendors()
+{
+ // For reload case
+ for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
+ 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 )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded `npc_vendor`, table is empty!");
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ uint32 count = 0;
+ do
+ {
+ bar.step();
+ Field* fields = result->Fetch();
+
+ uint32 entry = fields[0].GetUInt32();
+ uint32 item_id = fields[1].GetUInt32();
+ uint32 maxcount = fields[2].GetUInt32();
+ uint32 incrtime = fields[3].GetUInt32();
+ uint32 ExtendedCost = fields[4].GetUInt32();
+
+ if(!IsVendorItemValid(entry,item_id,maxcount,incrtime,ExtendedCost,NULL,&skip_vendors))
+ continue;
+
+ VendorItemData& vList = m_mCacheVendorItemMap[entry];
+
+ vList.AddItem(item_id,maxcount,incrtime,ExtendedCost);
+ ++count;
+
+ } while (result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %d Vendors ", count );
+}
+
+void ObjectMgr::LoadNpcTextId()
+{
+
+ m_mCacheNpcTextIdMap.clear();
+
+ QueryResult* result = WorldDatabase.PQuery("SELECT npc_guid, textid FROM npc_gossip");
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb(">> Loaded `npc_gossip`, table is empty!");
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ uint32 count = 0;
+ uint32 guid,textid;
+ do
+ {
+ bar.step();
+
+ Field* fields = result->Fetch();
+
+ guid = fields[0].GetUInt32();
+ textid = fields[1].GetUInt32();
+
+ if (!GetCreatureData(guid))
+ {
+ sLog.outErrorDb("Table `npc_gossip` have not existed creature (GUID: %u) entry, ignore. ",guid);
+ continue;
+ }
+ if (!GetGossipText(textid))
+ {
+ sLog.outErrorDb("Table `npc_gossip` for creature (GUID: %u) have wrong Textid (%u), ignore. ", guid, textid);
+ continue;
+ }
+
+ m_mCacheNpcTextIdMap[guid] = textid ;
+ ++count;
+
+ } while (result->NextRow());
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Loaded %d NpcTextId ", count );
+}
+
+void ObjectMgr::AddVendorItem( uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 extendedcost )
+{
+ VendorItemData& vList = m_mCacheVendorItemMap[entry];
+ vList.AddItem(item,maxcount,incrtime,extendedcost);
+
+ WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')",entry, item, maxcount,incrtime,extendedcost);
+}
+
+bool ObjectMgr::RemoveVendorItem( uint32 entry,uint32 item )
+{
+ CacheVendorItemMap::iterator iter = m_mCacheVendorItemMap.find(entry);
+ if(iter == m_mCacheVendorItemMap.end())
+ return false;
+
+ if(!iter->second.FindItem(item))
+ return false;
+
+ iter->second.RemoveItem(item);
+ WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",entry, item);
+ return true;
+}
+
+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)
+ {
+ if(pl)
+ ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
+ else
+ sLog.outErrorDb("Table `npc_vendor` have data for not existed creature template (Entry: %u), ignore", vendor_entry);
+ return false;
+ }
+
+ if(!(cInfo->npcflag & UNIT_NPC_FLAG_VENDOR))
+ {
+ 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;
+ }
+
+ if(!GetItemPrototype(item_id))
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id);
+ else
+ sLog.outErrorDb("Table `npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore",vendor_entry,item_id);
+ return false;
+ }
+
+ if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST,ExtendedCost);
+ else
+ sLog.outErrorDb("Table `npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore",item_id,ExtendedCost,vendor_entry);
+ return false;
+ }
+
+ if(maxcount > 0 && incrtime == 0)
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount);
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, item_id, vendor_entry);
+ return false;
+ }
+ else if(maxcount==0 && incrtime > 0)
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage("MaxCount==0 but IncrTime<>=0");
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", item_id, vendor_entry);
+ return false;
+ }
+
+ VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
+ if(!vItems)
+ return true; // later checks for non-empty lists
+
+ if(vItems->FindItem(item_id))
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST,item_id);
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has duplicate items %u for vendor (Entry: %u), ignore", item_id, vendor_entry);
+ return false;
+ }
+
+ if(vItems->GetItemCount() >= MAX_VENDOR_ITEMS)
+ {
+ if(pl)
+ ChatHandler(pl).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vItems->GetItemCount(), MAX_VENDOR_ITEMS, vendor_entry);
+ return false;
+ }
+
+ return true;
+}
+
+// Functions for scripting access
+const char* GetAreaTriggerScriptNameById(uint32 id)
+{
+ return objmgr.GetAreaTriggerScriptName(id);
+}
+
+bool LoadMangosStrings(DatabaseType& db, char const* table,int32 start_value, int32 end_value)
+{
+ if(start_value >= 0 || start_value <= end_value) // start/end reversed for negative values
+ {
+ sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), use (%d - %d) instead.",table,start_value,end_value,-1,std::numeric_limits<int32>::min());
+ start_value = -1;
+ end_value = std::numeric_limits<int32>::min();
+ }
+
+ // for scripting localized strings allowed use _only_ negative entries
+ return objmgr.LoadMangosStrings(db,table,end_value,start_value);
+}
diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h
index f5d35009307..a75e3902425 100644
--- a/src/game/ObjectMgr.h
+++ b/src/game/ObjectMgr.h
@@ -1,865 +1,865 @@
-/*
- * 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
- */
-
-#ifndef _OBJECTMGR_H
-#define _OBJECTMGR_H
-
-#include "Log.h"
-#include "Object.h"
-#include "Bag.h"
-#include "Creature.h"
-#include "Player.h"
-#include "DynamicObject.h"
-#include "GameObject.h"
-#include "Corpse.h"
-#include "QuestDef.h"
-#include "Path.h"
-#include "ItemPrototype.h"
-#include "NPCHandler.h"
-#include "Database/DatabaseEnv.h"
-#include "AuctionHouseObject.h"
-#include "Mail.h"
-#include "Map.h"
-#include "ObjectAccessor.h"
-#include "ObjectDefines.h"
-#include "Policies/Singleton.h"
-#include "Database/SQLStorage.h"
-
-#include <string>
-#include <map>
-#include <limits>
-
-extern SQLStorage sCreatureStorage;
-extern SQLStorage sCreatureDataAddonStorage;
-extern SQLStorage sCreatureInfoAddonStorage;
-extern SQLStorage sCreatureModelStorage;
-extern SQLStorage sEquipmentStorage;
-extern SQLStorage sGOStorage;
-extern SQLStorage sPageTextStore;
-extern SQLStorage sItemStorage;
-extern SQLStorage sInstanceTemplate;
-
-class Group;
-class Guild;
-class ArenaTeam;
-class Path;
-class TransportPath;
-class Item;
-
-struct GameTele
-{
- float position_x;
- float position_y;
- float position_z;
- float orientation;
- uint32 mapId;
- std::string name;
- std::wstring wnameLow;
-};
-
-typedef HM_NAMESPACE::hash_map<uint32, GameTele > GameTeleMap;
-
-struct ScriptInfo
-{
- uint32 id;
- uint32 delay;
- uint32 command;
- uint32 datalong;
- uint32 datalong2;
- std::string datatext;
- float x;
- float y;
- float z;
- float o;
-};
-
-typedef std::multimap<uint32, ScriptInfo> ScriptMap;
-typedef std::map<uint32, ScriptMap > ScriptMapMap;
-extern ScriptMapMap sQuestEndScripts;
-extern ScriptMapMap sQuestStartScripts;
-extern ScriptMapMap sSpellScripts;
-extern ScriptMapMap sGameObjectScripts;
-extern ScriptMapMap sEventScripts;
-
-struct AreaTrigger
-{
- uint8 requiredLevel;
- uint32 requiredItem;
- uint32 requiredItem2;
- uint32 heroicKey;
- uint32 heroicKey2;
- uint32 requiredQuest;
- std::string requiredFailedText;
- uint32 target_mapId;
- float target_X;
- float target_Y;
- float target_Z;
- float target_Orientation;
-};
-
-typedef std::set<uint32> CellGuidSet;
-typedef std::map<uint32/*player guid*/,uint32/*instance*/> CellCorpseSet;
-struct CellObjectGuids
-{
- CellGuidSet creatures;
- CellGuidSet gameobjects;
- CellCorpseSet corpses;
-};
-typedef HM_NAMESPACE::hash_map<uint32/*cell_id*/,CellObjectGuids> CellObjectGuidsMap;
-typedef HM_NAMESPACE::hash_map<uint32/*(mapid,spawnMode) pair*/,CellObjectGuidsMap> MapObjectGuids;
-
-typedef HM_NAMESPACE::hash_map<uint64/*(instance,guid) pair*/,time_t> RespawnTimes;
-
-struct MangosStringLocale
-{
- std::vector<std::string> Content; // 0 -> default, i -> i-1 locale index
-};
-
-typedef HM_NAMESPACE::hash_map<uint32,CreatureData> CreatureDataMap;
-typedef HM_NAMESPACE::hash_map<uint32,GameObjectData> GameObjectDataMap;
-typedef HM_NAMESPACE::hash_map<uint32,CreatureLocale> CreatureLocaleMap;
-typedef HM_NAMESPACE::hash_map<uint32,GameObjectLocale> GameObjectLocaleMap;
-typedef HM_NAMESPACE::hash_map<uint32,ItemLocale> ItemLocaleMap;
-typedef HM_NAMESPACE::hash_map<uint32,QuestLocale> QuestLocaleMap;
-typedef HM_NAMESPACE::hash_map<uint32,NpcTextLocale> NpcTextLocaleMap;
-typedef HM_NAMESPACE::hash_map<uint32,PageTextLocale> PageTextLocaleMap;
-typedef HM_NAMESPACE::hash_map<uint32,MangosStringLocale> MangosStringLocaleMap;
-
-typedef std::multimap<uint32,uint32> QuestRelations;
-
-struct PetLevelInfo
-{
- PetLevelInfo() : health(0), mana(0) { for(int i=0; i < MAX_STATS; ++i ) stats[i] = 0; }
-
- uint16 stats[MAX_STATS];
- uint16 health;
- uint16 mana;
- uint16 armor;
-};
-
-struct ReputationOnKillEntry
-{
- uint32 repfaction1;
- uint32 repfaction2;
- bool is_teamaward1;
- uint32 reputation_max_cap1;
- int32 repvalue1;
- bool is_teamaward2;
- uint32 reputation_max_cap2;
- int32 repvalue2;
- bool team_dependent;
-};
-
-struct PetCreateSpellEntry
-{
- uint32 spellid[4];
-};
-
-#define WEATHER_SEASONS 4
-struct WeatherSeasonChances
-{
- uint32 rainChance;
- uint32 snowChance;
- uint32 stormChance;
-};
-
-struct WeatherZoneChances
-{
- WeatherSeasonChances data[WEATHER_SEASONS];
-};
-
-struct GraveYardData
-{
- uint32 safeLocId;
- uint32 team;
-};
-typedef std::multimap<uint32,GraveYardData> GraveYardMap;
-
-enum ConditionType
-{ // value1 value2 for the Condition enumed
- CONDITION_NONE = 0, // 0 0
- CONDITION_AURA = 1, // spell_id effindex
- CONDITION_ITEM = 2, // item_id count
- CONDITION_ITEM_EQUIPPED = 3, // item_id 0
- CONDITION_ZONEID = 4, // zone_id 0
- CONDITION_REPUTATION_RANK = 5, // faction_id min_rank
- CONDITION_TEAM = 6, // player_team 0, (469 - Alliance 67 - Horde)
- CONDITION_SKILL = 7, // skill_id skill_value
- CONDITION_QUESTREWARDED = 8, // quest_id 0
- CONDITION_QUESTTAKEN = 9, // quest_id 0, for condition true while quest active.
- CONDITION_AD_COMMISSION_AURA = 10, // 0 0, for condition true while one from AD ñommission aura active
-};
-
-#define MAX_CONDITION 11 // maximum value in ConditionType enum
-
-struct PlayerCondition
-{
- ConditionType condition; // additional condition type
- uint32 value1; // data for the condition - see ConditionType definition
- uint32 value2;
-
- PlayerCondition(uint8 _condition = 0, uint32 _value1 = 0, uint32 _value2 = 0)
- : condition(ConditionType(_condition)), value1(_value1), value2(_value2) {}
-
- static bool IsValid(ConditionType condition, uint32 value1, uint32 value2);
- // Checks correctness of values
- bool Meets(Player const * APlayer) const; // Checks if the player meets the condition
- bool operator == (PlayerCondition const& lc) const
- {
- return (lc.condition == condition && lc.value1 == value1 && lc.value2 == value2);
- }
-};
-
-// NPC gossip text id
-typedef HM_NAMESPACE::hash_map<uint32, uint32> CacheNpcTextIdMap;
-
-
-typedef HM_NAMESPACE::hash_map<uint32, VendorItemData> CacheVendorItemMap;
-typedef HM_NAMESPACE::hash_map<uint32, TrainerSpellData> CacheTrainerSpellMap;
-
-enum SkillRangeType
-{
- SKILL_RANGE_LANGUAGE, // 300..300
- SKILL_RANGE_LEVEL, // 1..max skill for level
- SKILL_RANGE_MONO, // 1..1, grey monolite bar
- SKILL_RANGE_RANK, // 1..skill for known rank
- SKILL_RANGE_NONE, // 0..0 always
-};
-
-SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial);
-
-#define MAX_PLAYER_NAME 12 // max allowed by client name length
-#define MAX_INTERNAL_PLAYER_NAME 15 // max server internal player name length ( > MAX_PLAYER_NAME for support declined names )
-
-bool normalizePlayerName(std::string& name);
-
-struct MANGOS_DLL_SPEC LanguageDesc
-{
- Language lang_id;
- uint32 spell_id;
- uint32 skill_id;
-};
-
-extern LanguageDesc lang_description[LANGUAGES_COUNT];
-MANGOS_DLL_SPEC LanguageDesc const* GetLanguageDescByID(uint32 lang);
-
-class PlayerDumpReader;
-
-class ObjectMgr
-{
- friend class PlayerDumpReader;
-
- public:
- ObjectMgr();
- ~ObjectMgr();
-
- typedef HM_NAMESPACE::hash_map<uint32, Item*> ItemMap;
-
- typedef std::set< Group * > GroupSet;
- typedef std::set< Guild * > GuildSet;
- typedef std::set< ArenaTeam * > ArenaTeamSet;
-
- typedef HM_NAMESPACE::hash_map<uint32, Quest*> QuestMap;
-
- typedef HM_NAMESPACE::hash_map<uint32, AreaTrigger> AreaTriggerMap;
-
- typedef HM_NAMESPACE::hash_map<uint32, std::string> AreaTriggerScriptMap;
-
- typedef HM_NAMESPACE::hash_map<uint32, ReputationOnKillEntry> RepOnKillMap;
-
- typedef HM_NAMESPACE::hash_map<uint32, WeatherZoneChances> WeatherZoneMap;
-
- typedef HM_NAMESPACE::hash_map<uint32, PetCreateSpellEntry> PetCreateSpellMap;
-
- Player* GetPlayer(const char* name) const { return ObjectAccessor::Instance().FindPlayerByName(name);}
- Player* GetPlayer(uint64 guid) const { return ObjectAccessor::FindPlayer(guid); }
-
- static GameObjectInfo const *GetGameObjectInfo(uint32 id) { return sGOStorage.LookupEntry<GameObjectInfo>(id); }
-
- void LoadGameobjectInfo();
- void AddGameobjectInfo(GameObjectInfo *goinfo);
-
- Group * GetGroupByLeader(const uint64 &guid) const;
- void AddGroup(Group* group) { mGroupSet.insert( group ); }
- void RemoveGroup(Group* group) { mGroupSet.erase( group ); }
-
- Guild* GetGuildByLeader(uint64 const&guid) const;
- Guild* GetGuildById(const uint32 GuildId) const;
- Guild* GetGuildByName(std::string guildname) const;
- std::string GetGuildNameById(const uint32 GuildId) const;
- void AddGuild(Guild* guild) { mGuildSet.insert( guild ); }
- void RemoveGuild(Guild* guild) { mGuildSet.erase( guild ); }
-
- ArenaTeam* GetArenaTeamById(const uint32 ArenaTeamId) const;
- ArenaTeam* GetArenaTeamByName(std::string ArenaTeamName) const;
- ArenaTeam* GetArenaTeamByCapitan(uint64 const& guid) const;
- void AddArenaTeam(ArenaTeam* arenateam) { mArenaTeamSet.insert( arenateam ); }
- void RemoveArenaTeam(ArenaTeam* arenateam) { mArenaTeamSet.erase( arenateam ); }
- ArenaTeamSet::iterator GetArenaTeamSetBegin() { return mArenaTeamSet.begin(); }
- ArenaTeamSet::iterator GetArenaTeamSetEnd() { return mArenaTeamSet.end(); }
-
- static CreatureInfo const *GetCreatureTemplate( uint32 id );
- CreatureModelInfo const *GetCreatureModelInfo( uint32 modelid );
- CreatureModelInfo const* GetCreatureModelRandomGender(uint32 display_id);
- uint32 ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data = NULL);
- EquipmentInfo const *GetEquipmentInfo( uint32 entry );
- static CreatureDataAddon const *GetCreatureAddon( uint32 lowguid )
- {
- return sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(lowguid);
- }
-
- static CreatureDataAddon const *GetCreatureTemplateAddon( uint32 entry )
- {
- return sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(entry);
- }
-
- static ItemPrototype const* GetItemPrototype(uint32 id) { return sItemStorage.LookupEntry<ItemPrototype>(id); }
-
- static InstanceTemplate const* GetInstanceTemplate(uint32 map)
- {
- return sInstanceTemplate.LookupEntry<InstanceTemplate>(map);
- }
-
- Item* GetAItem(uint32 id)
- {
- ItemMap::const_iterator itr = mAitems.find(id);
- if (itr != mAitems.end())
- {
- return itr->second;
- }
- return NULL;
- }
- void AddAItem(Item* it)
- {
- ASSERT( it );
- ASSERT( mAitems.find(it->GetGUIDLow()) == mAitems.end());
- mAitems[it->GetGUIDLow()] = it;
- }
- bool RemoveAItem(uint32 id)
- {
- ItemMap::iterator i = mAitems.find(id);
- if (i == mAitems.end())
- {
- return false;
- }
- mAitems.erase(i);
- return true;
- }
- AuctionHouseObject * GetAuctionsMap( uint32 location );
-
- //auction messages
- void SendAuctionWonMail( AuctionEntry * auction );
- void SendAuctionSalePendingMail( AuctionEntry * auction );
- void SendAuctionSuccessfulMail( AuctionEntry * auction );
- void SendAuctionExpiredMail( AuctionEntry * auction );
- static uint32 GetAuctionCut( uint32 location, uint32 highBid );
- static uint32 GetAuctionDeposit(uint32 location, uint32 time, Item *pItem);
- static uint32 GetAuctionOutBid(uint32 currentBid);
-
- PetLevelInfo const* GetPetLevelInfo(uint32 creature_id, uint32 level) const;
-
- PlayerClassInfo const* GetPlayerClassInfo(uint32 class_) const
- {
- if(class_ >= MAX_CLASSES) return NULL;
- return &playerClassInfo[class_];
- }
- void GetPlayerClassLevelInfo(uint32 class_,uint32 level, PlayerClassLevelInfo* info) const;
-
- PlayerInfo const* GetPlayerInfo(uint32 race, uint32 class_) const
- {
- if(race >= MAX_RACES) return NULL;
- if(class_ >= MAX_CLASSES) return NULL;
- PlayerInfo const* info = &playerInfo[race][class_];
- if(info->displayId_m==0 || info->displayId_f==0) return NULL;
- return info;
- }
- void GetPlayerLevelInfo(uint32 race, uint32 class_,uint32 level, PlayerLevelInfo* info) const;
-
- uint64 GetPlayerGUIDByName(std::string name) const;
- bool GetPlayerNameByGUID(const uint64 &guid, std::string &name) const;
- uint32 GetPlayerTeamByGUID(const uint64 &guid) const;
- uint32 GetPlayerAccountIdByGUID(const uint64 &guid) const;
- uint32 GetSecurityByAccount(uint32 acc_id) const;
- bool GetAccountNameByAccount(uint32 acc_id, std::string &name) const;
- uint32 GetAccountByAccountName(std::string name) const;
-
- uint32 GetNearestTaxiNode( float x, float y, float z, uint32 mapid );
- void GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost);
- uint16 GetTaxiMount( uint32 id, uint32 team );
- void GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds );
- void GetTransportPathNodes( uint32 path, TransportPath &pathnodes );
-
- Quest const* GetQuestTemplate(uint32 quest_id) const
- {
- QuestMap::const_iterator itr = mQuestTemplates.find(quest_id);
- return itr != mQuestTemplates.end() ? itr->second : NULL;
- }
- QuestMap const& GetQuestTemplates() const { return mQuestTemplates; }
-
- uint32 GetQuestForAreaTrigger(uint32 Trigger_ID) const
- {
- QuestAreaTriggerMap::const_iterator itr = mQuestAreaTriggerMap.find(Trigger_ID);
- if(itr != mQuestAreaTriggerMap.end())
- return itr->second;
- return 0;
- }
- bool IsTavernAreaTrigger(uint32 Trigger_ID) const { return mTavernAreaTriggerSet.count(Trigger_ID) != 0; }
- bool IsGameObjectForQuests(uint32 entry) const { return mGameObjectForQuestSet.count(entry) != 0; }
- bool IsGuildVaultGameObject(Player *player, uint64 guid) const
- {
- if(GameObject *go = ObjectAccessor::GetGameObject(*player, guid))
- if(go->GetGoType() == GAMEOBJECT_TYPE_GUILD_BANK)
- return true;
- return false;
- }
-
- uint32 GetBattleMasterBG(uint32 entry) const
- {
- BattleMastersMap::const_iterator itr = mBattleMastersMap.find(entry);
- if(itr != mBattleMastersMap.end())
- return itr->second;
- return 2; //BATTLEGROUND_WS - i will not add include only for constant usage!
- }
-
- void AddGossipText(GossipText *pGText);
- GossipText *GetGossipText(uint32 Text_ID);
-
- WorldSafeLocsEntry const *GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team);
- bool AddGraveYardLink(uint32 id, uint32 zone, uint32 team, bool inDB = true);
- void LoadGraveyardZones();
- GraveYardData const* FindGraveYardData(uint32 id, uint32 zone);
-
- AreaTrigger const* GetAreaTrigger(uint32 trigger) const
- {
- AreaTriggerMap::const_iterator itr = mAreaTriggers.find( trigger );
- if( itr != mAreaTriggers.end( ) )
- return &itr->second;
- return NULL;
- }
-
- AreaTrigger const* GetGoBackTrigger(uint32 Map) const;
-
- const char* GetAreaTriggerScriptName(uint32 id);
-
- ReputationOnKillEntry const* GetReputationOnKilEntry(uint32 id) const
- {
- RepOnKillMap::const_iterator itr = mRepOnKill.find(id);
- if(itr != mRepOnKill.end())
- return &itr->second;
- return NULL;
- }
-
- PetCreateSpellEntry const* GetPetCreateSpellEntry(uint32 id) const
- {
- PetCreateSpellMap::const_iterator itr = mPetCreateSpell.find(id);
- if(itr != mPetCreateSpell.end())
- return &itr->second;
- return NULL;
- }
-
- void LoadGuilds();
- void LoadArenaTeams();
- void LoadGroups();
- void LoadQuests();
- void LoadQuestRelations()
- {
- LoadGameobjectQuestRelations();
- LoadGameobjectInvolvedRelations();
- LoadCreatureQuestRelations();
- LoadCreatureInvolvedRelations();
- }
- void LoadGameobjectQuestRelations();
- void LoadGameobjectInvolvedRelations();
- void LoadCreatureQuestRelations();
- void LoadCreatureInvolvedRelations();
-
- QuestRelations mGOQuestRelations;
- QuestRelations mGOQuestInvolvedRelations;
- QuestRelations mCreatureQuestRelations;
- QuestRelations mCreatureQuestInvolvedRelations;
-
- void LoadGameObjectScripts();
- void LoadQuestEndScripts();
- void LoadQuestStartScripts();
- void LoadEventScripts();
- void LoadSpellScripts();
-
- bool LoadMangosStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value);
- bool LoadMangosStrings() { return LoadMangosStrings(WorldDatabase,"mangos_string",1,std::numeric_limits<int32>::max()); }
- void LoadPetCreateSpells();
- void LoadCreatureLocales();
- void LoadCreatureTemplates();
- void LoadCreatures();
- void LoadCreatureRespawnTimes();
- void LoadCreatureAddons();
- void LoadCreatureModelInfo();
- void LoadEquipmentTemplates();
- void LoadGameObjectLocales();
- void LoadGameobjects();
- void LoadGameobjectRespawnTimes();
- void LoadItemPrototypes();
- void LoadItemLocales();
- void LoadQuestLocales();
- void LoadNpcTextLocales();
- void LoadPageTextLocales();
- void LoadInstanceTemplate();
-
- void LoadGossipText();
-
- void LoadAreaTriggerTeleports();
- void LoadQuestAreaTriggers();
- void LoadAreaTriggerScripts();
- void LoadTavernAreaTriggers();
- void LoadBattleMastersEntry();
- void LoadGameObjectForQuests();
-
- void LoadItemTexts();
- void LoadPageTexts();
-
- //load first auction items, because of check if item exists, when loading
- void LoadAuctionItems();
- void LoadAuctions();
- void LoadPlayerInfo();
- void LoadPetLevelInfo();
- void LoadExplorationBaseXP();
- void LoadPetNames();
- void LoadPetNumber();
- void LoadCorpses();
- void LoadFishingBaseSkillLevel();
-
- void LoadReputationOnKill();
-
- void LoadWeatherZoneChances();
- void LoadGameTele();
-
- void LoadNpcTextId();
- void LoadVendors();
- void LoadTrainerSpell();
-
- std::string GeneratePetName(uint32 entry);
- uint32 GetBaseXP(uint32 level);
-
- int32 GetFishingBaseSkillLevel(uint32 entry) const
- {
- FishingBaseSkillMap::const_iterator itr = mFishingBaseForArea.find(entry);
- return itr != mFishingBaseForArea.end() ? itr->second : 0;
- }
-
- void ReturnOrDeleteOldMails(bool serverUp);
-
- void SetHighestGuids();
- uint32 GenerateLowGuid(HighGuid guidhigh);
- uint32 GenerateAuctionID();
- uint32 GenerateMailID();
- uint32 GenerateItemTextID();
- uint32 GeneratePetNumber();
-
- uint32 CreateItemText(std::string text);
- std::string GetItemText( uint32 id )
- {
- ItemTextMap::const_iterator itr = mItemTexts.find( id );
- if ( itr != mItemTexts.end() )
- return itr->second;
- else
- return "There is no info for this item";
- }
-
- typedef std::multimap<int32, uint32> ExclusiveQuestGroups;
- ExclusiveQuestGroups mExclusiveQuestGroups;
-
- WeatherZoneChances const* GetWeatherChances(uint32 zone_id) const
- {
- WeatherZoneMap::const_iterator itr = mWeatherZoneMap.find(zone_id);
- if(itr != mWeatherZoneMap.end())
- return &itr->second;
- else
- return NULL;
- }
-
- CellObjectGuids const& GetCellObjectGuids(uint16 mapid, uint8 spawnMode, uint32 cell_id)
- {
- return mMapObjectGuids[MAKE_PAIR32(mapid,spawnMode)][cell_id];
- }
-
- CreatureData const* GetCreatureData(uint32 guid) const
- {
- CreatureDataMap::const_iterator itr = mCreatureDataMap.find(guid);
- if(itr==mCreatureDataMap.end()) return NULL;
- return &itr->second;
- }
- CreatureData& NewOrExistCreatureData(uint32 guid) { return mCreatureDataMap[guid]; }
- void DeleteCreatureData(uint32 guid);
- CreatureLocale const* GetCreatureLocale(uint32 entry) const
- {
- CreatureLocaleMap::const_iterator itr = mCreatureLocaleMap.find(entry);
- if(itr==mCreatureLocaleMap.end()) return NULL;
- return &itr->second;
- }
- GameObjectLocale const* GetGameObjectLocale(uint32 entry) const
- {
- GameObjectLocaleMap::const_iterator itr = mGameObjectLocaleMap.find(entry);
- if(itr==mGameObjectLocaleMap.end()) return NULL;
- return &itr->second;
- }
- ItemLocale const* GetItemLocale(uint32 entry) const
- {
- ItemLocaleMap::const_iterator itr = mItemLocaleMap.find(entry);
- if(itr==mItemLocaleMap.end()) return NULL;
- return &itr->second;
- }
- QuestLocale const* GetQuestLocale(uint32 entry) const
- {
- QuestLocaleMap::const_iterator itr = mQuestLocaleMap.find(entry);
- if(itr==mQuestLocaleMap.end()) return NULL;
- return &itr->second;
- }
- NpcTextLocale const* GetNpcTextLocale(uint32 entry) const
- {
- NpcTextLocaleMap::const_iterator itr = mNpcTextLocaleMap.find(entry);
- if(itr==mNpcTextLocaleMap.end()) return NULL;
- return &itr->second;
- }
- PageTextLocale const* GetPageTextLocale(uint32 entry) const
- {
- PageTextLocaleMap::const_iterator itr = mPageTextLocaleMap.find(entry);
- if(itr==mPageTextLocaleMap.end()) return NULL;
- return &itr->second;
- }
-
- GameObjectData const* GetGOData(uint32 guid) const
- {
- GameObjectDataMap::const_iterator itr = mGameObjectDataMap.find(guid);
- if(itr==mGameObjectDataMap.end()) return NULL;
- return &itr->second;
- }
- GameObjectData& NewGOData(uint32 guid) { return mGameObjectDataMap[guid]; }
- void DeleteGOData(uint32 guid);
-
- MangosStringLocale const* GetMangosStringLocale(int32 entry) const
- {
- MangosStringLocaleMap::const_iterator itr = mMangosStringLocaleMap.find(entry);
- if(itr==mMangosStringLocaleMap.end()) return NULL;
- return &itr->second;
- }
- const char *GetMangosString(int32 entry, int locale_idx) const;
- const char *GetMangosStringForDBCLocale(int32 entry) const { return GetMangosString(entry,DBCLocaleIndex); }
- void SetDBCLocaleIndex(uint32 lang) { DBCLocaleIndex = GetIndexForLocale(LocaleConstant(lang)); }
-
- void AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance);
- void DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid);
-
- time_t GetCreatureRespawnTime(uint32 loguid, uint32 instance) { return mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)]; }
- void SaveCreatureRespawnTime(uint32 loguid, uint32 instance, time_t t);
- time_t GetGORespawnTime(uint32 loguid, uint32 instance) { return mGORespawnTimes[MAKE_PAIR64(loguid,instance)]; }
- void SaveGORespawnTime(uint32 loguid, uint32 instance, time_t t);
- void DeleteRespawnTimeForInstance(uint32 instance);
-
- // grid objects
- void AddCreatureToGrid(uint32 guid, CreatureData const* data);
- void RemoveCreatureFromGrid(uint32 guid, CreatureData const* data);
- void AddGameobjectToGrid(uint32 guid, GameObjectData const* data);
- void RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data);
-
- // reserved names
- void LoadReservedPlayersNames();
- bool IsReservedName(std::string name) const
- {
- return m_ReservedNames.find(name) != m_ReservedNames.end();
- }
-
- // name with valid structure and symbols
- static bool IsValidName( std::string name, bool create = false );
- static bool IsValidCharterName( std::string name );
- static bool IsValidPetName( std::string name );
-
- static bool CheckDeclinedNames(std::wstring mainpart, DeclinedName const& names);
-
- int GetIndexForLocale(LocaleConstant loc);
- LocaleConstant GetLocaleForIndex(int i);
- // guild bank tabs
- const uint32 GetGuildBankTabPrice(uint8 Index) { return Index < GUILD_BANK_MAX_TABS ? mGuildBankTabPrice[Index] : 0; }
-
- uint16 GetConditionId(ConditionType condition, uint32 value1, uint32 value2);
- bool IsPlayerMeetToCondition(Player const* player, uint16 condition_id) const
- {
- if(condition_id >= mConditions.size())
- return false;
-
- return mConditions[condition_id].Meets(player);
- }
-
- GameTele const* GetGameTele(uint32 id) const
- {
- GameTeleMap::const_iterator itr = m_GameTeleMap.find(id);
- if(itr==m_GameTeleMap.end()) return NULL;
- return &itr->second;
- }
- GameTele const* GetGameTele(std::string name) const;
- GameTeleMap const& GetGameTeleMap() const { return m_GameTeleMap; }
- bool AddGameTele(GameTele& data);
- bool DeleteGameTele(std::string name);
-
- uint32 GetNpcGossip(uint32 entry) const
- {
- CacheNpcTextIdMap::const_iterator iter = m_mCacheNpcTextIdMap.find(entry);
- if(iter == m_mCacheNpcTextIdMap.end())
- return 0;
-
- return iter->second;
- }
-
- TrainerSpellData const* GetNpcTrainerSpells(uint32 entry) const
- {
- CacheTrainerSpellMap::const_iterator iter = m_mCacheTrainerSpellMap.find(entry);
- if(iter == m_mCacheTrainerSpellMap.end())
- return NULL;
-
- return &iter->second;
- }
-
- VendorItemData const* GetNpcVendorItemList(uint32 entry) const
- {
- CacheVendorItemMap::const_iterator iter = m_mCacheVendorItemMap.find(entry);
- if(iter == m_mCacheVendorItemMap.end())
- return NULL;
-
- return &iter->second;
- }
- 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, std::set<uint32>* skip_vendors = NULL ) const;
- protected:
- uint32 m_auctionid;
- uint32 m_mailid;
- uint32 m_ItemTextId;
-
- uint32 m_hiCharGuid;
- uint32 m_hiCreatureGuid;
- uint32 m_hiPetGuid;
- uint32 m_hiItemGuid;
- uint32 m_hiGoGuid;
- uint32 m_hiDoGuid;
- uint32 m_hiCorpseGuid;
-
- uint32 m_hiPetNumber;
-
- QuestMap mQuestTemplates;
-
- typedef HM_NAMESPACE::hash_map<uint32, GossipText*> GossipTextMap;
- typedef HM_NAMESPACE::hash_map<uint32, uint32> QuestAreaTriggerMap;
- typedef HM_NAMESPACE::hash_map<uint32, uint32> BattleMastersMap;
- typedef HM_NAMESPACE::hash_map<uint32, std::string> ItemTextMap;
- typedef std::set<uint32> TavernAreaTriggerSet;
- typedef std::set<uint32> GameObjectForQuestSet;
-
- GroupSet mGroupSet;
- GuildSet mGuildSet;
- ArenaTeamSet mArenaTeamSet;
-
- ItemMap mItems;
- ItemMap mAitems;
-
- ItemTextMap mItemTexts;
-
- AuctionHouseObject mHordeAuctions;
- AuctionHouseObject mAllianceAuctions;
- AuctionHouseObject mNeutralAuctions;
-
- QuestAreaTriggerMap mQuestAreaTriggerMap;
- BattleMastersMap mBattleMastersMap;
- TavernAreaTriggerSet mTavernAreaTriggerSet;
- GameObjectForQuestSet mGameObjectForQuestSet;
- GossipTextMap mGossipText;
- AreaTriggerMap mAreaTriggers;
- AreaTriggerScriptMap mAreaTriggerScripts;
-
- RepOnKillMap mRepOnKill;
-
- WeatherZoneMap mWeatherZoneMap;
-
- PetCreateSpellMap mPetCreateSpell;
-
- //character reserved names
- typedef std::set<std::string> ReservedNamesMap;
- ReservedNamesMap m_ReservedNames;
-
- GraveYardMap mGraveYardMap;
-
- GameTeleMap m_GameTeleMap;
-
- typedef std::vector<LocaleConstant> LocalForIndex;
- LocalForIndex m_LocalForIndex;
- int GetOrNewIndexForLocale(LocaleConstant loc);
-
- int DBCLocaleIndex;
- private:
- void LoadScripts(ScriptMapMap& scripts, char const* tablename);
- void ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr);
- void LoadQuestRelationsHelper(QuestRelations& map,char const* table);
-
- typedef std::map<uint32,PetLevelInfo*> PetLevelInfoMap;
- // PetLevelInfoMap[creature_id][level]
- PetLevelInfoMap petInfo; // [creature_id][level]
-
- PlayerClassInfo playerClassInfo[MAX_CLASSES];
-
- void BuildPlayerLevelInfo(uint8 race, uint8 class_, uint8 level, PlayerLevelInfo* plinfo) const;
- PlayerInfo playerInfo[MAX_RACES][MAX_CLASSES];
-
- typedef std::map<uint32,uint32> BaseXPMap; // [area level][base xp]
- BaseXPMap mBaseXPTable;
-
- typedef std::map<uint32,int32> FishingBaseSkillMap; // [areaId][base skill level]
- FishingBaseSkillMap mFishingBaseForArea;
-
- typedef std::map<uint32,std::vector<std::string> > HalfNameMap;
- HalfNameMap PetHalfName0;
- HalfNameMap PetHalfName1;
-
- MapObjectGuids mMapObjectGuids;
- CreatureDataMap mCreatureDataMap;
- CreatureLocaleMap mCreatureLocaleMap;
- GameObjectDataMap mGameObjectDataMap;
- GameObjectLocaleMap mGameObjectLocaleMap;
- ItemLocaleMap mItemLocaleMap;
- QuestLocaleMap mQuestLocaleMap;
- NpcTextLocaleMap mNpcTextLocaleMap;
- PageTextLocaleMap mPageTextLocaleMap;
- MangosStringLocaleMap mMangosStringLocaleMap;
- RespawnTimes mCreatureRespawnTimes;
- RespawnTimes mGORespawnTimes;
-
- typedef std::vector<uint32> GuildBankTabPriceMap;
- GuildBankTabPriceMap mGuildBankTabPrice;
-
- // Storage for Conditions. First element (index 0) is reserved for zero-condition (nothing required)
- typedef std::vector<PlayerCondition> ConditionStore;
- ConditionStore mConditions;
-
- CacheNpcTextIdMap m_mCacheNpcTextIdMap;
- CacheVendorItemMap m_mCacheVendorItemMap;
- CacheTrainerSpellMap m_mCacheTrainerSpellMap;
-};
-
-#define objmgr MaNGOS::Singleton<ObjectMgr>::Instance()
-
-// scripting access functions
-bool MANGOS_DLL_SPEC LoadMangosStrings(DatabaseType& db, char const* table,int32 start_value = -1, int32 end_value = std::numeric_limits<int32>::min());
-MANGOS_DLL_SPEC const char* GetAreaTriggerScriptNameById(uint32 id);
-
-#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
+ */
+
+#ifndef _OBJECTMGR_H
+#define _OBJECTMGR_H
+
+#include "Log.h"
+#include "Object.h"
+#include "Bag.h"
+#include "Creature.h"
+#include "Player.h"
+#include "DynamicObject.h"
+#include "GameObject.h"
+#include "Corpse.h"
+#include "QuestDef.h"
+#include "Path.h"
+#include "ItemPrototype.h"
+#include "NPCHandler.h"
+#include "Database/DatabaseEnv.h"
+#include "AuctionHouseObject.h"
+#include "Mail.h"
+#include "Map.h"
+#include "ObjectAccessor.h"
+#include "ObjectDefines.h"
+#include "Policies/Singleton.h"
+#include "Database/SQLStorage.h"
+
+#include <string>
+#include <map>
+#include <limits>
+
+extern SQLStorage sCreatureStorage;
+extern SQLStorage sCreatureDataAddonStorage;
+extern SQLStorage sCreatureInfoAddonStorage;
+extern SQLStorage sCreatureModelStorage;
+extern SQLStorage sEquipmentStorage;
+extern SQLStorage sGOStorage;
+extern SQLStorage sPageTextStore;
+extern SQLStorage sItemStorage;
+extern SQLStorage sInstanceTemplate;
+
+class Group;
+class Guild;
+class ArenaTeam;
+class Path;
+class TransportPath;
+class Item;
+
+struct GameTele
+{
+ float position_x;
+ float position_y;
+ float position_z;
+ float orientation;
+ uint32 mapId;
+ std::string name;
+ std::wstring wnameLow;
+};
+
+typedef HM_NAMESPACE::hash_map<uint32, GameTele > GameTeleMap;
+
+struct ScriptInfo
+{
+ uint32 id;
+ uint32 delay;
+ uint32 command;
+ uint32 datalong;
+ uint32 datalong2;
+ std::string datatext;
+ float x;
+ float y;
+ float z;
+ float o;
+};
+
+typedef std::multimap<uint32, ScriptInfo> ScriptMap;
+typedef std::map<uint32, ScriptMap > ScriptMapMap;
+extern ScriptMapMap sQuestEndScripts;
+extern ScriptMapMap sQuestStartScripts;
+extern ScriptMapMap sSpellScripts;
+extern ScriptMapMap sGameObjectScripts;
+extern ScriptMapMap sEventScripts;
+
+struct AreaTrigger
+{
+ uint8 requiredLevel;
+ uint32 requiredItem;
+ uint32 requiredItem2;
+ uint32 heroicKey;
+ uint32 heroicKey2;
+ uint32 requiredQuest;
+ std::string requiredFailedText;
+ uint32 target_mapId;
+ float target_X;
+ float target_Y;
+ float target_Z;
+ float target_Orientation;
+};
+
+typedef std::set<uint32> CellGuidSet;
+typedef std::map<uint32/*player guid*/,uint32/*instance*/> CellCorpseSet;
+struct CellObjectGuids
+{
+ CellGuidSet creatures;
+ CellGuidSet gameobjects;
+ CellCorpseSet corpses;
+};
+typedef HM_NAMESPACE::hash_map<uint32/*cell_id*/,CellObjectGuids> CellObjectGuidsMap;
+typedef HM_NAMESPACE::hash_map<uint32/*(mapid,spawnMode) pair*/,CellObjectGuidsMap> MapObjectGuids;
+
+typedef HM_NAMESPACE::hash_map<uint64/*(instance,guid) pair*/,time_t> RespawnTimes;
+
+struct MangosStringLocale
+{
+ std::vector<std::string> Content; // 0 -> default, i -> i-1 locale index
+};
+
+typedef HM_NAMESPACE::hash_map<uint32,CreatureData> CreatureDataMap;
+typedef HM_NAMESPACE::hash_map<uint32,GameObjectData> GameObjectDataMap;
+typedef HM_NAMESPACE::hash_map<uint32,CreatureLocale> CreatureLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,GameObjectLocale> GameObjectLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,ItemLocale> ItemLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,QuestLocale> QuestLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,NpcTextLocale> NpcTextLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,PageTextLocale> PageTextLocaleMap;
+typedef HM_NAMESPACE::hash_map<uint32,MangosStringLocale> MangosStringLocaleMap;
+
+typedef std::multimap<uint32,uint32> QuestRelations;
+
+struct PetLevelInfo
+{
+ PetLevelInfo() : health(0), mana(0) { for(int i=0; i < MAX_STATS; ++i ) stats[i] = 0; }
+
+ uint16 stats[MAX_STATS];
+ uint16 health;
+ uint16 mana;
+ uint16 armor;
+};
+
+struct ReputationOnKillEntry
+{
+ uint32 repfaction1;
+ uint32 repfaction2;
+ bool is_teamaward1;
+ uint32 reputation_max_cap1;
+ int32 repvalue1;
+ bool is_teamaward2;
+ uint32 reputation_max_cap2;
+ int32 repvalue2;
+ bool team_dependent;
+};
+
+struct PetCreateSpellEntry
+{
+ uint32 spellid[4];
+};
+
+#define WEATHER_SEASONS 4
+struct WeatherSeasonChances
+{
+ uint32 rainChance;
+ uint32 snowChance;
+ uint32 stormChance;
+};
+
+struct WeatherZoneChances
+{
+ WeatherSeasonChances data[WEATHER_SEASONS];
+};
+
+struct GraveYardData
+{
+ uint32 safeLocId;
+ uint32 team;
+};
+typedef std::multimap<uint32,GraveYardData> GraveYardMap;
+
+enum ConditionType
+{ // value1 value2 for the Condition enumed
+ CONDITION_NONE = 0, // 0 0
+ CONDITION_AURA = 1, // spell_id effindex
+ CONDITION_ITEM = 2, // item_id count
+ CONDITION_ITEM_EQUIPPED = 3, // item_id 0
+ CONDITION_ZONEID = 4, // zone_id 0
+ CONDITION_REPUTATION_RANK = 5, // faction_id min_rank
+ CONDITION_TEAM = 6, // player_team 0, (469 - Alliance 67 - Horde)
+ CONDITION_SKILL = 7, // skill_id skill_value
+ CONDITION_QUESTREWARDED = 8, // quest_id 0
+ CONDITION_QUESTTAKEN = 9, // quest_id 0, for condition true while quest active.
+ CONDITION_AD_COMMISSION_AURA = 10, // 0 0, for condition true while one from AD ñommission aura active
+};
+
+#define MAX_CONDITION 11 // maximum value in ConditionType enum
+
+struct PlayerCondition
+{
+ ConditionType condition; // additional condition type
+ uint32 value1; // data for the condition - see ConditionType definition
+ uint32 value2;
+
+ PlayerCondition(uint8 _condition = 0, uint32 _value1 = 0, uint32 _value2 = 0)
+ : condition(ConditionType(_condition)), value1(_value1), value2(_value2) {}
+
+ static bool IsValid(ConditionType condition, uint32 value1, uint32 value2);
+ // Checks correctness of values
+ bool Meets(Player const * APlayer) const; // Checks if the player meets the condition
+ bool operator == (PlayerCondition const& lc) const
+ {
+ return (lc.condition == condition && lc.value1 == value1 && lc.value2 == value2);
+ }
+};
+
+// NPC gossip text id
+typedef HM_NAMESPACE::hash_map<uint32, uint32> CacheNpcTextIdMap;
+
+
+typedef HM_NAMESPACE::hash_map<uint32, VendorItemData> CacheVendorItemMap;
+typedef HM_NAMESPACE::hash_map<uint32, TrainerSpellData> CacheTrainerSpellMap;
+
+enum SkillRangeType
+{
+ SKILL_RANGE_LANGUAGE, // 300..300
+ SKILL_RANGE_LEVEL, // 1..max skill for level
+ SKILL_RANGE_MONO, // 1..1, grey monolite bar
+ SKILL_RANGE_RANK, // 1..skill for known rank
+ SKILL_RANGE_NONE, // 0..0 always
+};
+
+SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial);
+
+#define MAX_PLAYER_NAME 12 // max allowed by client name length
+#define MAX_INTERNAL_PLAYER_NAME 15 // max server internal player name length ( > MAX_PLAYER_NAME for support declined names )
+
+bool normalizePlayerName(std::string& name);
+
+struct MANGOS_DLL_SPEC LanguageDesc
+{
+ Language lang_id;
+ uint32 spell_id;
+ uint32 skill_id;
+};
+
+extern LanguageDesc lang_description[LANGUAGES_COUNT];
+MANGOS_DLL_SPEC LanguageDesc const* GetLanguageDescByID(uint32 lang);
+
+class PlayerDumpReader;
+
+class ObjectMgr
+{
+ friend class PlayerDumpReader;
+
+ public:
+ ObjectMgr();
+ ~ObjectMgr();
+
+ typedef HM_NAMESPACE::hash_map<uint32, Item*> ItemMap;
+
+ typedef std::set< Group * > GroupSet;
+ typedef std::set< Guild * > GuildSet;
+ typedef std::set< ArenaTeam * > ArenaTeamSet;
+
+ typedef HM_NAMESPACE::hash_map<uint32, Quest*> QuestMap;
+
+ typedef HM_NAMESPACE::hash_map<uint32, AreaTrigger> AreaTriggerMap;
+
+ typedef HM_NAMESPACE::hash_map<uint32, std::string> AreaTriggerScriptMap;
+
+ typedef HM_NAMESPACE::hash_map<uint32, ReputationOnKillEntry> RepOnKillMap;
+
+ typedef HM_NAMESPACE::hash_map<uint32, WeatherZoneChances> WeatherZoneMap;
+
+ typedef HM_NAMESPACE::hash_map<uint32, PetCreateSpellEntry> PetCreateSpellMap;
+
+ Player* GetPlayer(const char* name) const { return ObjectAccessor::Instance().FindPlayerByName(name);}
+ Player* GetPlayer(uint64 guid) const { return ObjectAccessor::FindPlayer(guid); }
+
+ static GameObjectInfo const *GetGameObjectInfo(uint32 id) { return sGOStorage.LookupEntry<GameObjectInfo>(id); }
+
+ void LoadGameobjectInfo();
+ void AddGameobjectInfo(GameObjectInfo *goinfo);
+
+ Group * GetGroupByLeader(const uint64 &guid) const;
+ void AddGroup(Group* group) { mGroupSet.insert( group ); }
+ void RemoveGroup(Group* group) { mGroupSet.erase( group ); }
+
+ Guild* GetGuildByLeader(uint64 const&guid) const;
+ Guild* GetGuildById(const uint32 GuildId) const;
+ Guild* GetGuildByName(std::string guildname) const;
+ std::string GetGuildNameById(const uint32 GuildId) const;
+ void AddGuild(Guild* guild) { mGuildSet.insert( guild ); }
+ void RemoveGuild(Guild* guild) { mGuildSet.erase( guild ); }
+
+ ArenaTeam* GetArenaTeamById(const uint32 ArenaTeamId) const;
+ ArenaTeam* GetArenaTeamByName(std::string ArenaTeamName) const;
+ ArenaTeam* GetArenaTeamByCapitan(uint64 const& guid) const;
+ void AddArenaTeam(ArenaTeam* arenateam) { mArenaTeamSet.insert( arenateam ); }
+ void RemoveArenaTeam(ArenaTeam* arenateam) { mArenaTeamSet.erase( arenateam ); }
+ ArenaTeamSet::iterator GetArenaTeamSetBegin() { return mArenaTeamSet.begin(); }
+ ArenaTeamSet::iterator GetArenaTeamSetEnd() { return mArenaTeamSet.end(); }
+
+ static CreatureInfo const *GetCreatureTemplate( uint32 id );
+ CreatureModelInfo const *GetCreatureModelInfo( uint32 modelid );
+ CreatureModelInfo const* GetCreatureModelRandomGender(uint32 display_id);
+ uint32 ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data = NULL);
+ EquipmentInfo const *GetEquipmentInfo( uint32 entry );
+ static CreatureDataAddon const *GetCreatureAddon( uint32 lowguid )
+ {
+ return sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(lowguid);
+ }
+
+ static CreatureDataAddon const *GetCreatureTemplateAddon( uint32 entry )
+ {
+ return sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(entry);
+ }
+
+ static ItemPrototype const* GetItemPrototype(uint32 id) { return sItemStorage.LookupEntry<ItemPrototype>(id); }
+
+ static InstanceTemplate const* GetInstanceTemplate(uint32 map)
+ {
+ return sInstanceTemplate.LookupEntry<InstanceTemplate>(map);
+ }
+
+ Item* GetAItem(uint32 id)
+ {
+ ItemMap::const_iterator itr = mAitems.find(id);
+ if (itr != mAitems.end())
+ {
+ return itr->second;
+ }
+ return NULL;
+ }
+ void AddAItem(Item* it)
+ {
+ ASSERT( it );
+ ASSERT( mAitems.find(it->GetGUIDLow()) == mAitems.end());
+ mAitems[it->GetGUIDLow()] = it;
+ }
+ bool RemoveAItem(uint32 id)
+ {
+ ItemMap::iterator i = mAitems.find(id);
+ if (i == mAitems.end())
+ {
+ return false;
+ }
+ mAitems.erase(i);
+ return true;
+ }
+ AuctionHouseObject * GetAuctionsMap( uint32 location );
+
+ //auction messages
+ void SendAuctionWonMail( AuctionEntry * auction );
+ void SendAuctionSalePendingMail( AuctionEntry * auction );
+ void SendAuctionSuccessfulMail( AuctionEntry * auction );
+ void SendAuctionExpiredMail( AuctionEntry * auction );
+ static uint32 GetAuctionCut( uint32 location, uint32 highBid );
+ static uint32 GetAuctionDeposit(uint32 location, uint32 time, Item *pItem);
+ static uint32 GetAuctionOutBid(uint32 currentBid);
+
+ PetLevelInfo const* GetPetLevelInfo(uint32 creature_id, uint32 level) const;
+
+ PlayerClassInfo const* GetPlayerClassInfo(uint32 class_) const
+ {
+ if(class_ >= MAX_CLASSES) return NULL;
+ return &playerClassInfo[class_];
+ }
+ void GetPlayerClassLevelInfo(uint32 class_,uint32 level, PlayerClassLevelInfo* info) const;
+
+ PlayerInfo const* GetPlayerInfo(uint32 race, uint32 class_) const
+ {
+ if(race >= MAX_RACES) return NULL;
+ if(class_ >= MAX_CLASSES) return NULL;
+ PlayerInfo const* info = &playerInfo[race][class_];
+ if(info->displayId_m==0 || info->displayId_f==0) return NULL;
+ return info;
+ }
+ void GetPlayerLevelInfo(uint32 race, uint32 class_,uint32 level, PlayerLevelInfo* info) const;
+
+ uint64 GetPlayerGUIDByName(std::string name) const;
+ bool GetPlayerNameByGUID(const uint64 &guid, std::string &name) const;
+ uint32 GetPlayerTeamByGUID(const uint64 &guid) const;
+ uint32 GetPlayerAccountIdByGUID(const uint64 &guid) const;
+ uint32 GetSecurityByAccount(uint32 acc_id) const;
+ bool GetAccountNameByAccount(uint32 acc_id, std::string &name) const;
+ uint32 GetAccountByAccountName(std::string name) const;
+
+ uint32 GetNearestTaxiNode( float x, float y, float z, uint32 mapid );
+ void GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost);
+ uint16 GetTaxiMount( uint32 id, uint32 team );
+ void GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds );
+ void GetTransportPathNodes( uint32 path, TransportPath &pathnodes );
+
+ Quest const* GetQuestTemplate(uint32 quest_id) const
+ {
+ QuestMap::const_iterator itr = mQuestTemplates.find(quest_id);
+ return itr != mQuestTemplates.end() ? itr->second : NULL;
+ }
+ QuestMap const& GetQuestTemplates() const { return mQuestTemplates; }
+
+ uint32 GetQuestForAreaTrigger(uint32 Trigger_ID) const
+ {
+ QuestAreaTriggerMap::const_iterator itr = mQuestAreaTriggerMap.find(Trigger_ID);
+ if(itr != mQuestAreaTriggerMap.end())
+ return itr->second;
+ return 0;
+ }
+ bool IsTavernAreaTrigger(uint32 Trigger_ID) const { return mTavernAreaTriggerSet.count(Trigger_ID) != 0; }
+ bool IsGameObjectForQuests(uint32 entry) const { return mGameObjectForQuestSet.count(entry) != 0; }
+ bool IsGuildVaultGameObject(Player *player, uint64 guid) const
+ {
+ if(GameObject *go = ObjectAccessor::GetGameObject(*player, guid))
+ if(go->GetGoType() == GAMEOBJECT_TYPE_GUILD_BANK)
+ return true;
+ return false;
+ }
+
+ uint32 GetBattleMasterBG(uint32 entry) const
+ {
+ BattleMastersMap::const_iterator itr = mBattleMastersMap.find(entry);
+ if(itr != mBattleMastersMap.end())
+ return itr->second;
+ return 2; //BATTLEGROUND_WS - i will not add include only for constant usage!
+ }
+
+ void AddGossipText(GossipText *pGText);
+ GossipText *GetGossipText(uint32 Text_ID);
+
+ WorldSafeLocsEntry const *GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team);
+ bool AddGraveYardLink(uint32 id, uint32 zone, uint32 team, bool inDB = true);
+ void LoadGraveyardZones();
+ GraveYardData const* FindGraveYardData(uint32 id, uint32 zone);
+
+ AreaTrigger const* GetAreaTrigger(uint32 trigger) const
+ {
+ AreaTriggerMap::const_iterator itr = mAreaTriggers.find( trigger );
+ if( itr != mAreaTriggers.end( ) )
+ return &itr->second;
+ return NULL;
+ }
+
+ AreaTrigger const* GetGoBackTrigger(uint32 Map) const;
+
+ const char* GetAreaTriggerScriptName(uint32 id);
+
+ ReputationOnKillEntry const* GetReputationOnKilEntry(uint32 id) const
+ {
+ RepOnKillMap::const_iterator itr = mRepOnKill.find(id);
+ if(itr != mRepOnKill.end())
+ return &itr->second;
+ return NULL;
+ }
+
+ PetCreateSpellEntry const* GetPetCreateSpellEntry(uint32 id) const
+ {
+ PetCreateSpellMap::const_iterator itr = mPetCreateSpell.find(id);
+ if(itr != mPetCreateSpell.end())
+ return &itr->second;
+ return NULL;
+ }
+
+ void LoadGuilds();
+ void LoadArenaTeams();
+ void LoadGroups();
+ void LoadQuests();
+ void LoadQuestRelations()
+ {
+ LoadGameobjectQuestRelations();
+ LoadGameobjectInvolvedRelations();
+ LoadCreatureQuestRelations();
+ LoadCreatureInvolvedRelations();
+ }
+ void LoadGameobjectQuestRelations();
+ void LoadGameobjectInvolvedRelations();
+ void LoadCreatureQuestRelations();
+ void LoadCreatureInvolvedRelations();
+
+ QuestRelations mGOQuestRelations;
+ QuestRelations mGOQuestInvolvedRelations;
+ QuestRelations mCreatureQuestRelations;
+ QuestRelations mCreatureQuestInvolvedRelations;
+
+ void LoadGameObjectScripts();
+ void LoadQuestEndScripts();
+ void LoadQuestStartScripts();
+ void LoadEventScripts();
+ void LoadSpellScripts();
+
+ bool LoadMangosStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value);
+ bool LoadMangosStrings() { return LoadMangosStrings(WorldDatabase,"mangos_string",1,std::numeric_limits<int32>::max()); }
+ void LoadPetCreateSpells();
+ void LoadCreatureLocales();
+ void LoadCreatureTemplates();
+ void LoadCreatures();
+ void LoadCreatureRespawnTimes();
+ void LoadCreatureAddons();
+ void LoadCreatureModelInfo();
+ void LoadEquipmentTemplates();
+ void LoadGameObjectLocales();
+ void LoadGameobjects();
+ void LoadGameobjectRespawnTimes();
+ void LoadItemPrototypes();
+ void LoadItemLocales();
+ void LoadQuestLocales();
+ void LoadNpcTextLocales();
+ void LoadPageTextLocales();
+ void LoadInstanceTemplate();
+
+ void LoadGossipText();
+
+ void LoadAreaTriggerTeleports();
+ void LoadQuestAreaTriggers();
+ void LoadAreaTriggerScripts();
+ void LoadTavernAreaTriggers();
+ void LoadBattleMastersEntry();
+ void LoadGameObjectForQuests();
+
+ void LoadItemTexts();
+ void LoadPageTexts();
+
+ //load first auction items, because of check if item exists, when loading
+ void LoadAuctionItems();
+ void LoadAuctions();
+ void LoadPlayerInfo();
+ void LoadPetLevelInfo();
+ void LoadExplorationBaseXP();
+ void LoadPetNames();
+ void LoadPetNumber();
+ void LoadCorpses();
+ void LoadFishingBaseSkillLevel();
+
+ void LoadReputationOnKill();
+
+ void LoadWeatherZoneChances();
+ void LoadGameTele();
+
+ void LoadNpcTextId();
+ void LoadVendors();
+ void LoadTrainerSpell();
+
+ std::string GeneratePetName(uint32 entry);
+ uint32 GetBaseXP(uint32 level);
+
+ int32 GetFishingBaseSkillLevel(uint32 entry) const
+ {
+ FishingBaseSkillMap::const_iterator itr = mFishingBaseForArea.find(entry);
+ return itr != mFishingBaseForArea.end() ? itr->second : 0;
+ }
+
+ void ReturnOrDeleteOldMails(bool serverUp);
+
+ void SetHighestGuids();
+ uint32 GenerateLowGuid(HighGuid guidhigh);
+ uint32 GenerateAuctionID();
+ uint32 GenerateMailID();
+ uint32 GenerateItemTextID();
+ uint32 GeneratePetNumber();
+
+ uint32 CreateItemText(std::string text);
+ std::string GetItemText( uint32 id )
+ {
+ ItemTextMap::const_iterator itr = mItemTexts.find( id );
+ if ( itr != mItemTexts.end() )
+ return itr->second;
+ else
+ return "There is no info for this item";
+ }
+
+ typedef std::multimap<int32, uint32> ExclusiveQuestGroups;
+ ExclusiveQuestGroups mExclusiveQuestGroups;
+
+ WeatherZoneChances const* GetWeatherChances(uint32 zone_id) const
+ {
+ WeatherZoneMap::const_iterator itr = mWeatherZoneMap.find(zone_id);
+ if(itr != mWeatherZoneMap.end())
+ return &itr->second;
+ else
+ return NULL;
+ }
+
+ CellObjectGuids const& GetCellObjectGuids(uint16 mapid, uint8 spawnMode, uint32 cell_id)
+ {
+ return mMapObjectGuids[MAKE_PAIR32(mapid,spawnMode)][cell_id];
+ }
+
+ CreatureData const* GetCreatureData(uint32 guid) const
+ {
+ CreatureDataMap::const_iterator itr = mCreatureDataMap.find(guid);
+ if(itr==mCreatureDataMap.end()) return NULL;
+ return &itr->second;
+ }
+ CreatureData& NewOrExistCreatureData(uint32 guid) { return mCreatureDataMap[guid]; }
+ void DeleteCreatureData(uint32 guid);
+ CreatureLocale const* GetCreatureLocale(uint32 entry) const
+ {
+ CreatureLocaleMap::const_iterator itr = mCreatureLocaleMap.find(entry);
+ if(itr==mCreatureLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ GameObjectLocale const* GetGameObjectLocale(uint32 entry) const
+ {
+ GameObjectLocaleMap::const_iterator itr = mGameObjectLocaleMap.find(entry);
+ if(itr==mGameObjectLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ ItemLocale const* GetItemLocale(uint32 entry) const
+ {
+ ItemLocaleMap::const_iterator itr = mItemLocaleMap.find(entry);
+ if(itr==mItemLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ QuestLocale const* GetQuestLocale(uint32 entry) const
+ {
+ QuestLocaleMap::const_iterator itr = mQuestLocaleMap.find(entry);
+ if(itr==mQuestLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ NpcTextLocale const* GetNpcTextLocale(uint32 entry) const
+ {
+ NpcTextLocaleMap::const_iterator itr = mNpcTextLocaleMap.find(entry);
+ if(itr==mNpcTextLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ PageTextLocale const* GetPageTextLocale(uint32 entry) const
+ {
+ PageTextLocaleMap::const_iterator itr = mPageTextLocaleMap.find(entry);
+ if(itr==mPageTextLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+
+ GameObjectData const* GetGOData(uint32 guid) const
+ {
+ GameObjectDataMap::const_iterator itr = mGameObjectDataMap.find(guid);
+ if(itr==mGameObjectDataMap.end()) return NULL;
+ return &itr->second;
+ }
+ GameObjectData& NewGOData(uint32 guid) { return mGameObjectDataMap[guid]; }
+ void DeleteGOData(uint32 guid);
+
+ MangosStringLocale const* GetMangosStringLocale(int32 entry) const
+ {
+ MangosStringLocaleMap::const_iterator itr = mMangosStringLocaleMap.find(entry);
+ if(itr==mMangosStringLocaleMap.end()) return NULL;
+ return &itr->second;
+ }
+ const char *GetMangosString(int32 entry, int locale_idx) const;
+ const char *GetMangosStringForDBCLocale(int32 entry) const { return GetMangosString(entry,DBCLocaleIndex); }
+ void SetDBCLocaleIndex(uint32 lang) { DBCLocaleIndex = GetIndexForLocale(LocaleConstant(lang)); }
+
+ void AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance);
+ void DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid);
+
+ time_t GetCreatureRespawnTime(uint32 loguid, uint32 instance) { return mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)]; }
+ void SaveCreatureRespawnTime(uint32 loguid, uint32 instance, time_t t);
+ time_t GetGORespawnTime(uint32 loguid, uint32 instance) { return mGORespawnTimes[MAKE_PAIR64(loguid,instance)]; }
+ void SaveGORespawnTime(uint32 loguid, uint32 instance, time_t t);
+ void DeleteRespawnTimeForInstance(uint32 instance);
+
+ // grid objects
+ void AddCreatureToGrid(uint32 guid, CreatureData const* data);
+ void RemoveCreatureFromGrid(uint32 guid, CreatureData const* data);
+ void AddGameobjectToGrid(uint32 guid, GameObjectData const* data);
+ void RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data);
+
+ // reserved names
+ void LoadReservedPlayersNames();
+ bool IsReservedName(std::string name) const
+ {
+ return m_ReservedNames.find(name) != m_ReservedNames.end();
+ }
+
+ // name with valid structure and symbols
+ static bool IsValidName( std::string name, bool create = false );
+ static bool IsValidCharterName( std::string name );
+ static bool IsValidPetName( std::string name );
+
+ static bool CheckDeclinedNames(std::wstring mainpart, DeclinedName const& names);
+
+ int GetIndexForLocale(LocaleConstant loc);
+ LocaleConstant GetLocaleForIndex(int i);
+ // guild bank tabs
+ const uint32 GetGuildBankTabPrice(uint8 Index) { return Index < GUILD_BANK_MAX_TABS ? mGuildBankTabPrice[Index] : 0; }
+
+ uint16 GetConditionId(ConditionType condition, uint32 value1, uint32 value2);
+ bool IsPlayerMeetToCondition(Player const* player, uint16 condition_id) const
+ {
+ if(condition_id >= mConditions.size())
+ return false;
+
+ return mConditions[condition_id].Meets(player);
+ }
+
+ GameTele const* GetGameTele(uint32 id) const
+ {
+ GameTeleMap::const_iterator itr = m_GameTeleMap.find(id);
+ if(itr==m_GameTeleMap.end()) return NULL;
+ return &itr->second;
+ }
+ GameTele const* GetGameTele(std::string name) const;
+ GameTeleMap const& GetGameTeleMap() const { return m_GameTeleMap; }
+ bool AddGameTele(GameTele& data);
+ bool DeleteGameTele(std::string name);
+
+ uint32 GetNpcGossip(uint32 entry) const
+ {
+ CacheNpcTextIdMap::const_iterator iter = m_mCacheNpcTextIdMap.find(entry);
+ if(iter == m_mCacheNpcTextIdMap.end())
+ return 0;
+
+ return iter->second;
+ }
+
+ TrainerSpellData const* GetNpcTrainerSpells(uint32 entry) const
+ {
+ CacheTrainerSpellMap::const_iterator iter = m_mCacheTrainerSpellMap.find(entry);
+ if(iter == m_mCacheTrainerSpellMap.end())
+ return NULL;
+
+ return &iter->second;
+ }
+
+ VendorItemData const* GetNpcVendorItemList(uint32 entry) const
+ {
+ CacheVendorItemMap::const_iterator iter = m_mCacheVendorItemMap.find(entry);
+ if(iter == m_mCacheVendorItemMap.end())
+ return NULL;
+
+ return &iter->second;
+ }
+ 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, std::set<uint32>* skip_vendors = NULL ) const;
+ protected:
+ uint32 m_auctionid;
+ uint32 m_mailid;
+ uint32 m_ItemTextId;
+
+ uint32 m_hiCharGuid;
+ uint32 m_hiCreatureGuid;
+ uint32 m_hiPetGuid;
+ uint32 m_hiItemGuid;
+ uint32 m_hiGoGuid;
+ uint32 m_hiDoGuid;
+ uint32 m_hiCorpseGuid;
+
+ uint32 m_hiPetNumber;
+
+ QuestMap mQuestTemplates;
+
+ typedef HM_NAMESPACE::hash_map<uint32, GossipText*> GossipTextMap;
+ typedef HM_NAMESPACE::hash_map<uint32, uint32> QuestAreaTriggerMap;
+ typedef HM_NAMESPACE::hash_map<uint32, uint32> BattleMastersMap;
+ typedef HM_NAMESPACE::hash_map<uint32, std::string> ItemTextMap;
+ typedef std::set<uint32> TavernAreaTriggerSet;
+ typedef std::set<uint32> GameObjectForQuestSet;
+
+ GroupSet mGroupSet;
+ GuildSet mGuildSet;
+ ArenaTeamSet mArenaTeamSet;
+
+ ItemMap mItems;
+ ItemMap mAitems;
+
+ ItemTextMap mItemTexts;
+
+ AuctionHouseObject mHordeAuctions;
+ AuctionHouseObject mAllianceAuctions;
+ AuctionHouseObject mNeutralAuctions;
+
+ QuestAreaTriggerMap mQuestAreaTriggerMap;
+ BattleMastersMap mBattleMastersMap;
+ TavernAreaTriggerSet mTavernAreaTriggerSet;
+ GameObjectForQuestSet mGameObjectForQuestSet;
+ GossipTextMap mGossipText;
+ AreaTriggerMap mAreaTriggers;
+ AreaTriggerScriptMap mAreaTriggerScripts;
+
+ RepOnKillMap mRepOnKill;
+
+ WeatherZoneMap mWeatherZoneMap;
+
+ PetCreateSpellMap mPetCreateSpell;
+
+ //character reserved names
+ typedef std::set<std::string> ReservedNamesMap;
+ ReservedNamesMap m_ReservedNames;
+
+ GraveYardMap mGraveYardMap;
+
+ GameTeleMap m_GameTeleMap;
+
+ typedef std::vector<LocaleConstant> LocalForIndex;
+ LocalForIndex m_LocalForIndex;
+ int GetOrNewIndexForLocale(LocaleConstant loc);
+
+ int DBCLocaleIndex;
+ private:
+ void LoadScripts(ScriptMapMap& scripts, char const* tablename);
+ void ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr);
+ void LoadQuestRelationsHelper(QuestRelations& map,char const* table);
+
+ typedef std::map<uint32,PetLevelInfo*> PetLevelInfoMap;
+ // PetLevelInfoMap[creature_id][level]
+ PetLevelInfoMap petInfo; // [creature_id][level]
+
+ PlayerClassInfo playerClassInfo[MAX_CLASSES];
+
+ void BuildPlayerLevelInfo(uint8 race, uint8 class_, uint8 level, PlayerLevelInfo* plinfo) const;
+ PlayerInfo playerInfo[MAX_RACES][MAX_CLASSES];
+
+ typedef std::map<uint32,uint32> BaseXPMap; // [area level][base xp]
+ BaseXPMap mBaseXPTable;
+
+ typedef std::map<uint32,int32> FishingBaseSkillMap; // [areaId][base skill level]
+ FishingBaseSkillMap mFishingBaseForArea;
+
+ typedef std::map<uint32,std::vector<std::string> > HalfNameMap;
+ HalfNameMap PetHalfName0;
+ HalfNameMap PetHalfName1;
+
+ MapObjectGuids mMapObjectGuids;
+ CreatureDataMap mCreatureDataMap;
+ CreatureLocaleMap mCreatureLocaleMap;
+ GameObjectDataMap mGameObjectDataMap;
+ GameObjectLocaleMap mGameObjectLocaleMap;
+ ItemLocaleMap mItemLocaleMap;
+ QuestLocaleMap mQuestLocaleMap;
+ NpcTextLocaleMap mNpcTextLocaleMap;
+ PageTextLocaleMap mPageTextLocaleMap;
+ MangosStringLocaleMap mMangosStringLocaleMap;
+ RespawnTimes mCreatureRespawnTimes;
+ RespawnTimes mGORespawnTimes;
+
+ typedef std::vector<uint32> GuildBankTabPriceMap;
+ GuildBankTabPriceMap mGuildBankTabPrice;
+
+ // Storage for Conditions. First element (index 0) is reserved for zero-condition (nothing required)
+ typedef std::vector<PlayerCondition> ConditionStore;
+ ConditionStore mConditions;
+
+ CacheNpcTextIdMap m_mCacheNpcTextIdMap;
+ CacheVendorItemMap m_mCacheVendorItemMap;
+ CacheTrainerSpellMap m_mCacheTrainerSpellMap;
+};
+
+#define objmgr MaNGOS::Singleton<ObjectMgr>::Instance()
+
+// scripting access functions
+bool MANGOS_DLL_SPEC LoadMangosStrings(DatabaseType& db, char const* table,int32 start_value = -1, int32 end_value = std::numeric_limits<int32>::min());
+MANGOS_DLL_SPEC const char* GetAreaTriggerScriptNameById(uint32 id);
+
+#endif
diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp
index 3d8eaf17182..25e29798c08 100644
--- a/src/game/Pet.cpp
+++ b/src/game/Pet.cpp
@@ -1,1750 +1,1750 @@
-/*
- * 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 "Log.h"
-#include "WorldSession.h"
-#include "WorldPacket.h"
-#include "ObjectMgr.h"
-#include "SpellMgr.h"
-#include "Pet.h"
-#include "MapManager.h"
-#include "Formulas.h"
-#include "SpellAuras.h"
-#include "CreatureAI.h"
-#include "Unit.h"
-#include "Util.h"
-
-char const* petTypeSuffix[MAX_PET_TYPE] =
-{
- "'s Minion", // SUMMON_PET
- "'s Pet", // HUNTER_PET
- "'s Guardian", // GUARDIAN_PET
- "'s Companion" // MINI_PET
-};
-
-//numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy)
-uint32 const LevelUpLoyalty[6] =
-{
- 5500,
- 11500,
- 17000,
- 23500,
- 31000,
- 39500,
-};
-
-uint32 const LevelStartLoyalty[6] =
-{
- 2000,
- 4500,
- 7000,
- 10000,
- 13500,
- 17500,
-};
-
-Pet::Pet(PetType type) : Creature()
-{
- m_isPet = true;
- m_name = "Pet";
- m_petType = type;
-
- m_removed = false;
- m_regenTimer = 4000;
- m_happinessTimer = 7500;
- m_loyaltyTimer = 12000;
- m_duration = 0;
- m_bonusdamage = 0;
-
- m_loyaltyPoints = 0;
- m_TrainingPoints = 0;
- m_resetTalentsCost = 0;
- m_resetTalentsTime = 0;
-
- m_auraUpdateMask = 0;
-
- // pets always have a charminfo, even if they are not actually charmed
- CharmInfo* charmInfo = InitCharmInfo(this);
-
- if(type == MINI_PET) // always passive
- charmInfo->SetReactState(REACT_PASSIVE);
- else if(type == GUARDIAN_PET) // always aggressive
- charmInfo->SetReactState(REACT_AGGRESSIVE);
-
- m_spells.clear();
- m_Auras.clear();
- m_CreatureSpellCooldowns.clear();
- m_CreatureCategoryCooldowns.clear();
- m_autospells.clear();
- m_declinedname = NULL;
-}
-
-Pet::~Pet()
-{
- if(m_uint32Values) // only for fully created Object
- {
- for (PetSpellMap::iterator i = m_spells.begin(); i != m_spells.end(); ++i)
- delete i->second;
- ObjectAccessor::Instance().RemoveObject(this);
- }
-
- delete m_declinedname;
-}
-
-void Pet::AddToWorld()
-{
- ///- Register the pet for guid lookup
- if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
- Unit::AddToWorld();
-}
-
-void Pet::RemoveFromWorld()
-{
- ///- Remove the pet from the accessor
- if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
- ///- Don't call the function for Creature, normal mobs + totems go in a different storage
- Unit::RemoveFromWorld();
-}
-
-bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool current )
-{
- uint32 ownerid = owner->GetGUIDLow();
-
- QueryResult *result;
-
- if(petnumber)
- // known petnumber entry 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
- result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND id = '%u'",ownerid, petnumber);
- else if(current)
- // current pet (slot 0) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
- result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND slot = '0'",ownerid );
- else if(petentry)
- // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets)
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
- result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '0' OR slot = '3') ",ownerid, petentry );
- else
- // any current or other non-stabled pet (for hunter "call pet")
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
- result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3') ",ownerid);
-
- if(!result)
- return false;
-
- Field *fields = result->Fetch();
-
- // update for case of current pet "slot = 0"
- petentry = fields[1].GetUInt32();
- if(!petentry)
- {
- delete result;
- return false;
- }
-
- uint32 summon_spell_id = fields[21].GetUInt32();
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id);
-
- bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0;
-
- // check temporary summoned pets like mage water elemental
- if(current && is_temporary_summoned)
- {
- delete result;
- return false;
- }
-
- Map *map = owner->GetMap();
- uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
- uint32 pet_number = fields[0].GetUInt32();
- if(!Create(guid, map, petentry, pet_number))
- {
- delete result;
- return false;
- }
-
- float px, py, pz;
- owner->GetClosePoint(px, py, pz,GetObjectSize(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
-
- Relocate(px, py, pz, owner->GetOrientation());
-
- if(!IsPositionValid())
- {
- sLog.outError("ERROR: Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
- delete result;
- return false;
- }
-
- setPetType(PetType(fields[22].GetUInt8()));
- SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction());
- SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id);
-
- CreatureInfo const *cinfo = GetCreatureInfo();
- if(cinfo->type == CREATURE_TYPE_CRITTER)
- {
- AIM_Initialize();
- map->Add((Creature*)this);
- delete result;
- return true;
- }
- if(getPetType()==HUNTER_PET || getPetType()==SUMMON_PET && cinfo->type == CREATURE_TYPE_DEMON && owner->getClass() == CLASS_WARLOCK)
- m_charmInfo->SetPetNumber(pet_number, true);
- else
- m_charmInfo->SetPetNumber(pet_number, false);
- SetUInt64Value(UNIT_FIELD_SUMMONEDBY, owner->GetGUID());
- SetDisplayId(fields[3].GetUInt32());
- SetNativeDisplayId(fields[3].GetUInt32());
- uint32 petlevel=fields[4].GetUInt32();
- SetUInt32Value(UNIT_NPC_FLAGS , 0);
- SetName(fields[11].GetString());
-
- switch(getPetType())
- {
-
- case SUMMON_PET:
- petlevel=owner->getLevel();
-
- SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
- SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
- // this enables popup window (pet dismiss, cancel)
- break;
- case HUNTER_PET:
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
- SetByteValue(UNIT_FIELD_BYTES_1, 1, fields[8].GetUInt32());
- SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
- SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
-
- if(fields[12].GetBool())
- SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED);
- else
- SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
-
- SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
- // this enables popup window (pet abandon, cancel)
- SetTP(fields[9].GetInt32());
- SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
- SetPower( POWER_HAPPINESS,fields[15].GetUInt32());
- setPowerType(POWER_FOCUS);
- break;
- default:
- sLog.outError("Pet have incorrect type (%u) for pet loading.",getPetType());
- }
- InitStatsForLevel( petlevel);
- SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
- SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32());
- SetUInt64Value(UNIT_FIELD_CREATEDBY, owner->GetGUID());
-
- m_charmInfo->SetReactState( ReactStates( fields[6].GetUInt8() ));
- m_loyaltyPoints = fields[7].GetInt32();
-
- uint32 savedhealth = fields[13].GetUInt32();
- uint32 savedmana = fields[14].GetUInt32();
-
- // set current pet as current
- if(fields[10].GetUInt32() != 0)
- {
- CharacterDatabase.BeginTransaction();
- CharacterDatabase.PExecute("UPDATE character_pet SET slot = '3' WHERE owner = '%u' AND slot = '0' AND id <> '%u'",ownerid, m_charmInfo->GetPetNumber());
- CharacterDatabase.PExecute("UPDATE character_pet SET slot = '0' WHERE owner = '%u' AND id = '%u'",ownerid, m_charmInfo->GetPetNumber());
- CharacterDatabase.CommitTransaction();
- }
-
- if(!is_temporary_summoned)
- {
- // permanent controlled pets store state in DB
- Tokens tokens = StrSplit(fields[16].GetString(), " ");
-
- if(tokens.size() != 20)
- {
- delete result;
- return false;
- }
-
- int index;
- Tokens::iterator iter;
- for(iter = tokens.begin(), index = 0; index < 10; ++iter, ++index )
- {
- m_charmInfo->GetActionBarEntry(index)->Type = atol((*iter).c_str());
- ++iter;
- m_charmInfo->GetActionBarEntry(index)->SpellOrAction = atol((*iter).c_str());
- }
-
- //init teach spells
- tokens = StrSplit(fields[17].GetString(), " ");
- for (iter = tokens.begin(), index = 0; index < 4; ++iter, ++index)
- {
- uint32 tmp = atol((*iter).c_str());
-
- ++iter;
-
- if(tmp)
- AddTeachSpell(tmp, atol((*iter).c_str()));
- else
- break;
- }
- }
-
- // since last save (in seconds)
- uint32 timediff = (time(NULL) - fields[18].GetUInt32());
-
- delete result;
-
- //load spells/cooldowns/auras
- SetCanModifyStats(true);
- _LoadAuras(timediff);
-
- //init AB
- if(is_temporary_summoned)
- {
- // Temporary summoned pets always have initial spell list at load
- InitPetCreateSpells();
- }
- else
- {
- LearnPetPassives();
- CastPetAuras(current);
- }
-
- if(getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current
- {
- SetHealth(GetMaxHealth());
- SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
- }
- else
- {
- SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth);
- SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana);
- }
-
- AIM_Initialize();
- map->Add((Creature*)this);
-
- // Spells should be loaded after pet is added to map, because in CanCast is check on it
- _LoadSpells();
- _LoadSpellCooldowns();
-
- owner->SetPet(this); // in DB stored only full controlled creature
- sLog.outDebug("New Pet has guid %u", GetGUIDLow());
-
- if(owner->GetTypeId() == TYPEID_PLAYER)
- {
- ((Player*)owner)->PetSpellInitialize();
- if(((Player*)owner)->GetGroup())
- ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_PET);
- }
-
- if(owner->GetTypeId() == TYPEID_PLAYER && getPetType() == HUNTER_PET)
- {
- result = CharacterDatabase.PQuery("SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'",owner->GetGUIDLow(),GetCharmInfo()->GetPetNumber());
-
- if(result)
- {
- if(m_declinedname)
- delete m_declinedname;
-
- m_declinedname = new DeclinedName;
- Field *fields = result->Fetch();
- for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
- {
- m_declinedname->name[i] = fields[i].GetCppString();
- }
- }
- }
-
- return true;
-}
-
-void Pet::SavePetToDB(PetSaveMode mode)
-{
- if(!GetEntry())
- return;
-
- // save only fully controlled creature
- if(!isControlled())
- return;
-
- uint32 curhealth = GetHealth();
- uint32 curmana = GetPower(POWER_MANA);
-
- switch(mode)
- {
- case PET_SAVE_IN_STABLE_SLOT_1:
- case PET_SAVE_IN_STABLE_SLOT_2:
- case PET_SAVE_NOT_IN_SLOT:
- {
- RemoveAllAuras();
-
- //only alive hunter pets get auras saved, the others don't
- if(!(getPetType() == HUNTER_PET && isAlive()))
- m_Auras.clear();
- }
- default:
- break;
- }
-
- _SaveSpells();
- _SaveSpellCooldowns();
- _SaveAuras();
-
- switch(mode)
- {
- case PET_SAVE_AS_CURRENT:
- case PET_SAVE_IN_STABLE_SLOT_1:
- case PET_SAVE_IN_STABLE_SLOT_2:
- case PET_SAVE_NOT_IN_SLOT:
- {
- uint32 loyalty =1;
- if(getPetType()!=HUNTER_PET)
- loyalty = GetLoyaltyLevel();
-
- uint32 owner = GUID_LOPART(GetOwnerGUID());
- std::string name = m_name;
- CharacterDatabase.escape_string(name);
- CharacterDatabase.BeginTransaction();
- // remove current data
- CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner,m_charmInfo->GetPetNumber() );
-
- // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
- if(mode!=PET_SAVE_NOT_IN_SLOT)
- CharacterDatabase.PExecute("UPDATE character_pet SET slot = 3 WHERE owner = '%u' AND slot = '%u'", owner, uint32(mode) );
-
- // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
- if(getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode==PET_SAVE_NOT_IN_SLOT))
- CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3')", owner );
- // save pet
- std::ostringstream ss;
- ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata,TeachSpelldata,savetime,resettalents_cost,resettalents_time,CreatedBySpell,PetType) "
- << "VALUES ("
- << m_charmInfo->GetPetNumber() << ", "
- << GetEntry() << ", "
- << owner << ", "
- << GetNativeDisplayId() << ", "
- << getLevel() << ", "
- << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", "
- << uint32(m_charmInfo->GetReactState()) << ", "
- << m_loyaltyPoints << ", "
- << GetLoyaltyLevel() << ", "
- << m_TrainingPoints << ", "
- << uint32(mode) << ", '"
- << name.c_str() << "', "
- << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", "
- << (curhealth<1?1:curhealth) << ", "
- << curmana << ", "
- << GetPower(POWER_HAPPINESS) << ", '";
-
- for(uint32 i = 0; i < 10; i++)
- ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " ";
- ss << "', '";
-
- //save spells the pet can teach to it's Master
- {
- int i = 0;
- for(TeachSpellMap::iterator itr = m_teachspells.begin(); i < 4 && itr != m_teachspells.end(); ++i, ++itr)
- ss << itr->first << " " << itr->second << " ";
- for(; i < 4; ++i)
- ss << uint32(0) << " " << uint32(0) << " ";
- }
-
- ss << "', "
- << time(NULL) << ", "
- << uint32(m_resetTalentsCost) << ", "
- << uint64(m_resetTalentsTime) << ", "
- << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", "
- << uint32(getPetType()) << ")";
-
- CharacterDatabase.Execute( ss.str().c_str() );
-
- CharacterDatabase.CommitTransaction();
- break;
- }
- case PET_SAVE_AS_DELETED:
- {
- RemoveAllAuras();
- uint32 owner = GUID_LOPART(GetOwnerGUID());
- DeleteFromDB(m_charmInfo->GetPetNumber());
- break;
- }
- default:
- sLog.outError("Unknown pet save/remove mode: %d",mode);
- }
-}
-
-void Pet::DeleteFromDB(uint32 guidlow)
-{
- CharacterDatabase.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow);
- CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE id = '%u'", guidlow);
- CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", guidlow);
- CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u'", guidlow);
- CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", guidlow);
-}
-
-void Pet::setDeathState(DeathState s) // overwrite virtual Creature::setDeathState and Unit::setDeathState
-{
- Creature::setDeathState(s);
- if(getDeathState()==CORPSE)
- {
- //remove summoned pet (no corpse)
- if(getPetType()==SUMMON_PET)
- Remove(PET_SAVE_NOT_IN_SLOT);
- // other will despawn at corpse desppawning (Pet::Update code)
- else
- {
- // pet corpse non lootable and non skinnable
- SetUInt32Value( UNIT_DYNAMIC_FLAGS, 0x00 );
- RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
-
- //lose happiness when died and not in BG/Arena
- MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId());
- if(!mapEntry || (mapEntry->map_type != MAP_ARENA && mapEntry->map_type != MAP_BATTLEGROUND))
- ModifyPower(POWER_HAPPINESS, -HAPPINESS_LEVEL_SIZE);
-
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
- }
- }
- else if(getDeathState()==ALIVE)
- {
- RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
- CastPetAuras(true);
- }
-}
-
-void Pet::Update(uint32 diff)
-{
- if(m_removed) // pet already removed, just wait in remove queue, no updates
- return;
-
- switch( m_deathState )
- {
- case CORPSE:
- {
- if( m_deathTimer <= diff )
- {
- assert(getPetType()!=SUMMON_PET && "Must be already removed.");
- Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER!
- return;
- }
- break;
- }
- case ALIVE:
- {
- // unsummon pet that lost owner
- Unit* owner = GetOwner();
- if(!owner || !IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) || isControlled() && !owner->GetPetGUID())
- {
- Remove(PET_SAVE_NOT_IN_SLOT, true);
- return;
- }
-
- if(isControlled())
- {
- if( owner->GetPetGUID() != GetGUID() )
- {
- Remove(getPetType()==HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
- return;
- }
- }
-
- if(m_duration > 0)
- {
- if(m_duration > diff)
- m_duration -= diff;
- else
- {
- Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
- return;
- }
- }
-
- if(getPetType() != HUNTER_PET)
- break;
-
- //regenerate Focus
- if(m_regenTimer <= diff)
- {
- RegenerateFocus();
- m_regenTimer = 4000;
- }
- else
- m_regenTimer -= diff;
-
- if(m_happinessTimer <= diff)
- {
- LooseHappiness();
- m_happinessTimer = 7500;
- }
- else
- m_happinessTimer -= diff;
-
- if(m_loyaltyTimer <= diff)
- {
- TickLoyaltyChange();
- m_loyaltyTimer = 12000;
- }
- else
- m_loyaltyTimer -= diff;
-
- break;
- }
- default:
- break;
- }
- Creature::Update(diff);
-}
-
-void Pet::RegenerateFocus()
-{
- uint32 curValue = GetPower(POWER_FOCUS);
- uint32 maxValue = GetMaxPower(POWER_FOCUS);
-
- if (curValue >= maxValue)
- return;
-
- float addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS);
-
- AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
- for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
- if ((*i)->GetModifier()->m_miscvalue == POWER_FOCUS)
- addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f;
-
- ModifyPower(POWER_FOCUS, (int32)addvalue);
-}
-
-void Pet::LooseHappiness()
-{
- uint32 curValue = GetPower(POWER_HAPPINESS);
- if (curValue <= 0)
- return;
- int32 addvalue = (140 >> GetLoyaltyLevel()) * 125; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs)
- if(isInCombat()) //we know in combat happiness fades faster, multiplier guess
- addvalue = int32(addvalue * 1.5);
- ModifyPower(POWER_HAPPINESS, -addvalue);
-}
-
-void Pet::ModifyLoyalty(int32 addvalue)
-{
- uint32 loyaltylevel = GetLoyaltyLevel();
-
- if(addvalue > 0) //only gain influenced, not loss
- addvalue = int32((float)addvalue * sWorld.getRate(RATE_LOYALTY));
-
- if(loyaltylevel >= BEST_FRIEND && (addvalue + m_loyaltyPoints) > int32(GetMaxLoyaltyPoints(loyaltylevel)))
- return;
-
- m_loyaltyPoints += addvalue;
-
- if(m_loyaltyPoints < 0)
- {
- if(loyaltylevel > REBELLIOUS)
- {
- //level down
- --loyaltylevel;
- SetLoyaltyLevel(LoyaltyLevel(loyaltylevel));
- m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel);
- SetTP(m_TrainingPoints - int32(getLevel()));
- }
- else
- {
- m_loyaltyPoints = 0;
- Unit* owner = GetOwner();
- if(owner && owner->GetTypeId() == TYPEID_PLAYER)
- {
- WorldPacket data(SMSG_PET_BROKEN, 0);
- ((Player*)owner)->GetSession()->SendPacket(&data);
-
- //run away
- ((Player*)owner)->RemovePet(this,PET_SAVE_AS_DELETED);
- }
- }
- }
- //level up
- else if(m_loyaltyPoints > int32(GetMaxLoyaltyPoints(loyaltylevel)))
- {
- ++loyaltylevel;
- SetLoyaltyLevel(LoyaltyLevel(loyaltylevel));
- m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel);
- SetTP(m_TrainingPoints + getLevel());
- }
-}
-
-void Pet::TickLoyaltyChange()
-{
- int32 addvalue;
-
- switch(GetHappinessState())
- {
- case HAPPY: addvalue = 20; break;
- case CONTENT: addvalue = 10; break;
- case UNHAPPY: addvalue = -20; break;
- default:
- return;
- }
- ModifyLoyalty(addvalue);
-}
-
-void Pet::KillLoyaltyBonus(uint32 level)
-{
- if(level > 100)
- return;
-
- //at lower levels gain is faster | the lower loyalty the more loyalty is gained
- uint32 bonus = uint32(((100 - level) / 10) + (6 - GetLoyaltyLevel()));
- ModifyLoyalty(bonus);
-}
-
-HappinessState Pet::GetHappinessState()
-{
- if(GetPower(POWER_HAPPINESS) < HAPPINESS_LEVEL_SIZE)
- return UNHAPPY;
- else if(GetPower(POWER_HAPPINESS) >= HAPPINESS_LEVEL_SIZE * 2)
- return HAPPY;
- else
- return CONTENT;
-}
-
-void Pet::SetLoyaltyLevel(LoyaltyLevel level)
-{
- SetByteValue(UNIT_FIELD_BYTES_1, 1, level);
-}
-
-bool Pet::CanTakeMoreActiveSpells(uint32 spellid)
-{
- uint8 activecount = 1;
- uint32 chainstartstore[ACTIVE_SPELLS_MAX];
-
- if(IsPassiveSpell(spellid))
- return true;
-
- chainstartstore[0] = spellmgr.GetFirstSpellInChain(spellid);
-
- for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- {
- if(IsPassiveSpell(itr->first))
- continue;
-
- uint32 chainstart = spellmgr.GetFirstSpellInChain(itr->first);
-
- uint8 x;
-
- for(x = 0; x < activecount; x++)
- {
- if(chainstart == chainstartstore[x])
- break;
- }
-
- if(x == activecount) //spellchain not yet saved -> add active count
- {
- ++activecount;
- if(activecount > ACTIVE_SPELLS_MAX)
- return false;
- chainstartstore[x] = chainstart;
- }
- }
- return true;
-}
-
-bool Pet::HasTPForSpell(uint32 spellid)
-{
- int32 neededtrainp = GetTPForSpell(spellid);
- if((m_TrainingPoints - neededtrainp < 0 || neededtrainp < 0) && neededtrainp != 0)
- return false;
- return true;
-}
-
-int32 Pet::GetTPForSpell(uint32 spellid)
-{
- uint32 basetrainp = 0;
-
- SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid);
- SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid);
- for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
- {
- if(!_spell_idx->second->reqtrainpoints)
- return 0;
-
- basetrainp = _spell_idx->second->reqtrainpoints;
- break;
- }
-
- uint32 spenttrainp = 0;
- uint32 chainstart = spellmgr.GetFirstSpellInChain(spellid);
-
- for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- {
- if(itr->second->state == PETSPELL_REMOVED)
- continue;
-
- if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
- {
- SkillLineAbilityMap::const_iterator _lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first);
- SkillLineAbilityMap::const_iterator _upper = spellmgr.GetEndSkillLineAbilityMap(itr->first);
-
- for(SkillLineAbilityMap::const_iterator _spell_idx2 = _lower; _spell_idx2 != _upper; ++_spell_idx2)
- {
- if(_spell_idx2->second->reqtrainpoints > spenttrainp)
- {
- spenttrainp = _spell_idx2->second->reqtrainpoints;
- break;
- }
- }
- }
- }
-
- return int32(basetrainp) - int32(spenttrainp);
-}
-
-uint32 Pet::GetMaxLoyaltyPoints(uint32 level)
-{
- return LevelUpLoyalty[level - 1];
-}
-
-uint32 Pet::GetStartLoyaltyPoints(uint32 level)
-{
- return LevelStartLoyalty[level - 1];
-}
-
-void Pet::SetTP(int32 TP)
-{
- m_TrainingPoints = TP;
- SetUInt32Value(UNIT_TRAINING_POINTS, (uint32)GetDispTP());
-}
-
-int32 Pet::GetDispTP()
-{
- if(getPetType()!= HUNTER_PET)
- return(0);
- if(m_TrainingPoints < 0)
- return -m_TrainingPoints;
- else
- return -(m_TrainingPoints + 1);
-}
-
-void Pet::Remove(PetSaveMode mode, bool returnreagent)
-{
- Unit* owner = GetOwner();
-
- if(owner)
- {
- if(owner->GetTypeId()==TYPEID_PLAYER)
- {
- ((Player*)owner)->RemovePet(this,mode,returnreagent);
- return;
- }
-
- // only if current pet in slot
- if(owner->GetPetGUID()==GetGUID())
- owner->SetPet(0);
- }
-
- CleanupsBeforeDelete();
- AddObjectToRemoveList();
- m_removed = true;
-}
-
-void Pet::GivePetXP(uint32 xp)
-{
- if(getPetType() != HUNTER_PET)
- return;
-
- if ( xp < 1 )
- return;
-
- if(!isAlive())
- return;
-
- uint32 level = getLevel();
-
- // XP to money conversion processed in Player::RewardQuest
- if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- return;
-
- uint32 curXP = GetUInt32Value(UNIT_FIELD_PETEXPERIENCE);
- uint32 nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
- uint32 newXP = curXP + xp;
-
- if(newXP >= nextLvlXP && level+1 > GetOwner()->getLevel())
- {
- SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, nextLvlXP-1);
- return;
- }
-
- while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
- {
- newXP -= nextLvlXP;
-
- SetLevel( level + 1 );
- SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(level+1))/4));
-
- level = getLevel();
- nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
- GivePetLevel(level);
- }
-
- SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, newXP);
-
- if(getPetType() == HUNTER_PET)
- KillLoyaltyBonus(level);
-}
-
-void Pet::GivePetLevel(uint32 level)
-{
- if(!level)
- return;
-
- InitStatsForLevel( level);
-
- SetTP(m_TrainingPoints + (GetLoyaltyLevel() - 1));
-}
-
-bool Pet::CreateBaseAtCreature(Creature* creature)
-{
- if(!creature)
- {
- sLog.outError("CRITICAL ERROR: NULL pointer parsed into CreateBaseAtCreature()");
- return false;
- }
- uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
-
- sLog.outBasic("SetInstanceID()");
- SetInstanceId(creature->GetInstanceId());
-
- sLog.outBasic("Create pet");
- uint32 pet_number = objmgr.GeneratePetNumber();
- if(!Create(guid, creature->GetMap(), creature->GetEntry(), pet_number))
- return false;
-
- Relocate(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation());
-
- if(!IsPositionValid())
- {
- sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
- return false;
- }
-
- CreatureInfo const *cinfo = GetCreatureInfo();
- if(!cinfo)
- {
- sLog.outError("ERROR: CreateBaseAtCreature() failed, creatureInfo is missing!");
- return false;
- }
-
- if(cinfo->type == CREATURE_TYPE_CRITTER)
- {
- setPetType(MINI_PET);
- return true;
- }
- SetDisplayId(creature->GetDisplayId());
- SetNativeDisplayId(creature->GetNativeDisplayId());
- SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
- SetPower( POWER_HAPPINESS,166500);
- setPowerType(POWER_FOCUS);
- SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
- SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
- SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(creature->getLevel()))/4));
- SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
- SetUInt32Value(UNIT_NPC_FLAGS , 0);
-
- CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(creature->GetCreatureInfo()->family);
- if( char* familyname = cFamily->Name[sWorld.GetDefaultDbcLocale()] )
- SetName(familyname);
- else
- SetName(creature->GetName());
-
- m_loyaltyPoints = 1000;
- if(cinfo->type == CREATURE_TYPE_BEAST)
- {
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
- SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
- SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
- SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
-
- SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED) );
- SetLoyaltyLevel(REBELLIOUS);
- }
- return true;
-}
-
-bool Pet::InitStatsForLevel(uint32 petlevel)
-{
- CreatureInfo const *cinfo = GetCreatureInfo();
- assert(cinfo);
-
- Unit* owner = GetOwner();
- if(!owner)
- {
- sLog.outError("ERROR: attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo->Entry);
- return false;
- }
-
- uint32 creature_ID = (getPetType() == HUNTER_PET) ? 1 : cinfo->Entry;
-
- SetLevel( petlevel);
-
- SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
-
- SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel*50));
-
- SetAttackTime(BASE_ATTACK, BASE_ATTACK_TIME);
- SetAttackTime(OFF_ATTACK, BASE_ATTACK_TIME);
- SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
-
- SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0);
-
- CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family);
- if(cFamily && cFamily->minScale > 0.0f)
- {
- float scale;
- if (getLevel() >= cFamily->maxScaleLevel)
- scale = cFamily->maxScale;
- else if (getLevel() <= cFamily->minScaleLevel)
- scale = cFamily->minScale;
- else
- scale = cFamily->minScale + (getLevel() - cFamily->minScaleLevel) / cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale);
-
- SetFloatValue(OBJECT_FIELD_SCALE_X, scale);
- }
- m_bonusdamage = 0;
-
- int32 createResistance[MAX_SPELL_SCHOOL] = {0,0,0,0,0,0,0};
-
- if(cinfo && getPetType() != HUNTER_PET)
- {
- createResistance[SPELL_SCHOOL_HOLY] = cinfo->resistance1;
- createResistance[SPELL_SCHOOL_FIRE] = cinfo->resistance2;
- createResistance[SPELL_SCHOOL_NATURE] = cinfo->resistance3;
- createResistance[SPELL_SCHOOL_FROST] = cinfo->resistance4;
- createResistance[SPELL_SCHOOL_SHADOW] = cinfo->resistance5;
- createResistance[SPELL_SCHOOL_ARCANE] = cinfo->resistance6;
- }
-
- switch(getPetType())
- {
- case SUMMON_PET:
- {
- if(owner->GetTypeId() == TYPEID_PLAYER)
- {
- switch(owner->getClass())
- {
- case CLASS_WARLOCK:
- {
-
- //the damage bonus used for pets is either fire or shadow damage, whatever is higher
- uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE);
- uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW);
- uint32 val = (fire > shadow) ? fire : shadow;
-
- SetBonusDamage(int32 (val * 0.15f));
- //bonusAP += val * 0.57;
- break;
- }
- case CLASS_MAGE:
- {
- //40% damage bonus of mage's frost damage
- float val = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4;
- if(val < 0)
- val = 0;
- SetBonusDamage( int32(val));
- break;
- }
- default:
- break;
- }
- }
-
- SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
- SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
-
- //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
-
- PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
- if(pInfo) // exist in DB
- {
- SetCreateHealth(pInfo->health);
- SetCreateMana(pInfo->mana);
-
- if(pInfo->armor > 0)
- SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
-
- for(int stat = 0; stat < MAX_STATS; ++stat)
- {
- SetCreateStat(Stats(stat),float(pInfo->stats[stat]));
- }
- }
- else // not exist in DB, use some default fake data
- {
- sLog.outErrorDb("Summoned pet (Entry: %u) not have pet stats data in DB",cinfo->Entry);
-
- // remove elite bonuses included in DB values
- SetCreateHealth(uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
- SetCreateMana( uint32(((float(cinfo->maxmana) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
-
- SetCreateStat(STAT_STRENGTH,22);
- SetCreateStat(STAT_AGILITY,22);
- SetCreateStat(STAT_STAMINA,25);
- SetCreateStat(STAT_INTELLECT,28);
- SetCreateStat(STAT_SPIRIT,27);
- }
- break;
- }
- case HUNTER_PET:
- {
- SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(petlevel))/4));
-
- //these formula may not be correct; however, it is designed to be close to what it should be
- //this makes dps 0.5 of pets level
- SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
- //damage range is then petlevel / 2
- SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
- //damage is increased afterwards as strength and pet scaling modify attack power
-
- //stored standard pet stats are entry 1 in pet_levelinfo
- PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
- if(pInfo) // exist in DB
- {
- SetCreateHealth(pInfo->health);
- SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
- //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
-
- for( int i = STAT_STRENGTH; i < MAX_STATS; i++)
- {
- SetCreateStat(Stats(i), float(pInfo->stats[i]));
- }
- }
- else // not exist in DB, use some default fake data
- {
- sLog.outErrorDb("Hunter pet levelstats missing in DB");
-
- // remove elite bonuses included in DB values
- SetCreateHealth( uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
-
- SetCreateStat(STAT_STRENGTH,22);
- SetCreateStat(STAT_AGILITY,22);
- SetCreateStat(STAT_STAMINA,25);
- SetCreateStat(STAT_INTELLECT,28);
- SetCreateStat(STAT_SPIRIT,27);
- }
- break;
- }
- case GUARDIAN_PET:
- SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
- SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
-
- SetCreateMana( 28 + 10*petlevel );
- SetCreateHealth( 28 + 30*petlevel );
-
- // FIXME: this is wrong formula, possible each guardian pet have own damage formula
- //these formula may not be correct; however, it is designed to be close to what it should be
- //this makes dps 0.5 of pets level
- SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
- //damage range is then petlevel / 2
- SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
- break;
- default:
- sLog.outError("Pet have incorrect type (%u) for levelup.",getPetType()); break;
- }
-
- for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
- SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i]) );
-
- UpdateAllStats();
-
- SetHealth(GetMaxHealth());
- SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
-
- return true;
-}
-
-bool Pet::HaveInDiet(ItemPrototype const* item) const
-{
- if (!item->FoodType)
- return false;
-
- CreatureInfo const* cInfo = GetCreatureInfo();
- if(!cInfo)
- return false;
-
- CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
- if(!cFamily)
- return false;
-
- uint32 diet = cFamily->petFoodMask;
- uint32 FoodMask = 1 << (item->FoodType-1);
- return diet & FoodMask;
-}
-
-uint32 Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel)
-{
- // -5 or greater food level
- if(getLevel() <= itemlevel +5) //possible to feed level 60 pet with level 55 level food for full effect
- return 35000;
- // -10..-6
- else if(getLevel() <= itemlevel + 10) //pure guess, but sounds good
- return 17000;
- // -14..-11
- else if(getLevel() <= itemlevel + 14) //level 55 food gets green on 70, makes sense to me
- return 8000;
- // -15 or less
- else
- return 0; //food too low level
-}
-
-void Pet::_LoadSpellCooldowns()
-{
- m_CreatureSpellCooldowns.clear();
- m_CreatureCategoryCooldowns.clear();
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT spell,time FROM pet_spell_cooldown WHERE guid = '%u'",m_charmInfo->GetPetNumber());
-
- if(result)
- {
- time_t curTime = time(NULL);
-
- WorldPacket data(SMSG_SPELL_COOLDOWN, (8+1+result->GetRowCount()*8));
- data << GetGUID();
- data << uint8(0x0);
-
- do
- {
- Field *fields = result->Fetch();
-
- uint32 spell_id = fields[0].GetUInt32();
- time_t db_time = (time_t)fields[1].GetUInt64();
-
- if(!sSpellStore.LookupEntry(spell_id))
- {
- sLog.outError("Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping.",m_charmInfo->GetPetNumber(),spell_id);
- continue;
- }
-
- // skip outdated cooldown
- if(db_time <= curTime)
- continue;
-
- data << uint32(spell_id);
- data << uint32(uint32(db_time-curTime)*1000); // in m.secs
-
- _AddCreatureSpellCooldown(spell_id,db_time);
-
- sLog.outDebug("Pet (Number: %u) spell %u cooldown loaded (%u secs).",m_charmInfo->GetPetNumber(),spell_id,uint32(db_time-curTime));
- }
- while( result->NextRow() );
-
- delete result;
-
- if(!m_CreatureSpellCooldowns.empty() && GetOwner())
- {
- ((Player*)GetOwner())->GetSession()->SendPacket(&data);
- }
- }
-}
-
-void Pet::_SaveSpellCooldowns()
-{
- CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", m_charmInfo->GetPetNumber());
-
- time_t curTime = time(NULL);
-
- // remove oudated and save active
- for(CreatureSpellCooldowns::iterator itr = m_CreatureSpellCooldowns.begin();itr != m_CreatureSpellCooldowns.end();)
- {
- if(itr->second <= curTime)
- m_CreatureSpellCooldowns.erase(itr++);
- else
- {
- CharacterDatabase.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" I64FMTD "')", m_charmInfo->GetPetNumber(), itr->first, uint64(itr->second));
- ++itr;
- }
- }
-}
-
-void Pet::_LoadSpells()
-{
- QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber());
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
-
- addSpell(fields[0].GetUInt16(), fields[2].GetUInt16(), PETSPELL_UNCHANGED, fields[1].GetUInt16());
- }
- while( result->NextRow() );
-
- delete result;
- }
-}
-
-void Pet::_SaveSpells()
-{
- for (PetSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
- {
- ++next;
- if (itr->second->type == PETSPELL_FAMILY) continue; // prevent saving family passives to DB
- if (itr->second->state == PETSPELL_REMOVED || itr->second->state == PETSPELL_CHANGED)
- CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first);
- if (itr->second->state == PETSPELL_NEW || itr->second->state == PETSPELL_CHANGED)
- CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,slot,active) VALUES ('%u', '%u', '%u','%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second->slotId,itr->second->active);
-
- if (itr->second->state == PETSPELL_REMOVED)
- _removeSpell(itr->first);
- else
- itr->second->state = PETSPELL_UNCHANGED;
- }
-}
-
-void Pet::_LoadAuras(uint32 timediff)
-{
- m_Auras.clear();
- for (int i = 0; i < TOTAL_AURAS; i++)
- m_modAuras[i].clear();
-
- // all aura related fields
- for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i)
- SetUInt32Value(i, 0);
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
- uint64 caster_guid = fields[0].GetUInt64();
- uint32 spellid = fields[1].GetUInt32();
- uint32 effindex = fields[2].GetUInt32();
- int32 damage = (int32)fields[3].GetUInt32();
- int32 maxduration = (int32)fields[4].GetUInt32();
- int32 remaintime = (int32)fields[5].GetUInt32();
- int32 remaincharges = (int32)fields[6].GetUInt32();
-
- SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
- if(!spellproto)
- {
- sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex);
- continue;
- }
-
- if(effindex >= 3)
- {
- sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex);
- continue;
- }
-
- // negative effects should continue counting down after logout
- if (remaintime != -1 && !IsPositiveEffect(spellid, effindex))
- {
- if(remaintime <= int32(timediff))
- continue;
-
- remaintime -= timediff;
- }
-
- // prevent wrong values of remaincharges
- if(spellproto->procCharges)
- {
- if(remaincharges <= 0 || remaincharges > spellproto->procCharges)
- remaincharges = spellproto->procCharges;
- }
- else
- remaincharges = -1;
-
- Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL);
-
- if(!damage)
- damage = aura->GetModifier()->m_amount;
- aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges);
- AddAura(aura);
- }
- while( result->NextRow() );
-
- delete result;
- }
-}
-
-void Pet::_SaveAuras()
-{
- CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
-
- AuraMap const& auras = GetAuras();
- for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
- {
- // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras.
- SpellEntry const *spellInfo = itr->second->GetSpellProto();
- uint8 i;
- for (i = 0; i < 3; i++)
- if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH ||
- spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER ||
- spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PET )
- break;
-
- if (i == 3 && !itr->second->IsPassive())
- CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges) "
- "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d')",
- m_charmInfo->GetPetNumber(), itr->second->GetCasterGUID(),(uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex(),(*itr).second->GetModifier()->m_amount,int((*itr).second->GetAuraMaxDuration()),int((*itr).second->GetAuraDuration()),int((*itr).second->m_procCharges));
- }
-}
-
-bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 slot_id, PetSpellType type)
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
- if (!spellInfo)
- {
- // do pet spell book cleanup
- if(state == PETSPELL_UNCHANGED) // spell load case
- {
- sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.",spell_id);
- CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE spell = '%u'",spell_id);
- }
- else
- sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
-
- return false;
- }
-
- PetSpellMap::iterator itr = m_spells.find(spell_id);
- if (itr != m_spells.end())
- {
- if (itr->second->state == PETSPELL_REMOVED)
- {
- delete itr->second;
- m_spells.erase(itr);
- state = PETSPELL_CHANGED;
- }
- else if (state == PETSPELL_UNCHANGED && itr->second->state != PETSPELL_UNCHANGED)
- {
- // can be in case spell loading but learned at some previous spell loading
- itr->second->state = PETSPELL_UNCHANGED;
- return false;
- }
- else
- return false;
- }
-
- uint32 oldspell_id = 0;
-
- PetSpell *newspell = new PetSpell;
- newspell->state = state;
- newspell->type = type;
-
- if(active == ACT_DECIDE) //active was not used before, so we save it's autocast/passive state here
- {
- if(IsPassiveSpell(spell_id))
- newspell->active = ACT_PASSIVE;
- else
- newspell->active = ACT_DISABLED;
- }
- else
- newspell->active = active;
-
- uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id);
-
- for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); itr++)
- {
- if(itr->second->state == PETSPELL_REMOVED) continue;
-
- if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
- {
- slot_id = itr->second->slotId;
- newspell->active = itr->second->active;
-
- if(newspell->active == ACT_ENABLED)
- ToggleAutocast(itr->first, false);
-
- oldspell_id = itr->first;
- removeSpell(itr->first);
- }
- }
-
- uint16 tmpslot=slot_id;
-
- if (tmpslot == 0xffff)
- {
- uint16 maxid = 0;
- PetSpellMap::iterator itr;
- for (itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- {
- if(itr->second->state == PETSPELL_REMOVED) continue;
- if (itr->second->slotId > maxid) maxid = itr->second->slotId;
- }
- tmpslot = maxid + 1;
- }
-
- newspell->slotId = tmpslot;
- m_spells[spell_id] = newspell;
-
- if (IsPassiveSpell(spell_id))
- CastSpell(this, spell_id, true);
- else if(state == PETSPELL_NEW)
- m_charmInfo->AddSpellToAB(oldspell_id, spell_id);
-
- if(newspell->active == ACT_ENABLED)
- ToggleAutocast(spell_id, true);
-
- return true;
-}
-
-bool Pet::learnSpell(uint16 spell_id)
-{
- // prevent duplicated entires in spell book
- if (!addSpell(spell_id))
- return false;
-
- Unit* owner = GetOwner();
- if(owner->GetTypeId()==TYPEID_PLAYER)
- ((Player*)owner)->PetSpellInitialize();
- return true;
-}
-
-void Pet::removeSpell(uint16 spell_id)
-{
- PetSpellMap::iterator itr = m_spells.find(spell_id);
- if (itr == m_spells.end())
- return;
-
- if(itr->second->state == PETSPELL_REMOVED)
- return;
-
- if(itr->second->state == PETSPELL_NEW)
- {
- delete itr->second;
- m_spells.erase(itr);
- }
- else
- itr->second->state = PETSPELL_REMOVED;
-
- RemoveAurasDueToSpell(spell_id);
-}
-
-bool Pet::_removeSpell(uint16 spell_id)
-{
- PetSpellMap::iterator itr = m_spells.find(spell_id);
- if (itr != m_spells.end())
- {
- delete itr->second;
- m_spells.erase(itr);
- return true;
- }
- return false;
-}
-
-void Pet::InitPetCreateSpells()
-{
- m_charmInfo->InitPetActionBar();
-
- m_spells.clear();
- int32 usedtrainpoints = 0, petspellid;
- PetCreateSpellEntry const* CreateSpells = objmgr.GetPetCreateSpellEntry(GetEntry());
- if(CreateSpells)
- {
- for(uint8 i = 0; i < 4; i++)
- {
- if(!CreateSpells->spellid[i])
- break;
-
- SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(CreateSpells->spellid[i]);
- if(!learn_spellproto)
- continue;
-
- if(learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_SPELL || learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_PET_SPELL)
- {
- petspellid = learn_spellproto->EffectTriggerSpell[0];
- Unit* owner = GetOwner();
- if(owner->GetTypeId() == TYPEID_PLAYER && !((Player*)owner)->HasSpell(learn_spellproto->Id))
- {
- if(IsPassiveSpell(petspellid)) //learn passive skills when tamed, not sure if thats right
- ((Player*)owner)->learnSpell(learn_spellproto->Id);
- else
- AddTeachSpell(learn_spellproto->EffectTriggerSpell[0], learn_spellproto->Id);
- }
- }
- else
- petspellid = learn_spellproto->Id;
-
- addSpell(petspellid);
-
- SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
- SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
-
- for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
- {
- usedtrainpoints += _spell_idx->second->reqtrainpoints;
- break;
- }
- }
- }
-
- LearnPetPassives();
-
- CastPetAuras(false);
-
- SetTP(-usedtrainpoints);
-}
-
-void Pet::CheckLearning(uint32 spellid)
-{
- //charmed case -> prevent crash
- if(GetTypeId() == TYPEID_PLAYER || getPetType() != HUNTER_PET)
- return;
-
- Unit* owner = GetOwner();
-
- if(m_teachspells.empty() || !owner || owner->GetTypeId() != TYPEID_PLAYER)
- return;
-
- TeachSpellMap::iterator itr = m_teachspells.find(spellid);
- if(itr == m_teachspells.end())
- return;
-
- if(urand(0, 100) < 10)
- {
- ((Player*)owner)->learnSpell(itr->second);
- m_teachspells.erase(itr);
- }
-}
-
-uint32 Pet::resetTalentsCost() const
-{
- uint32 days = (sWorld.GetGameTime() - m_resetTalentsTime)/DAY;
-
- // The first time reset costs 10 silver; after 1 day cost is reset to 10 silver
- if(m_resetTalentsCost < 10*SILVER || days > 0)
- return 10*SILVER;
- // then 50 silver
- else if(m_resetTalentsCost < 50*SILVER)
- return 50*SILVER;
- // then 1 gold
- else if(m_resetTalentsCost < 1*GOLD)
- return 1*GOLD;
- // then increasing at a rate of 1 gold; cap 10 gold
- else
- return (m_resetTalentsCost + 1*GOLD > 10*GOLD ? 10*GOLD : m_resetTalentsCost + 1*GOLD);
-}
-
-void Pet::ToggleAutocast(uint32 spellid, bool apply)
-{
- if(IsPassiveSpell(spellid))
- return;
-
- PetSpellMap::const_iterator itr = m_spells.find((uint16)spellid);
-
- int i;
-
- if(apply)
- {
- for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++);
- if (i == m_autospells.size())
- {
- m_autospells.push_back(spellid);
- itr->second->active = ACT_ENABLED;
- itr->second->state = PETSPELL_CHANGED;
- }
- }
- else
- {
- AutoSpellList::iterator itr2 = m_autospells.begin();
- for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++, itr2++);
- if (i < m_autospells.size())
- {
- m_autospells.erase(itr2);
- itr->second->active = ACT_DISABLED;
- itr->second->state = PETSPELL_CHANGED;
- }
- }
-}
-
-bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number)
-{
- SetMapId(map->GetId());
- SetInstanceId(map->GetInstanceId());
-
- Object::_Create(guidlow, pet_number, HIGHGUID_PET);
-
- m_DBTableGuid = guidlow;
- m_originalEntry = Entry;
-
- if(!InitEntry(Entry))
- return false;
-
- SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
- SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
-
- if(getPetType() == MINI_PET) // always non-attackable
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
-
- return true;
-}
-
-bool Pet::HasSpell(uint32 spell) const
-{
- return (m_spells.find(spell) != m_spells.end());
-}
-
-// Get all passive spells in our skill line
-void Pet::LearnPetPassives()
-{
- CreatureInfo const* cInfo = GetCreatureInfo();
- if(!cInfo)
- return;
-
- CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
- if(!cFamily)
- return;
-
- PetFamilySpellsStore::const_iterator petStore = sPetFamilySpellsStore.find(cFamily->ID);
- if(petStore != sPetFamilySpellsStore.end())
- {
- for(PetFamilySpellsSet::const_iterator petSet = petStore->second.begin(); petSet != petStore->second.end(); ++petSet)
- addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, 0xffff, PETSPELL_FAMILY);
- }
-}
-
-void Pet::CastPetAuras(bool current)
-{
- Unit* owner = GetOwner();
- if(!owner)
- return;
-
- if(getPetType() != HUNTER_PET && (getPetType() != SUMMON_PET || owner->getClass() != CLASS_WARLOCK))
- return;
-
- for(PetAuraSet::iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); )
- {
- PetAura const* pa = *itr;
- ++itr;
-
- if(!current && pa->IsRemovedOnChangePet())
- owner->RemovePetAura(pa);
- else
- CastPetAura(pa);
- }
-}
-
-void Pet::CastPetAura(PetAura const* aura)
-{
- uint16 auraId = aura->GetAura(GetEntry());
- if(!auraId)
- return;
-
- if(auraId == 35696) // Demonic Knowledge
- {
- int32 basePoints = int32(aura->GetDamage() * (GetStat(STAT_STAMINA) + GetStat(STAT_INTELLECT)) / 100);
- CastCustomSpell(this,auraId,&basePoints, NULL, NULL, true );
- }
- else
- CastSpell(this, auraId, 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 "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "WorldSession.h"
+#include "WorldPacket.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Pet.h"
+#include "MapManager.h"
+#include "Formulas.h"
+#include "SpellAuras.h"
+#include "CreatureAI.h"
+#include "Unit.h"
+#include "Util.h"
+
+char const* petTypeSuffix[MAX_PET_TYPE] =
+{
+ "'s Minion", // SUMMON_PET
+ "'s Pet", // HUNTER_PET
+ "'s Guardian", // GUARDIAN_PET
+ "'s Companion" // MINI_PET
+};
+
+//numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy)
+uint32 const LevelUpLoyalty[6] =
+{
+ 5500,
+ 11500,
+ 17000,
+ 23500,
+ 31000,
+ 39500,
+};
+
+uint32 const LevelStartLoyalty[6] =
+{
+ 2000,
+ 4500,
+ 7000,
+ 10000,
+ 13500,
+ 17500,
+};
+
+Pet::Pet(PetType type) : Creature()
+{
+ m_isPet = true;
+ m_name = "Pet";
+ m_petType = type;
+
+ m_removed = false;
+ m_regenTimer = 4000;
+ m_happinessTimer = 7500;
+ m_loyaltyTimer = 12000;
+ m_duration = 0;
+ m_bonusdamage = 0;
+
+ m_loyaltyPoints = 0;
+ m_TrainingPoints = 0;
+ m_resetTalentsCost = 0;
+ m_resetTalentsTime = 0;
+
+ m_auraUpdateMask = 0;
+
+ // pets always have a charminfo, even if they are not actually charmed
+ CharmInfo* charmInfo = InitCharmInfo(this);
+
+ if(type == MINI_PET) // always passive
+ charmInfo->SetReactState(REACT_PASSIVE);
+ else if(type == GUARDIAN_PET) // always aggressive
+ charmInfo->SetReactState(REACT_AGGRESSIVE);
+
+ m_spells.clear();
+ m_Auras.clear();
+ m_CreatureSpellCooldowns.clear();
+ m_CreatureCategoryCooldowns.clear();
+ m_autospells.clear();
+ m_declinedname = NULL;
+}
+
+Pet::~Pet()
+{
+ if(m_uint32Values) // only for fully created Object
+ {
+ for (PetSpellMap::iterator i = m_spells.begin(); i != m_spells.end(); ++i)
+ delete i->second;
+ ObjectAccessor::Instance().RemoveObject(this);
+ }
+
+ delete m_declinedname;
+}
+
+void Pet::AddToWorld()
+{
+ ///- Register the pet for guid lookup
+ if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
+ Unit::AddToWorld();
+}
+
+void Pet::RemoveFromWorld()
+{
+ ///- Remove the pet from the accessor
+ if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
+ ///- Don't call the function for Creature, normal mobs + totems go in a different storage
+ Unit::RemoveFromWorld();
+}
+
+bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool current )
+{
+ uint32 ownerid = owner->GetGUIDLow();
+
+ QueryResult *result;
+
+ if(petnumber)
+ // known petnumber entry 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND id = '%u'",ownerid, petnumber);
+ else if(current)
+ // current pet (slot 0) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND slot = '0'",ownerid );
+ else if(petentry)
+ // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets)
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '0' OR slot = '3') ",ownerid, petentry );
+ else
+ // any current or other non-stabled pet (for hunter "call pet")
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3') ",ownerid);
+
+ if(!result)
+ return false;
+
+ Field *fields = result->Fetch();
+
+ // update for case of current pet "slot = 0"
+ petentry = fields[1].GetUInt32();
+ if(!petentry)
+ {
+ delete result;
+ return false;
+ }
+
+ uint32 summon_spell_id = fields[21].GetUInt32();
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id);
+
+ bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0;
+
+ // check temporary summoned pets like mage water elemental
+ if(current && is_temporary_summoned)
+ {
+ delete result;
+ return false;
+ }
+
+ Map *map = owner->GetMap();
+ uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
+ uint32 pet_number = fields[0].GetUInt32();
+ if(!Create(guid, map, petentry, pet_number))
+ {
+ delete result;
+ return false;
+ }
+
+ float px, py, pz;
+ owner->GetClosePoint(px, py, pz,GetObjectSize(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
+
+ Relocate(px, py, pz, owner->GetOrientation());
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
+ delete result;
+ return false;
+ }
+
+ setPetType(PetType(fields[22].GetUInt8()));
+ SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction());
+ SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id);
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ if(cinfo->type == CREATURE_TYPE_CRITTER)
+ {
+ AIM_Initialize();
+ map->Add((Creature*)this);
+ delete result;
+ return true;
+ }
+ if(getPetType()==HUNTER_PET || getPetType()==SUMMON_PET && cinfo->type == CREATURE_TYPE_DEMON && owner->getClass() == CLASS_WARLOCK)
+ m_charmInfo->SetPetNumber(pet_number, true);
+ else
+ m_charmInfo->SetPetNumber(pet_number, false);
+ SetUInt64Value(UNIT_FIELD_SUMMONEDBY, owner->GetGUID());
+ SetDisplayId(fields[3].GetUInt32());
+ SetNativeDisplayId(fields[3].GetUInt32());
+ uint32 petlevel=fields[4].GetUInt32();
+ SetUInt32Value(UNIT_NPC_FLAGS , 0);
+ SetName(fields[11].GetString());
+
+ switch(getPetType())
+ {
+
+ case SUMMON_PET:
+ petlevel=owner->getLevel();
+
+ SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+ // this enables popup window (pet dismiss, cancel)
+ break;
+ case HUNTER_PET:
+ SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
+ SetByteValue(UNIT_FIELD_BYTES_1, 1, fields[8].GetUInt32());
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
+
+ if(fields[12].GetBool())
+ SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED);
+ else
+ SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
+
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+ // this enables popup window (pet abandon, cancel)
+ SetTP(fields[9].GetInt32());
+ SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
+ SetPower( POWER_HAPPINESS,fields[15].GetUInt32());
+ setPowerType(POWER_FOCUS);
+ break;
+ default:
+ sLog.outError("Pet have incorrect type (%u) for pet loading.",getPetType());
+ }
+ InitStatsForLevel( petlevel);
+ SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32());
+ SetUInt64Value(UNIT_FIELD_CREATEDBY, owner->GetGUID());
+
+ m_charmInfo->SetReactState( ReactStates( fields[6].GetUInt8() ));
+ m_loyaltyPoints = fields[7].GetInt32();
+
+ uint32 savedhealth = fields[13].GetUInt32();
+ uint32 savedmana = fields[14].GetUInt32();
+
+ // set current pet as current
+ if(fields[10].GetUInt32() != 0)
+ {
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("UPDATE character_pet SET slot = '3' WHERE owner = '%u' AND slot = '0' AND id <> '%u'",ownerid, m_charmInfo->GetPetNumber());
+ CharacterDatabase.PExecute("UPDATE character_pet SET slot = '0' WHERE owner = '%u' AND id = '%u'",ownerid, m_charmInfo->GetPetNumber());
+ CharacterDatabase.CommitTransaction();
+ }
+
+ if(!is_temporary_summoned)
+ {
+ // permanent controlled pets store state in DB
+ Tokens tokens = StrSplit(fields[16].GetString(), " ");
+
+ if(tokens.size() != 20)
+ {
+ delete result;
+ return false;
+ }
+
+ int index;
+ Tokens::iterator iter;
+ for(iter = tokens.begin(), index = 0; index < 10; ++iter, ++index )
+ {
+ m_charmInfo->GetActionBarEntry(index)->Type = atol((*iter).c_str());
+ ++iter;
+ m_charmInfo->GetActionBarEntry(index)->SpellOrAction = atol((*iter).c_str());
+ }
+
+ //init teach spells
+ tokens = StrSplit(fields[17].GetString(), " ");
+ for (iter = tokens.begin(), index = 0; index < 4; ++iter, ++index)
+ {
+ uint32 tmp = atol((*iter).c_str());
+
+ ++iter;
+
+ if(tmp)
+ AddTeachSpell(tmp, atol((*iter).c_str()));
+ else
+ break;
+ }
+ }
+
+ // since last save (in seconds)
+ uint32 timediff = (time(NULL) - fields[18].GetUInt32());
+
+ delete result;
+
+ //load spells/cooldowns/auras
+ SetCanModifyStats(true);
+ _LoadAuras(timediff);
+
+ //init AB
+ if(is_temporary_summoned)
+ {
+ // Temporary summoned pets always have initial spell list at load
+ InitPetCreateSpells();
+ }
+ else
+ {
+ LearnPetPassives();
+ CastPetAuras(current);
+ }
+
+ if(getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current
+ {
+ SetHealth(GetMaxHealth());
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
+ }
+ else
+ {
+ SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth);
+ SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana);
+ }
+
+ AIM_Initialize();
+ map->Add((Creature*)this);
+
+ // Spells should be loaded after pet is added to map, because in CanCast is check on it
+ _LoadSpells();
+ _LoadSpellCooldowns();
+
+ owner->SetPet(this); // in DB stored only full controlled creature
+ sLog.outDebug("New Pet has guid %u", GetGUIDLow());
+
+ if(owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)owner)->PetSpellInitialize();
+ if(((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_PET);
+ }
+
+ if(owner->GetTypeId() == TYPEID_PLAYER && getPetType() == HUNTER_PET)
+ {
+ result = CharacterDatabase.PQuery("SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'",owner->GetGUIDLow(),GetCharmInfo()->GetPetNumber());
+
+ if(result)
+ {
+ if(m_declinedname)
+ delete m_declinedname;
+
+ m_declinedname = new DeclinedName;
+ Field *fields = result->Fetch();
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ {
+ m_declinedname->name[i] = fields[i].GetCppString();
+ }
+ }
+ }
+
+ return true;
+}
+
+void Pet::SavePetToDB(PetSaveMode mode)
+{
+ if(!GetEntry())
+ return;
+
+ // save only fully controlled creature
+ if(!isControlled())
+ return;
+
+ uint32 curhealth = GetHealth();
+ uint32 curmana = GetPower(POWER_MANA);
+
+ switch(mode)
+ {
+ case PET_SAVE_IN_STABLE_SLOT_1:
+ case PET_SAVE_IN_STABLE_SLOT_2:
+ case PET_SAVE_NOT_IN_SLOT:
+ {
+ RemoveAllAuras();
+
+ //only alive hunter pets get auras saved, the others don't
+ if(!(getPetType() == HUNTER_PET && isAlive()))
+ m_Auras.clear();
+ }
+ default:
+ break;
+ }
+
+ _SaveSpells();
+ _SaveSpellCooldowns();
+ _SaveAuras();
+
+ switch(mode)
+ {
+ case PET_SAVE_AS_CURRENT:
+ case PET_SAVE_IN_STABLE_SLOT_1:
+ case PET_SAVE_IN_STABLE_SLOT_2:
+ case PET_SAVE_NOT_IN_SLOT:
+ {
+ uint32 loyalty =1;
+ if(getPetType()!=HUNTER_PET)
+ loyalty = GetLoyaltyLevel();
+
+ uint32 owner = GUID_LOPART(GetOwnerGUID());
+ std::string name = m_name;
+ CharacterDatabase.escape_string(name);
+ CharacterDatabase.BeginTransaction();
+ // remove current data
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner,m_charmInfo->GetPetNumber() );
+
+ // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
+ if(mode!=PET_SAVE_NOT_IN_SLOT)
+ CharacterDatabase.PExecute("UPDATE character_pet SET slot = 3 WHERE owner = '%u' AND slot = '%u'", owner, uint32(mode) );
+
+ // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
+ if(getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode==PET_SAVE_NOT_IN_SLOT))
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3')", owner );
+ // save pet
+ std::ostringstream ss;
+ ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata,TeachSpelldata,savetime,resettalents_cost,resettalents_time,CreatedBySpell,PetType) "
+ << "VALUES ("
+ << m_charmInfo->GetPetNumber() << ", "
+ << GetEntry() << ", "
+ << owner << ", "
+ << GetNativeDisplayId() << ", "
+ << getLevel() << ", "
+ << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", "
+ << uint32(m_charmInfo->GetReactState()) << ", "
+ << m_loyaltyPoints << ", "
+ << GetLoyaltyLevel() << ", "
+ << m_TrainingPoints << ", "
+ << uint32(mode) << ", '"
+ << name.c_str() << "', "
+ << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", "
+ << (curhealth<1?1:curhealth) << ", "
+ << curmana << ", "
+ << GetPower(POWER_HAPPINESS) << ", '";
+
+ for(uint32 i = 0; i < 10; i++)
+ ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " ";
+ ss << "', '";
+
+ //save spells the pet can teach to it's Master
+ {
+ int i = 0;
+ for(TeachSpellMap::iterator itr = m_teachspells.begin(); i < 4 && itr != m_teachspells.end(); ++i, ++itr)
+ ss << itr->first << " " << itr->second << " ";
+ for(; i < 4; ++i)
+ ss << uint32(0) << " " << uint32(0) << " ";
+ }
+
+ ss << "', "
+ << time(NULL) << ", "
+ << uint32(m_resetTalentsCost) << ", "
+ << uint64(m_resetTalentsTime) << ", "
+ << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", "
+ << uint32(getPetType()) << ")";
+
+ CharacterDatabase.Execute( ss.str().c_str() );
+
+ CharacterDatabase.CommitTransaction();
+ break;
+ }
+ case PET_SAVE_AS_DELETED:
+ {
+ RemoveAllAuras();
+ uint32 owner = GUID_LOPART(GetOwnerGUID());
+ DeleteFromDB(m_charmInfo->GetPetNumber());
+ break;
+ }
+ default:
+ sLog.outError("Unknown pet save/remove mode: %d",mode);
+ }
+}
+
+void Pet::DeleteFromDB(uint32 guidlow)
+{
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE id = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", guidlow);
+}
+
+void Pet::setDeathState(DeathState s) // overwrite virtual Creature::setDeathState and Unit::setDeathState
+{
+ Creature::setDeathState(s);
+ if(getDeathState()==CORPSE)
+ {
+ //remove summoned pet (no corpse)
+ if(getPetType()==SUMMON_PET)
+ Remove(PET_SAVE_NOT_IN_SLOT);
+ // other will despawn at corpse desppawning (Pet::Update code)
+ else
+ {
+ // pet corpse non lootable and non skinnable
+ SetUInt32Value( UNIT_DYNAMIC_FLAGS, 0x00 );
+ RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+
+ //lose happiness when died and not in BG/Arena
+ MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId());
+ if(!mapEntry || (mapEntry->map_type != MAP_ARENA && mapEntry->map_type != MAP_BATTLEGROUND))
+ ModifyPower(POWER_HAPPINESS, -HAPPINESS_LEVEL_SIZE);
+
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ }
+ }
+ else if(getDeathState()==ALIVE)
+ {
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ CastPetAuras(true);
+ }
+}
+
+void Pet::Update(uint32 diff)
+{
+ if(m_removed) // pet already removed, just wait in remove queue, no updates
+ return;
+
+ switch( m_deathState )
+ {
+ case CORPSE:
+ {
+ if( m_deathTimer <= diff )
+ {
+ assert(getPetType()!=SUMMON_PET && "Must be already removed.");
+ Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER!
+ return;
+ }
+ break;
+ }
+ case ALIVE:
+ {
+ // unsummon pet that lost owner
+ Unit* owner = GetOwner();
+ if(!owner || !IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) || isControlled() && !owner->GetPetGUID())
+ {
+ Remove(PET_SAVE_NOT_IN_SLOT, true);
+ return;
+ }
+
+ if(isControlled())
+ {
+ if( owner->GetPetGUID() != GetGUID() )
+ {
+ Remove(getPetType()==HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
+ return;
+ }
+ }
+
+ if(m_duration > 0)
+ {
+ if(m_duration > diff)
+ m_duration -= diff;
+ else
+ {
+ Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
+ return;
+ }
+ }
+
+ if(getPetType() != HUNTER_PET)
+ break;
+
+ //regenerate Focus
+ if(m_regenTimer <= diff)
+ {
+ RegenerateFocus();
+ m_regenTimer = 4000;
+ }
+ else
+ m_regenTimer -= diff;
+
+ if(m_happinessTimer <= diff)
+ {
+ LooseHappiness();
+ m_happinessTimer = 7500;
+ }
+ else
+ m_happinessTimer -= diff;
+
+ if(m_loyaltyTimer <= diff)
+ {
+ TickLoyaltyChange();
+ m_loyaltyTimer = 12000;
+ }
+ else
+ m_loyaltyTimer -= diff;
+
+ break;
+ }
+ default:
+ break;
+ }
+ Creature::Update(diff);
+}
+
+void Pet::RegenerateFocus()
+{
+ uint32 curValue = GetPower(POWER_FOCUS);
+ uint32 maxValue = GetMaxPower(POWER_FOCUS);
+
+ if (curValue >= maxValue)
+ return;
+
+ float addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS);
+
+ AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
+ for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue == POWER_FOCUS)
+ addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f;
+
+ ModifyPower(POWER_FOCUS, (int32)addvalue);
+}
+
+void Pet::LooseHappiness()
+{
+ uint32 curValue = GetPower(POWER_HAPPINESS);
+ if (curValue <= 0)
+ return;
+ int32 addvalue = (140 >> GetLoyaltyLevel()) * 125; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs)
+ if(isInCombat()) //we know in combat happiness fades faster, multiplier guess
+ addvalue = int32(addvalue * 1.5);
+ ModifyPower(POWER_HAPPINESS, -addvalue);
+}
+
+void Pet::ModifyLoyalty(int32 addvalue)
+{
+ uint32 loyaltylevel = GetLoyaltyLevel();
+
+ if(addvalue > 0) //only gain influenced, not loss
+ addvalue = int32((float)addvalue * sWorld.getRate(RATE_LOYALTY));
+
+ if(loyaltylevel >= BEST_FRIEND && (addvalue + m_loyaltyPoints) > int32(GetMaxLoyaltyPoints(loyaltylevel)))
+ return;
+
+ m_loyaltyPoints += addvalue;
+
+ if(m_loyaltyPoints < 0)
+ {
+ if(loyaltylevel > REBELLIOUS)
+ {
+ //level down
+ --loyaltylevel;
+ SetLoyaltyLevel(LoyaltyLevel(loyaltylevel));
+ m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel);
+ SetTP(m_TrainingPoints - int32(getLevel()));
+ }
+ else
+ {
+ m_loyaltyPoints = 0;
+ Unit* owner = GetOwner();
+ if(owner && owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_PET_BROKEN, 0);
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+
+ //run away
+ ((Player*)owner)->RemovePet(this,PET_SAVE_AS_DELETED);
+ }
+ }
+ }
+ //level up
+ else if(m_loyaltyPoints > int32(GetMaxLoyaltyPoints(loyaltylevel)))
+ {
+ ++loyaltylevel;
+ SetLoyaltyLevel(LoyaltyLevel(loyaltylevel));
+ m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel);
+ SetTP(m_TrainingPoints + getLevel());
+ }
+}
+
+void Pet::TickLoyaltyChange()
+{
+ int32 addvalue;
+
+ switch(GetHappinessState())
+ {
+ case HAPPY: addvalue = 20; break;
+ case CONTENT: addvalue = 10; break;
+ case UNHAPPY: addvalue = -20; break;
+ default:
+ return;
+ }
+ ModifyLoyalty(addvalue);
+}
+
+void Pet::KillLoyaltyBonus(uint32 level)
+{
+ if(level > 100)
+ return;
+
+ //at lower levels gain is faster | the lower loyalty the more loyalty is gained
+ uint32 bonus = uint32(((100 - level) / 10) + (6 - GetLoyaltyLevel()));
+ ModifyLoyalty(bonus);
+}
+
+HappinessState Pet::GetHappinessState()
+{
+ if(GetPower(POWER_HAPPINESS) < HAPPINESS_LEVEL_SIZE)
+ return UNHAPPY;
+ else if(GetPower(POWER_HAPPINESS) >= HAPPINESS_LEVEL_SIZE * 2)
+ return HAPPY;
+ else
+ return CONTENT;
+}
+
+void Pet::SetLoyaltyLevel(LoyaltyLevel level)
+{
+ SetByteValue(UNIT_FIELD_BYTES_1, 1, level);
+}
+
+bool Pet::CanTakeMoreActiveSpells(uint32 spellid)
+{
+ uint8 activecount = 1;
+ uint32 chainstartstore[ACTIVE_SPELLS_MAX];
+
+ if(IsPassiveSpell(spellid))
+ return true;
+
+ chainstartstore[0] = spellmgr.GetFirstSpellInChain(spellid);
+
+ for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(IsPassiveSpell(itr->first))
+ continue;
+
+ uint32 chainstart = spellmgr.GetFirstSpellInChain(itr->first);
+
+ uint8 x;
+
+ for(x = 0; x < activecount; x++)
+ {
+ if(chainstart == chainstartstore[x])
+ break;
+ }
+
+ if(x == activecount) //spellchain not yet saved -> add active count
+ {
+ ++activecount;
+ if(activecount > ACTIVE_SPELLS_MAX)
+ return false;
+ chainstartstore[x] = chainstart;
+ }
+ }
+ return true;
+}
+
+bool Pet::HasTPForSpell(uint32 spellid)
+{
+ int32 neededtrainp = GetTPForSpell(spellid);
+ if((m_TrainingPoints - neededtrainp < 0 || neededtrainp < 0) && neededtrainp != 0)
+ return false;
+ return true;
+}
+
+int32 Pet::GetTPForSpell(uint32 spellid)
+{
+ uint32 basetrainp = 0;
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid);
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ if(!_spell_idx->second->reqtrainpoints)
+ return 0;
+
+ basetrainp = _spell_idx->second->reqtrainpoints;
+ break;
+ }
+
+ uint32 spenttrainp = 0;
+ uint32 chainstart = spellmgr.GetFirstSpellInChain(spellid);
+
+ for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PETSPELL_REMOVED)
+ continue;
+
+ if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
+ {
+ SkillLineAbilityMap::const_iterator _lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first);
+ SkillLineAbilityMap::const_iterator _upper = spellmgr.GetEndSkillLineAbilityMap(itr->first);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx2 = _lower; _spell_idx2 != _upper; ++_spell_idx2)
+ {
+ if(_spell_idx2->second->reqtrainpoints > spenttrainp)
+ {
+ spenttrainp = _spell_idx2->second->reqtrainpoints;
+ break;
+ }
+ }
+ }
+ }
+
+ return int32(basetrainp) - int32(spenttrainp);
+}
+
+uint32 Pet::GetMaxLoyaltyPoints(uint32 level)
+{
+ return LevelUpLoyalty[level - 1];
+}
+
+uint32 Pet::GetStartLoyaltyPoints(uint32 level)
+{
+ return LevelStartLoyalty[level - 1];
+}
+
+void Pet::SetTP(int32 TP)
+{
+ m_TrainingPoints = TP;
+ SetUInt32Value(UNIT_TRAINING_POINTS, (uint32)GetDispTP());
+}
+
+int32 Pet::GetDispTP()
+{
+ if(getPetType()!= HUNTER_PET)
+ return(0);
+ if(m_TrainingPoints < 0)
+ return -m_TrainingPoints;
+ else
+ return -(m_TrainingPoints + 1);
+}
+
+void Pet::Remove(PetSaveMode mode, bool returnreagent)
+{
+ Unit* owner = GetOwner();
+
+ if(owner)
+ {
+ if(owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ ((Player*)owner)->RemovePet(this,mode,returnreagent);
+ return;
+ }
+
+ // only if current pet in slot
+ if(owner->GetPetGUID()==GetGUID())
+ owner->SetPet(0);
+ }
+
+ CleanupsBeforeDelete();
+ AddObjectToRemoveList();
+ m_removed = true;
+}
+
+void Pet::GivePetXP(uint32 xp)
+{
+ if(getPetType() != HUNTER_PET)
+ return;
+
+ if ( xp < 1 )
+ return;
+
+ if(!isAlive())
+ return;
+
+ uint32 level = getLevel();
+
+ // XP to money conversion processed in Player::RewardQuest
+ if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ return;
+
+ uint32 curXP = GetUInt32Value(UNIT_FIELD_PETEXPERIENCE);
+ uint32 nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
+ uint32 newXP = curXP + xp;
+
+ if(newXP >= nextLvlXP && level+1 > GetOwner()->getLevel())
+ {
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, nextLvlXP-1);
+ return;
+ }
+
+ while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
+ {
+ newXP -= nextLvlXP;
+
+ SetLevel( level + 1 );
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(level+1))/4));
+
+ level = getLevel();
+ nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
+ GivePetLevel(level);
+ }
+
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, newXP);
+
+ if(getPetType() == HUNTER_PET)
+ KillLoyaltyBonus(level);
+}
+
+void Pet::GivePetLevel(uint32 level)
+{
+ if(!level)
+ return;
+
+ InitStatsForLevel( level);
+
+ SetTP(m_TrainingPoints + (GetLoyaltyLevel() - 1));
+}
+
+bool Pet::CreateBaseAtCreature(Creature* creature)
+{
+ if(!creature)
+ {
+ sLog.outError("CRITICAL ERROR: NULL pointer parsed into CreateBaseAtCreature()");
+ return false;
+ }
+ uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
+
+ sLog.outBasic("SetInstanceID()");
+ SetInstanceId(creature->GetInstanceId());
+
+ sLog.outBasic("Create pet");
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!Create(guid, creature->GetMap(), creature->GetEntry(), pet_number))
+ return false;
+
+ Relocate(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation());
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
+ return false;
+ }
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ if(!cinfo)
+ {
+ sLog.outError("ERROR: CreateBaseAtCreature() failed, creatureInfo is missing!");
+ return false;
+ }
+
+ if(cinfo->type == CREATURE_TYPE_CRITTER)
+ {
+ setPetType(MINI_PET);
+ return true;
+ }
+ SetDisplayId(creature->GetDisplayId());
+ SetNativeDisplayId(creature->GetNativeDisplayId());
+ SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
+ SetPower( POWER_HAPPINESS,166500);
+ setPowerType(POWER_FOCUS);
+ SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(creature->getLevel()))/4));
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+ SetUInt32Value(UNIT_NPC_FLAGS , 0);
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(creature->GetCreatureInfo()->family);
+ if( char* familyname = cFamily->Name[sWorld.GetDefaultDbcLocale()] )
+ SetName(familyname);
+ else
+ SetName(creature->GetName());
+
+ m_loyaltyPoints = 1000;
+ if(cinfo->type == CREATURE_TYPE_BEAST)
+ {
+ SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
+ SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
+
+ SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED) );
+ SetLoyaltyLevel(REBELLIOUS);
+ }
+ return true;
+}
+
+bool Pet::InitStatsForLevel(uint32 petlevel)
+{
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ assert(cinfo);
+
+ Unit* owner = GetOwner();
+ if(!owner)
+ {
+ sLog.outError("ERROR: attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo->Entry);
+ return false;
+ }
+
+ uint32 creature_ID = (getPetType() == HUNTER_PET) ? 1 : cinfo->Entry;
+
+ SetLevel( petlevel);
+
+ SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
+
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel*50));
+
+ SetAttackTime(BASE_ATTACK, BASE_ATTACK_TIME);
+ SetAttackTime(OFF_ATTACK, BASE_ATTACK_TIME);
+ SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
+
+ SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0);
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family);
+ if(cFamily && cFamily->minScale > 0.0f)
+ {
+ float scale;
+ if (getLevel() >= cFamily->maxScaleLevel)
+ scale = cFamily->maxScale;
+ else if (getLevel() <= cFamily->minScaleLevel)
+ scale = cFamily->minScale;
+ else
+ scale = cFamily->minScale + (getLevel() - cFamily->minScaleLevel) / cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale);
+
+ SetFloatValue(OBJECT_FIELD_SCALE_X, scale);
+ }
+ m_bonusdamage = 0;
+
+ int32 createResistance[MAX_SPELL_SCHOOL] = {0,0,0,0,0,0,0};
+
+ if(cinfo && getPetType() != HUNTER_PET)
+ {
+ createResistance[SPELL_SCHOOL_HOLY] = cinfo->resistance1;
+ createResistance[SPELL_SCHOOL_FIRE] = cinfo->resistance2;
+ createResistance[SPELL_SCHOOL_NATURE] = cinfo->resistance3;
+ createResistance[SPELL_SCHOOL_FROST] = cinfo->resistance4;
+ createResistance[SPELL_SCHOOL_SHADOW] = cinfo->resistance5;
+ createResistance[SPELL_SCHOOL_ARCANE] = cinfo->resistance6;
+ }
+
+ switch(getPetType())
+ {
+ case SUMMON_PET:
+ {
+ if(owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ switch(owner->getClass())
+ {
+ case CLASS_WARLOCK:
+ {
+
+ //the damage bonus used for pets is either fire or shadow damage, whatever is higher
+ uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE);
+ uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW);
+ uint32 val = (fire > shadow) ? fire : shadow;
+
+ SetBonusDamage(int32 (val * 0.15f));
+ //bonusAP += val * 0.57;
+ break;
+ }
+ case CLASS_MAGE:
+ {
+ //40% damage bonus of mage's frost damage
+ float val = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4;
+ if(val < 0)
+ val = 0;
+ SetBonusDamage( int32(val));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
+
+ //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
+
+ PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
+ if(pInfo) // exist in DB
+ {
+ SetCreateHealth(pInfo->health);
+ SetCreateMana(pInfo->mana);
+
+ if(pInfo->armor > 0)
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
+
+ for(int stat = 0; stat < MAX_STATS; ++stat)
+ {
+ SetCreateStat(Stats(stat),float(pInfo->stats[stat]));
+ }
+ }
+ else // not exist in DB, use some default fake data
+ {
+ sLog.outErrorDb("Summoned pet (Entry: %u) not have pet stats data in DB",cinfo->Entry);
+
+ // remove elite bonuses included in DB values
+ SetCreateHealth(uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
+ SetCreateMana( uint32(((float(cinfo->maxmana) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
+
+ SetCreateStat(STAT_STRENGTH,22);
+ SetCreateStat(STAT_AGILITY,22);
+ SetCreateStat(STAT_STAMINA,25);
+ SetCreateStat(STAT_INTELLECT,28);
+ SetCreateStat(STAT_SPIRIT,27);
+ }
+ break;
+ }
+ case HUNTER_PET:
+ {
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(petlevel))/4));
+
+ //these formula may not be correct; however, it is designed to be close to what it should be
+ //this makes dps 0.5 of pets level
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
+ //damage range is then petlevel / 2
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
+ //damage is increased afterwards as strength and pet scaling modify attack power
+
+ //stored standard pet stats are entry 1 in pet_levelinfo
+ PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
+ if(pInfo) // exist in DB
+ {
+ SetCreateHealth(pInfo->health);
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
+ //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
+
+ for( int i = STAT_STRENGTH; i < MAX_STATS; i++)
+ {
+ SetCreateStat(Stats(i), float(pInfo->stats[i]));
+ }
+ }
+ else // not exist in DB, use some default fake data
+ {
+ sLog.outErrorDb("Hunter pet levelstats missing in DB");
+
+ // remove elite bonuses included in DB values
+ SetCreateHealth( uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
+
+ SetCreateStat(STAT_STRENGTH,22);
+ SetCreateStat(STAT_AGILITY,22);
+ SetCreateStat(STAT_STAMINA,25);
+ SetCreateStat(STAT_INTELLECT,28);
+ SetCreateStat(STAT_SPIRIT,27);
+ }
+ break;
+ }
+ case GUARDIAN_PET:
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
+
+ SetCreateMana( 28 + 10*petlevel );
+ SetCreateHealth( 28 + 30*petlevel );
+
+ // FIXME: this is wrong formula, possible each guardian pet have own damage formula
+ //these formula may not be correct; however, it is designed to be close to what it should be
+ //this makes dps 0.5 of pets level
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
+ //damage range is then petlevel / 2
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
+ break;
+ default:
+ sLog.outError("Pet have incorrect type (%u) for levelup.",getPetType()); break;
+ }
+
+ for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
+ SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i]) );
+
+ UpdateAllStats();
+
+ SetHealth(GetMaxHealth());
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
+
+ return true;
+}
+
+bool Pet::HaveInDiet(ItemPrototype const* item) const
+{
+ if (!item->FoodType)
+ return false;
+
+ CreatureInfo const* cInfo = GetCreatureInfo();
+ if(!cInfo)
+ return false;
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
+ if(!cFamily)
+ return false;
+
+ uint32 diet = cFamily->petFoodMask;
+ uint32 FoodMask = 1 << (item->FoodType-1);
+ return diet & FoodMask;
+}
+
+uint32 Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel)
+{
+ // -5 or greater food level
+ if(getLevel() <= itemlevel +5) //possible to feed level 60 pet with level 55 level food for full effect
+ return 35000;
+ // -10..-6
+ else if(getLevel() <= itemlevel + 10) //pure guess, but sounds good
+ return 17000;
+ // -14..-11
+ else if(getLevel() <= itemlevel + 14) //level 55 food gets green on 70, makes sense to me
+ return 8000;
+ // -15 or less
+ else
+ return 0; //food too low level
+}
+
+void Pet::_LoadSpellCooldowns()
+{
+ m_CreatureSpellCooldowns.clear();
+ m_CreatureCategoryCooldowns.clear();
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT spell,time FROM pet_spell_cooldown WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ if(result)
+ {
+ time_t curTime = time(NULL);
+
+ WorldPacket data(SMSG_SPELL_COOLDOWN, (8+1+result->GetRowCount()*8));
+ data << GetGUID();
+ data << uint8(0x0);
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 spell_id = fields[0].GetUInt32();
+ time_t db_time = (time_t)fields[1].GetUInt64();
+
+ if(!sSpellStore.LookupEntry(spell_id))
+ {
+ sLog.outError("Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping.",m_charmInfo->GetPetNumber(),spell_id);
+ continue;
+ }
+
+ // skip outdated cooldown
+ if(db_time <= curTime)
+ continue;
+
+ data << uint32(spell_id);
+ data << uint32(uint32(db_time-curTime)*1000); // in m.secs
+
+ _AddCreatureSpellCooldown(spell_id,db_time);
+
+ sLog.outDebug("Pet (Number: %u) spell %u cooldown loaded (%u secs).",m_charmInfo->GetPetNumber(),spell_id,uint32(db_time-curTime));
+ }
+ while( result->NextRow() );
+
+ delete result;
+
+ if(!m_CreatureSpellCooldowns.empty() && GetOwner())
+ {
+ ((Player*)GetOwner())->GetSession()->SendPacket(&data);
+ }
+ }
+}
+
+void Pet::_SaveSpellCooldowns()
+{
+ CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", m_charmInfo->GetPetNumber());
+
+ time_t curTime = time(NULL);
+
+ // remove oudated and save active
+ for(CreatureSpellCooldowns::iterator itr = m_CreatureSpellCooldowns.begin();itr != m_CreatureSpellCooldowns.end();)
+ {
+ if(itr->second <= curTime)
+ m_CreatureSpellCooldowns.erase(itr++);
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" I64FMTD "')", m_charmInfo->GetPetNumber(), itr->first, uint64(itr->second));
+ ++itr;
+ }
+ }
+}
+
+void Pet::_LoadSpells()
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ addSpell(fields[0].GetUInt16(), fields[2].GetUInt16(), PETSPELL_UNCHANGED, fields[1].GetUInt16());
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Pet::_SaveSpells()
+{
+ for (PetSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
+ {
+ ++next;
+ if (itr->second->type == PETSPELL_FAMILY) continue; // prevent saving family passives to DB
+ if (itr->second->state == PETSPELL_REMOVED || itr->second->state == PETSPELL_CHANGED)
+ CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first);
+ if (itr->second->state == PETSPELL_NEW || itr->second->state == PETSPELL_CHANGED)
+ CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,slot,active) VALUES ('%u', '%u', '%u','%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second->slotId,itr->second->active);
+
+ if (itr->second->state == PETSPELL_REMOVED)
+ _removeSpell(itr->first);
+ else
+ itr->second->state = PETSPELL_UNCHANGED;
+ }
+}
+
+void Pet::_LoadAuras(uint32 timediff)
+{
+ m_Auras.clear();
+ for (int i = 0; i < TOTAL_AURAS; i++)
+ m_modAuras[i].clear();
+
+ // all aura related fields
+ for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i)
+ SetUInt32Value(i, 0);
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint64 caster_guid = fields[0].GetUInt64();
+ uint32 spellid = fields[1].GetUInt32();
+ uint32 effindex = fields[2].GetUInt32();
+ int32 damage = (int32)fields[3].GetUInt32();
+ int32 maxduration = (int32)fields[4].GetUInt32();
+ int32 remaintime = (int32)fields[5].GetUInt32();
+ int32 remaincharges = (int32)fields[6].GetUInt32();
+
+ SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
+ if(!spellproto)
+ {
+ sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex);
+ continue;
+ }
+
+ if(effindex >= 3)
+ {
+ sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex);
+ continue;
+ }
+
+ // negative effects should continue counting down after logout
+ if (remaintime != -1 && !IsPositiveEffect(spellid, effindex))
+ {
+ if(remaintime <= int32(timediff))
+ continue;
+
+ remaintime -= timediff;
+ }
+
+ // prevent wrong values of remaincharges
+ if(spellproto->procCharges)
+ {
+ if(remaincharges <= 0 || remaincharges > spellproto->procCharges)
+ remaincharges = spellproto->procCharges;
+ }
+ else
+ remaincharges = -1;
+
+ Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL);
+
+ if(!damage)
+ damage = aura->GetModifier()->m_amount;
+ aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges);
+ AddAura(aura);
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Pet::_SaveAuras()
+{
+ CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ AuraMap const& auras = GetAuras();
+ for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras.
+ SpellEntry const *spellInfo = itr->second->GetSpellProto();
+ uint8 i;
+ for (i = 0; i < 3; i++)
+ if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH ||
+ spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER ||
+ spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PET )
+ break;
+
+ if (i == 3 && !itr->second->IsPassive())
+ CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges) "
+ "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d')",
+ m_charmInfo->GetPetNumber(), itr->second->GetCasterGUID(),(uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex(),(*itr).second->GetModifier()->m_amount,int((*itr).second->GetAuraMaxDuration()),int((*itr).second->GetAuraDuration()),int((*itr).second->m_procCharges));
+ }
+}
+
+bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 slot_id, PetSpellType type)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+ if (!spellInfo)
+ {
+ // do pet spell book cleanup
+ if(state == PETSPELL_UNCHANGED) // spell load case
+ {
+ sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.",spell_id);
+ CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE spell = '%u'",spell_id);
+ }
+ else
+ sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
+
+ return false;
+ }
+
+ PetSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr != m_spells.end())
+ {
+ if (itr->second->state == PETSPELL_REMOVED)
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ state = PETSPELL_CHANGED;
+ }
+ else if (state == PETSPELL_UNCHANGED && itr->second->state != PETSPELL_UNCHANGED)
+ {
+ // can be in case spell loading but learned at some previous spell loading
+ itr->second->state = PETSPELL_UNCHANGED;
+ return false;
+ }
+ else
+ return false;
+ }
+
+ uint32 oldspell_id = 0;
+
+ PetSpell *newspell = new PetSpell;
+ newspell->state = state;
+ newspell->type = type;
+
+ if(active == ACT_DECIDE) //active was not used before, so we save it's autocast/passive state here
+ {
+ if(IsPassiveSpell(spell_id))
+ newspell->active = ACT_PASSIVE;
+ else
+ newspell->active = ACT_DISABLED;
+ }
+ else
+ newspell->active = active;
+
+ uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id);
+
+ for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); itr++)
+ {
+ if(itr->second->state == PETSPELL_REMOVED) continue;
+
+ if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
+ {
+ slot_id = itr->second->slotId;
+ newspell->active = itr->second->active;
+
+ if(newspell->active == ACT_ENABLED)
+ ToggleAutocast(itr->first, false);
+
+ oldspell_id = itr->first;
+ removeSpell(itr->first);
+ }
+ }
+
+ uint16 tmpslot=slot_id;
+
+ if (tmpslot == 0xffff)
+ {
+ uint16 maxid = 0;
+ PetSpellMap::iterator itr;
+ for (itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PETSPELL_REMOVED) continue;
+ if (itr->second->slotId > maxid) maxid = itr->second->slotId;
+ }
+ tmpslot = maxid + 1;
+ }
+
+ newspell->slotId = tmpslot;
+ m_spells[spell_id] = newspell;
+
+ if (IsPassiveSpell(spell_id))
+ CastSpell(this, spell_id, true);
+ else if(state == PETSPELL_NEW)
+ m_charmInfo->AddSpellToAB(oldspell_id, spell_id);
+
+ if(newspell->active == ACT_ENABLED)
+ ToggleAutocast(spell_id, true);
+
+ return true;
+}
+
+bool Pet::learnSpell(uint16 spell_id)
+{
+ // prevent duplicated entires in spell book
+ if (!addSpell(spell_id))
+ return false;
+
+ Unit* owner = GetOwner();
+ if(owner->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)owner)->PetSpellInitialize();
+ return true;
+}
+
+void Pet::removeSpell(uint16 spell_id)
+{
+ PetSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr == m_spells.end())
+ return;
+
+ if(itr->second->state == PETSPELL_REMOVED)
+ return;
+
+ if(itr->second->state == PETSPELL_NEW)
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ }
+ else
+ itr->second->state = PETSPELL_REMOVED;
+
+ RemoveAurasDueToSpell(spell_id);
+}
+
+bool Pet::_removeSpell(uint16 spell_id)
+{
+ PetSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr != m_spells.end())
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ return true;
+ }
+ return false;
+}
+
+void Pet::InitPetCreateSpells()
+{
+ m_charmInfo->InitPetActionBar();
+
+ m_spells.clear();
+ int32 usedtrainpoints = 0, petspellid;
+ PetCreateSpellEntry const* CreateSpells = objmgr.GetPetCreateSpellEntry(GetEntry());
+ if(CreateSpells)
+ {
+ for(uint8 i = 0; i < 4; i++)
+ {
+ if(!CreateSpells->spellid[i])
+ break;
+
+ SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(CreateSpells->spellid[i]);
+ if(!learn_spellproto)
+ continue;
+
+ if(learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_SPELL || learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_PET_SPELL)
+ {
+ petspellid = learn_spellproto->EffectTriggerSpell[0];
+ Unit* owner = GetOwner();
+ if(owner->GetTypeId() == TYPEID_PLAYER && !((Player*)owner)->HasSpell(learn_spellproto->Id))
+ {
+ if(IsPassiveSpell(petspellid)) //learn passive skills when tamed, not sure if thats right
+ ((Player*)owner)->learnSpell(learn_spellproto->Id);
+ else
+ AddTeachSpell(learn_spellproto->EffectTriggerSpell[0], learn_spellproto->Id);
+ }
+ }
+ else
+ petspellid = learn_spellproto->Id;
+
+ addSpell(petspellid);
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ usedtrainpoints += _spell_idx->second->reqtrainpoints;
+ break;
+ }
+ }
+ }
+
+ LearnPetPassives();
+
+ CastPetAuras(false);
+
+ SetTP(-usedtrainpoints);
+}
+
+void Pet::CheckLearning(uint32 spellid)
+{
+ //charmed case -> prevent crash
+ if(GetTypeId() == TYPEID_PLAYER || getPetType() != HUNTER_PET)
+ return;
+
+ Unit* owner = GetOwner();
+
+ if(m_teachspells.empty() || !owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ TeachSpellMap::iterator itr = m_teachspells.find(spellid);
+ if(itr == m_teachspells.end())
+ return;
+
+ if(urand(0, 100) < 10)
+ {
+ ((Player*)owner)->learnSpell(itr->second);
+ m_teachspells.erase(itr);
+ }
+}
+
+uint32 Pet::resetTalentsCost() const
+{
+ uint32 days = (sWorld.GetGameTime() - m_resetTalentsTime)/DAY;
+
+ // The first time reset costs 10 silver; after 1 day cost is reset to 10 silver
+ if(m_resetTalentsCost < 10*SILVER || days > 0)
+ return 10*SILVER;
+ // then 50 silver
+ else if(m_resetTalentsCost < 50*SILVER)
+ return 50*SILVER;
+ // then 1 gold
+ else if(m_resetTalentsCost < 1*GOLD)
+ return 1*GOLD;
+ // then increasing at a rate of 1 gold; cap 10 gold
+ else
+ return (m_resetTalentsCost + 1*GOLD > 10*GOLD ? 10*GOLD : m_resetTalentsCost + 1*GOLD);
+}
+
+void Pet::ToggleAutocast(uint32 spellid, bool apply)
+{
+ if(IsPassiveSpell(spellid))
+ return;
+
+ PetSpellMap::const_iterator itr = m_spells.find((uint16)spellid);
+
+ int i;
+
+ if(apply)
+ {
+ for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++);
+ if (i == m_autospells.size())
+ {
+ m_autospells.push_back(spellid);
+ itr->second->active = ACT_ENABLED;
+ itr->second->state = PETSPELL_CHANGED;
+ }
+ }
+ else
+ {
+ AutoSpellList::iterator itr2 = m_autospells.begin();
+ for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++, itr2++);
+ if (i < m_autospells.size())
+ {
+ m_autospells.erase(itr2);
+ itr->second->active = ACT_DISABLED;
+ itr->second->state = PETSPELL_CHANGED;
+ }
+ }
+}
+
+bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number)
+{
+ SetMapId(map->GetId());
+ SetInstanceId(map->GetInstanceId());
+
+ Object::_Create(guidlow, pet_number, HIGHGUID_PET);
+
+ m_DBTableGuid = guidlow;
+ m_originalEntry = Entry;
+
+ if(!InitEntry(Entry))
+ return false;
+
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
+
+ if(getPetType() == MINI_PET) // always non-attackable
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
+ return true;
+}
+
+bool Pet::HasSpell(uint32 spell) const
+{
+ return (m_spells.find(spell) != m_spells.end());
+}
+
+// Get all passive spells in our skill line
+void Pet::LearnPetPassives()
+{
+ CreatureInfo const* cInfo = GetCreatureInfo();
+ if(!cInfo)
+ return;
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
+ if(!cFamily)
+ return;
+
+ PetFamilySpellsStore::const_iterator petStore = sPetFamilySpellsStore.find(cFamily->ID);
+ if(petStore != sPetFamilySpellsStore.end())
+ {
+ for(PetFamilySpellsSet::const_iterator petSet = petStore->second.begin(); petSet != petStore->second.end(); ++petSet)
+ addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, 0xffff, PETSPELL_FAMILY);
+ }
+}
+
+void Pet::CastPetAuras(bool current)
+{
+ Unit* owner = GetOwner();
+ if(!owner)
+ return;
+
+ if(getPetType() != HUNTER_PET && (getPetType() != SUMMON_PET || owner->getClass() != CLASS_WARLOCK))
+ return;
+
+ for(PetAuraSet::iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); )
+ {
+ PetAura const* pa = *itr;
+ ++itr;
+
+ if(!current && pa->IsRemovedOnChangePet())
+ owner->RemovePetAura(pa);
+ else
+ CastPetAura(pa);
+ }
+}
+
+void Pet::CastPetAura(PetAura const* aura)
+{
+ uint16 auraId = aura->GetAura(GetEntry());
+ if(!auraId)
+ return;
+
+ if(auraId == 35696) // Demonic Knowledge
+ {
+ int32 basePoints = int32(aura->GetDamage() * (GetStat(STAT_STAMINA) + GetStat(STAT_INTELLECT)) / 100);
+ CastCustomSpell(this,auraId,&basePoints, NULL, NULL, true );
+ }
+ else
+ CastSpell(this, auraId, true);
+}
diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp
index 9ac00048d49..3eafbd4dd73 100644
--- a/src/game/PetHandler.cpp
+++ b/src/game/PetHandler.cpp
@@ -1,649 +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"
-#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;
- }
-}
+/*
+ * 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 2c5c4493c10..612bb14b684 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(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");
-}
+/*
+ * 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 acbcc9d3ca0..3beadc88278 100644
--- a/src/game/Player.cpp
+++ b/src/game/Player.cpp
@@ -1,18297 +1,18297 @@
-/*
- * 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 "Log.h"
-#include "Opcodes.h"
-#include "ObjectMgr.h"
-#include "SpellMgr.h"
-#include "World.h"
-#include "WorldPacket.h"
-#include "WorldSession.h"
-#include "UpdateMask.h"
-#include "Player.h"
-#include "SkillDiscovery.h"
-#include "QuestDef.h"
-#include "GossipDef.h"
-#include "UpdateData.h"
-#include "Channel.h"
-#include "ChannelMgr.h"
-#include "MapManager.h"
-#include "MapInstanced.h"
-#include "InstanceSaveMgr.h"
-#include "GridNotifiers.h"
-#include "GridNotifiersImpl.h"
-#include "CellImpl.h"
-#include "ObjectMgr.h"
-#include "ObjectAccessor.h"
-#include "CreatureAI.h"
-#include "Formulas.h"
-#include "Group.h"
-#include "Guild.h"
-#include "Pet.h"
-#include "SpellAuras.h"
-#include "Util.h"
-#include "Transports.h"
-#include "Weather.h"
-#include "BattleGround.h"
-#include "BattleGroundMgr.h"
-#include "ArenaTeam.h"
-#include "Chat.h"
-#include "Database/DatabaseImpl.h"
-#include "Spell.h"
-#include "SocialMgr.h"
-
-#include <cmath>
-
-#define ZONE_UPDATE_INTERVAL 1000
-
-#define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
-#define PLAYER_SKILL_VALUE_INDEX(x) (PLAYER_SKILL_INDEX(x)+1)
-#define PLAYER_SKILL_BONUS_INDEX(x) (PLAYER_SKILL_INDEX(x)+2)
-
-#define SKILL_VALUE(x) PAIR32_LOPART(x)
-#define SKILL_MAX(x) PAIR32_HIPART(x)
-#define MAKE_SKILL_VALUE(v, m) MAKE_PAIR32(v,m)
-
-#define SKILL_TEMP_BONUS(x) int16(PAIR32_LOPART(x))
-#define SKILL_PERM_BONUS(x) int16(PAIR32_HIPART(x))
-#define MAKE_SKILL_BONUS(t, p) MAKE_PAIR32(t,p)
-
-enum CharacterFlags
-{
- CHARACTER_FLAG_NONE = 0x00000000,
- CHARACTER_FLAG_UNK1 = 0x00000001,
- CHARACTER_FLAG_UNK2 = 0x00000002,
- CHARACTER_LOCKED_FOR_TRANSFER = 0x00000004,
- CHARACTER_FLAG_UNK4 = 0x00000008,
- CHARACTER_FLAG_UNK5 = 0x00000010,
- CHARACTER_FLAG_UNK6 = 0x00000020,
- CHARACTER_FLAG_UNK7 = 0x00000040,
- CHARACTER_FLAG_UNK8 = 0x00000080,
- CHARACTER_FLAG_UNK9 = 0x00000100,
- CHARACTER_FLAG_UNK10 = 0x00000200,
- CHARACTER_FLAG_HIDE_HELM = 0x00000400,
- CHARACTER_FLAG_HIDE_CLOAK = 0x00000800,
- CHARACTER_FLAG_UNK13 = 0x00001000,
- CHARACTER_FLAG_GHOST = 0x00002000,
- CHARACTER_FLAG_RENAME = 0x00004000,
- CHARACTER_FLAG_UNK16 = 0x00008000,
- CHARACTER_FLAG_UNK17 = 0x00010000,
- CHARACTER_FLAG_UNK18 = 0x00020000,
- CHARACTER_FLAG_UNK19 = 0x00040000,
- CHARACTER_FLAG_UNK20 = 0x00080000,
- CHARACTER_FLAG_UNK21 = 0x00100000,
- CHARACTER_FLAG_UNK22 = 0x00200000,
- CHARACTER_FLAG_UNK23 = 0x00400000,
- CHARACTER_FLAG_UNK24 = 0x00800000,
- CHARACTER_FLAG_LOCKED_BY_BILLING = 0x01000000,
- CHARACTER_FLAG_DECLINED = 0x02000000,
- CHARACTER_FLAG_UNK27 = 0x04000000,
- CHARACTER_FLAG_UNK28 = 0x08000000,
- CHARACTER_FLAG_UNK29 = 0x10000000,
- CHARACTER_FLAG_UNK30 = 0x20000000,
- CHARACTER_FLAG_UNK31 = 0x40000000,
- CHARACTER_FLAG_UNK32 = 0x80000000
-};
-
-// corpse reclaim times
-#define DEATH_EXPIRE_STEP (5*MINUTE)
-#define MAX_DEATH_COUNT 3
-
-static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
-
-//== PlayerTaxi ================================================
-
-PlayerTaxi::PlayerTaxi()
-{
- // Taxi nodes
- memset(m_taximask, 0, sizeof(m_taximask));
-}
-
-void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 level)
-{
- // capital and taxi hub masks
- switch(race)
- {
- case RACE_HUMAN: SetTaximaskNode(2); break; // Human
- case RACE_ORC: SetTaximaskNode(23); break; // Orc
- case RACE_DWARF: SetTaximaskNode(6); break; // Dwarf
- case RACE_NIGHTELF: SetTaximaskNode(26);
- SetTaximaskNode(27); break; // Night Elf
- case RACE_UNDEAD_PLAYER: SetTaximaskNode(11); break;// Undead
- case RACE_TAUREN: SetTaximaskNode(22); break; // Tauren
- case RACE_GNOME: SetTaximaskNode(6); break; // Gnome
- case RACE_TROLL: SetTaximaskNode(23); break; // Troll
- case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf
- case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei
- }
- // new continent starting masks (It will be accessible only at new map)
- switch(Player::TeamForRace(race))
- {
- case ALLIANCE: SetTaximaskNode(100); break;
- case HORDE: SetTaximaskNode(99); break;
- }
- // level dependent taxi hubs
- if(level>=68)
- SetTaximaskNode(213); //Shattered Sun Staging Area
-}
-
-void PlayerTaxi::LoadTaxiMask(const char* data)
-{
- Tokens tokens = StrSplit(data, " ");
-
- int index;
- Tokens::iterator iter;
- for (iter = tokens.begin(), index = 0;
- (index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index)
- {
- // load and set bits only for existed taxi nodes
- m_taximask[index] = sTaxiNodesMask[index] & uint32(atol((*iter).c_str()));
- }
-}
-
-void PlayerTaxi::AppendTaximaskTo( ByteBuffer& data, bool all )
-{
- if(all)
- {
- for (uint8 i=0; i<TaxiMaskSize; i++)
- data << sTaxiNodesMask[i]; // all existed nodes
- }
- else
- {
- for (uint8 i=0; i<TaxiMaskSize; i++)
- data << uint32(m_taximask[i]); // known nodes
- }
-}
-
-bool PlayerTaxi::LoadTaxiDestinationsFromString( std::string values )
-{
- ClearTaxiDestinations();
-
- Tokens tokens = StrSplit(values," ");
-
- for(Tokens::iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
- {
- uint32 node = uint32(atol(iter->c_str()));
- AddTaxiDestination(node);
- }
-
- if(m_TaxiDestinations.empty())
- return true;
-
- // Check integrity
- if(m_TaxiDestinations.size() < 2)
- return false;
-
- for(size_t i = 1; i < m_TaxiDestinations.size(); ++i)
- {
- uint32 cost;
- uint32 path;
- objmgr.GetTaxiPath(m_TaxiDestinations[i-1],m_TaxiDestinations[i],path,cost);
- if(!path)
- return false;
- }
-
- return true;
-}
-
-std::string PlayerTaxi::SaveTaxiDestinationsToString()
-{
- if(m_TaxiDestinations.empty())
- return "";
-
- std::ostringstream ss;
-
- for(size_t i=0; i < m_TaxiDestinations.size(); ++i)
- ss << m_TaxiDestinations[i] << " ";
-
- return ss.str();
-}
-
-uint32 PlayerTaxi::GetCurrentTaxiPath() const
-{
- if(m_TaxiDestinations.size() < 2)
- return 0;
-
- uint32 path;
- uint32 cost;
-
- objmgr.GetTaxiPath(m_TaxiDestinations[0],m_TaxiDestinations[1],path,cost);
-
- return path;
-}
-
-//== Player ====================================================
-
-const int32 Player::ReputationRank_Length[MAX_REPUTATION_RANK] = {36000, 3000, 3000, 3000, 6000, 12000, 21000, 1000};
-
-UpdateMask Player::updateVisualBits;
-
-Player::Player (WorldSession *session): Unit()
-{
- m_transport = 0;
-
- m_speakTime = 0;
- m_speakCount = 0;
-
- m_objectType |= TYPEMASK_PLAYER;
- m_objectTypeId = TYPEID_PLAYER;
-
- m_valuesCount = PLAYER_END;
-
- m_session = session;
-
- m_divider = 0;
-
- m_ExtraFlags = 0;
- if(GetSession()->GetSecurity() >= SEC_GAMEMASTER)
- SetAcceptTicket(true);
-
- // players always accept
- if(GetSession()->GetSecurity() == SEC_PLAYER)
- SetAcceptWhispers(true);
-
- m_curSelection = 0;
- m_lootGuid = 0;
-
- m_comboTarget = 0;
- m_comboPoints = 0;
-
- m_usedTalentCount = 0;
-
- m_regenTimer = 0;
- m_weaponChangeTimer = 0;
-
- m_zoneUpdateId = 0;
- m_zoneUpdateTimer = 0;
-
- m_areaUpdateId = 0;
-
- m_nextSave = sWorld.getConfig(CONFIG_INTERVAL_SAVE);
-
- // randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE]
- // this must help in case next save after mass player load after server startup
- m_nextSave = urand(m_nextSave/2,m_nextSave*3/2);
-
- clearResurrectRequestData();
-
- m_SpellModRemoveCount = 0;
-
- memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT);
-
- m_social = NULL;
-
- // group is initialized in the reference constructor
- SetGroupInvite(NULL);
- m_groupUpdateMask = 0;
- m_auraUpdateMask = 0;
-
- duel = NULL;
-
- m_GuildIdInvited = 0;
- m_ArenaTeamIdInvited = 0;
-
- m_atLoginFlags = AT_LOGIN_NONE;
-
- m_dontMove = false;
-
- pTrader = 0;
- ClearTrade();
-
- m_cinematic = 0;
-
- PlayerTalkClass = new PlayerMenu( GetSession() );
- m_currentBuybackSlot = BUYBACK_SLOT_START;
-
- for ( int aX = 0 ; aX < 8 ; aX++ )
- m_Tutorials[ aX ] = 0x00;
- m_TutorialsChanged = false;
-
- m_DailyQuestChanged = false;
- m_lastDailyQuestTime = 0;
-
- m_regenTimer = 0;
- m_weaponChangeTimer = 0;
- m_breathTimer = 0;
- m_isunderwater = 0;
- m_isInWater = false;
- m_drunkTimer = 0;
- m_drunk = 0;
- m_restTime = 0;
- m_deathTimer = 0;
- m_deathExpireTime = 0;
-
- m_swingErrorMsg = 0;
-
- m_DetectInvTimer = 1000;
-
- m_bgBattleGroundID = 0;
- for (int j=0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; j++)
- {
- m_bgBattleGroundQueueID[j].bgQueueType = 0;
- m_bgBattleGroundQueueID[j].invitedToInstance = 0;
- }
- m_bgTeam = 0;
-
- m_logintime = time(NULL);
- m_Last_tick = m_logintime;
- m_WeaponProficiency = 0;
- m_ArmorProficiency = 0;
- m_canParry = false;
- m_canBlock = false;
- m_canDualWield = false;
- m_ammoDPS = 0.0f;
-
- m_temporaryUnsummonedPetNumber = 0;
- //cache for UNIT_CREATED_BY_SPELL to allow
- //returning reagests for temporarily removed pets
- //when dying/logging out
- m_oldpetspell = 0;
-
- ////////////////////Rest System/////////////////////
- time_inn_enter=0;
- inn_pos_mapid=0;
- inn_pos_x=0;
- inn_pos_y=0;
- inn_pos_z=0;
- m_rest_bonus=0;
- rest_type=REST_TYPE_NO;
- ////////////////////Rest System/////////////////////
-
- m_mailsLoaded = false;
- m_mailsUpdated = false;
- unReadMails = 0;
- m_nextMailDelivereTime = 0;
-
- m_resetTalentsCost = 0;
- m_resetTalentsTime = 0;
- m_itemUpdateQueueBlocked = false;
-
- for (int i = 0; i < MAX_MOVE_TYPE; ++i)
- m_forced_speed_changes[i] = 0;
-
- m_stableSlots = 0;
-
- /////////////////// Instance System /////////////////////
-
- m_HomebindTimer = 0;
- m_InstanceValid = true;
- m_dungeonDifficulty = DIFFICULTY_NORMAL;
-
- for (int i = 0; i < BASEMOD_END; i++)
- {
- m_auraBaseMod[i][FLAT_MOD] = 0.0f;
- m_auraBaseMod[i][PCT_MOD] = 1.0f;
- }
-
- // Honor System
- m_lastHonorUpdateTime = time(NULL);
-
- // Player summoning
- m_summon_expire = 0;
- m_summon_mapid = 0;
- m_summon_x = 0.0f;
- m_summon_y = 0.0f;
- m_summon_z = 0.0f;
-
- //Default movement to run mode
- m_unit_movement_flags = 0;
-
- m_miniPet = 0;
- m_bgAfkReportedTimer = 0;
- m_contestedPvPTimer = 0;
-
- m_declinedname = NULL;
-}
-
-Player::~Player ()
-{
- CleanupsBeforeDelete();
-
- if(m_uint32Values) // only for fully created Object
- {
- sSocialMgr.RemovePlayerSocial(GetGUIDLow());
- }
-
- // Note: buy back item already deleted from DB when player was saved
- for(int i = 0; i < PLAYER_SLOTS_COUNT; ++i)
- {
- if(m_items[i])
- delete m_items[i];
- }
- CleanupChannels();
-
- for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- delete itr->second;
-
- //all mailed items should be deleted, also all mail should be deallocated
- for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();++itr)
- delete *itr;
-
- for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
- delete iter->second; //if item is duplicated... then server may crash ... but that item should be deallocated
-
- delete PlayerTalkClass;
-
- if (m_transport)
- {
- m_transport->RemovePassenger(this);
- }
-
- for(size_t x = 0; x < ItemSetEff.size(); x++)
- if(ItemSetEff[x])
- delete ItemSetEff[x];
-
- // clean up player-instance binds, 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->RemovePlayer(this);
-
- delete m_declinedname;
-}
-
-void Player::CleanupsBeforeDelete()
-{
- if(m_uint32Values) // only for fully created Object
- {
- TradeCancel(false);
- DuelComplete(DUEL_INTERUPTED);
- }
- Unit::CleanupsBeforeDelete();
-}
-
-bool Player::Create( uint32 guidlow, std::string name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId )
-{
- Object::_Create(guidlow, 0, HIGHGUID_PLAYER);
-
- m_name = name;
-
- PlayerInfo const* info = objmgr.GetPlayerInfo(race, class_);
- if(!info)
- {
- sLog.outError("Player have incorrect race/class pair. Can't be loaded.");
- return false;
- }
-
- for (int i = 0; i < PLAYER_SLOTS_COUNT; i++)
- m_items[i] = NULL;
-
- //for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++)
- //{
- // SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+j*2,0);
- // SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+j,0);
- // SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+j,0);
- //}
-
- m_race = race;
- m_class = class_;
-
- SetMapId(info->mapId);
- Relocate(info->positionX,info->positionY,info->positionZ);
-
- ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(class_);
- if(!cEntry)
- {
- sLog.outError("Class %u not found in DBC (Wrong DBC files?)",class_);
- return false;
- }
-
- uint8 powertype = cEntry->powerType;
-
- uint32 unitfield;
-
- switch(powertype)
- {
- case POWER_ENERGY:
- case POWER_MANA:
- unitfield = 0x00000000;
- break;
- case POWER_RAGE:
- unitfield = 0x00110000;
- break;
- default:
- sLog.outError("Invalid default powertype %u for player (class %u)",powertype,class_);
- return false;
- }
-
- SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE );
- SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f );
-
- switch(gender)
- {
- case GENDER_FEMALE:
- SetDisplayId(info->displayId_f );
- SetNativeDisplayId(info->displayId_f );
- break;
- case GENDER_MALE:
- SetDisplayId(info->displayId_m );
- SetNativeDisplayId(info->displayId_m );
- break;
- default:
- sLog.outError("Invalid gender %u for player",gender);
- return false;
- break;
- }
-
- setFactionForRace(m_race);
-
- SetUInt32Value(UNIT_FIELD_BYTES_0, ( ( race ) | ( class_ << 8 ) | ( gender << 16 ) | ( powertype << 24 ) ) );
- SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield);
- SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_UNK5 );
- SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE );
- SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client
-
- //-1 is default value
- SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1));
-
- SetUInt32Value(PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24)));
- SetUInt32Value(PLAYER_BYTES_2, (facialHair | (0x00 << 8) | (0x00 << 16) | (0x02 << 24)));
- SetByteValue(PLAYER_BYTES_3, 0, gender);
-
- SetUInt32Value( PLAYER_GUILDID, 0 );
- SetUInt32Value( PLAYER_GUILDRANK, 0 );
- SetUInt32Value( PLAYER_GUILD_TIMESTAMP, 0 );
-
- SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES, 0 ); // 0=disabled
- SetUInt32Value( PLAYER_CHOSEN_TITLE, 0 );
- SetUInt32Value( PLAYER_FIELD_KILLS, 0 );
- SetUInt32Value( PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0 );
- SetUInt32Value( PLAYER_FIELD_TODAY_CONTRIBUTION, 0 );
- SetUInt32Value( PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0 );
-
- // set starting level
- if(GetSession()->GetSecurity() >= SEC_MODERATOR)
- SetUInt32Value( UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_GM_START_LEVEL) ); //ImpConfig
- else
- SetUInt32Value( UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_START_PLAYER_LEVEL) );
- // set starting gold
- SetUInt32Value( PLAYER_FIELD_COINAGE, sWorld.PlayerStartGold()*10000 );
-
- // set starting honor
- SetUInt32Value( PLAYER_FIELD_HONOR_CURRENCY, sWorld.getConfig(CONFIG_PLAYER_START_HONOR) );
-
- // set starting arena pts
- SetUInt32Value( PLAYER_FIELD_ARENA_CURRENCY, sWorld.getConfig(CONFIG_PLAYER_START_ARENAPTS) );
-
- // start with every map explored
- if(sWorld.getConfig(CONFIG_START_ALL_EXPLORED))
- {
- for (uint8 i=0; i<64; i++)
- SetFlag(PLAYER_EXPLORED_ZONES_1+i,0xFFFFFFFF);
- }
-
- // Played time
- m_Last_tick = time(NULL);
- m_Played_time[0] = 0;
- m_Played_time[1] = 0;
-
- // base stats and related field values
- InitStatsForLevel();
- InitTaxiNodesForLevel();
- InitTalentForLevel();
- InitPrimaryProffesions(); // to max set before any spell added
-
- // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
- UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
- SetHealth(GetMaxHealth());
- if (getPowerType()==POWER_MANA)
- {
- UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intelect)
- SetPower(POWER_MANA,GetMaxPower(POWER_MANA));
- }
-
- learnDefaultSpells(true);
-
- std::list<uint16>::const_iterator action_itr[4];
- for(int i=0; i<4; i++)
- action_itr[i] = info->action[i].begin();
-
- for (; action_itr[0]!=info->action[0].end() && action_itr[1]!=info->action[1].end();)
- {
- uint16 taction[4];
- for(int i=0; i<4 ;i++)
- taction[i] = (*action_itr[i]);
-
- addActionButton((uint8)taction[0], taction[1], (uint8)taction[2], (uint8)taction[3]);
-
- for(int i=0; i<4 ;i++)
- ++action_itr[i];
- }
-
- for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr!=info->item.end(); ++item_id_itr++)
- {
- uint32 titem_id = item_id_itr->item_id;
- uint32 titem_amount = item_id_itr->item_amount;
-
- sLog.outDebug("STORAGE: Creating initial item, itemId = %u, count = %u",titem_id, titem_amount);
-
- // attempt equip
- uint16 eDest;
- uint8 msg = CanEquipNewItem( NULL_SLOT, eDest, titem_id, titem_amount, false );
- if( msg == EQUIP_ERR_OK )
- {
- EquipNewItem( eDest, titem_id, titem_amount, true);
- AutoUnequipOffhandIfNeed();
- continue; // equipped, to next
- }
-
- // attempt store
- ItemPosCountVec sDest;
- // store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
- msg = CanStoreNewItem( INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount );
- if( msg == EQUIP_ERR_OK )
- {
- StoreNewItem( sDest, titem_id, true, Item::GenerateItemRandomPropertyId(titem_id) );
- continue; // stored, to next
- }
-
- // item can't be added
- sLog.outError("STORAGE: Can't equip or store initial item %u for race %u class %u , error msg = %u",titem_id,race,class_,msg);
- }
-
- // bags and main-hand weapon must equipped at this moment
- // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon)
- // or ammo not equipped in special bag
- for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- if(Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- {
- uint16 eDest;
- // equip offhand weapon/shield if it attempt equipped before main-hand weapon
- uint8 msg = CanEquipItem( NULL_SLOT, eDest, pItem, false );
- if( msg == EQUIP_ERR_OK )
- {
- RemoveItem(INVENTORY_SLOT_BAG_0, i,true);
- EquipItem( eDest, pItem, true);
- }
- // move other items to more appropriate slots (ammo not equipped in special bag)
- else
- {
- ItemPosCountVec sDest;
- msg = CanStoreItem( NULL_BAG, NULL_SLOT, sDest, pItem, false );
- if( msg == EQUIP_ERR_OK )
- {
- RemoveItem(INVENTORY_SLOT_BAG_0, i,true);
- pItem = StoreItem( sDest, pItem, true);
- }
-
- // if this is ammo then use it
- uint8 msg = CanUseAmmo( pItem->GetProto()->ItemId );
- if( msg == EQUIP_ERR_OK )
- SetAmmo( pItem->GetProto()->ItemId );
- }
- }
- }
- // all item positions resolved
-
- return true;
-}
-
-void Player::StartMirrorTimer(MirrorTimerType Type, uint32 MaxValue)
-{
- uint32 BreathRegen = (uint32)-1;
-
- WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
- data << (uint32)Type;
- data << MaxValue;
- data << MaxValue;
- data << BreathRegen;
- data << (uint8)0;
- data << (uint32)0; // spell id
- GetSession()->SendPacket(&data);
-}
-
-void Player::ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, uint32 Regen)
-{
- if(Type==BREATH_TIMER)
- m_breathTimer = ((MaxValue + 1000) - CurrentValue) / Regen;
-
- WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
- data << (uint32)Type;
- data << CurrentValue;
- data << MaxValue;
- data << Regen;
- data << (uint8)0;
- data << (uint32)0; // spell id
- GetSession()->SendPacket( &data );
-}
-
-void Player::StopMirrorTimer(MirrorTimerType Type)
-{
- if(Type==BREATH_TIMER)
- m_breathTimer = 0;
-
- WorldPacket data(SMSG_STOP_MIRROR_TIMER, 4);
- data << (uint32)Type;
- GetSession()->SendPacket( &data );
-}
-
-void Player::EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 damage)
-{
- WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21));
- data << (uint64)guid;
- data << (uint8)(type!=DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL);
- data << (uint32)damage;
- data << (uint32)0;
- data << (uint32)0;
- //m_session->SendPacket(&data);
- //Let other players see that you get damage
- SendMessageToSet(&data, true);
- DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
-
- if(type==DAMAGE_FALL && !isAlive()) // DealDamage not apply item durability loss at self damage
- {
- DEBUG_LOG("We are fall to death, loosing 10 percents durability");
- DurabilityLossAll(0.10f,false);
- // durability lost message
- WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0);
- GetSession()->SendPacket(&data);
- }
-}
-
-void Player::HandleDrowning()
-{
- if(!m_isunderwater)
- return;
-
- //if players is GM, have waterbreath, dead or breathing is disabled
- if(sWorld.getConfig(CONFIG_DISABLE_BREATHING) || waterbreath || isGameMaster() || !isAlive())
- {
- StopMirrorTimer(BREATH_TIMER);
- m_isunderwater = 0;
- return;
- }
-
- uint32 UnderWaterTime = 1*MINUTE*1000; // default leangthL 1 min
-
- AuraList const& mModWaterBreathing = GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING);
- for(AuraList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i)
- UnderWaterTime = uint32(UnderWaterTime * (100.0f + (*i)->GetModifier()->m_amount) / 100.0f);
-
- if ((m_isunderwater & 0x01) && !(m_isunderwater & 0x80) && isAlive())
- {
- //single trigger timer
- if (!(m_isunderwater & 0x02))
- {
- m_isunderwater|= 0x02;
- m_breathTimer = UnderWaterTime + 1000;
- }
- //single trigger "Breathbar"
- if ( m_breathTimer <= UnderWaterTime && !(m_isunderwater & 0x04))
- {
- m_isunderwater|= 0x04;
- StartMirrorTimer(BREATH_TIMER, UnderWaterTime);
- }
- //continius trigger drowning "Damage"
- if ((m_breathTimer == 0) && (m_isunderwater & 0x01))
- {
- //TODO: Check this formula
- uint64 guid = GetGUID();
- uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1);
-
- EnvironmentalDamage(guid, DAMAGE_DROWNING,damage);
- m_breathTimer = 2000;
- }
- }
- //single trigger retract bar
- else if (!(m_isunderwater & 0x01) && !(m_isunderwater & 0x08) && (m_isunderwater & 0x02) && (m_breathTimer > 0) && isAlive())
- {
- m_isunderwater = 0x08;
-
- uint32 BreathRegen = 10;
- ModifyMirrorTimer(BREATH_TIMER, UnderWaterTime, m_breathTimer,BreathRegen);
- m_isunderwater = 0x10;
- }
- //remove bar
- else if ((m_breathTimer < 50) && !(m_isunderwater & 0x01) && (m_isunderwater == 0x10))
- {
- StopMirrorTimer(BREATH_TIMER);
- m_isunderwater = 0;
- }
-}
-
-void Player::HandleLava()
-{
- bool ValidArea = false;
-
- if ((m_isunderwater & 0x80) && isAlive())
- {
- //Single trigger Set BreathTimer
- if (!(m_isunderwater & 0x80))
- {
- m_isunderwater|= 0x04;
- m_breathTimer = 1000;
- }
- //Reset BreathTimer and still in the lava
- if (!m_breathTimer)
- {
- uint64 guid = GetGUID();
- uint32 damage = urand(600, 700); // TODO: Get more detailed information about lava damage
- uint32 dmgZone = GetZoneId(); // TODO: Find correct "lava dealing zone" flag in Area Table
-
- // Deal lava damage only in lava zones.
- switch(dmgZone)
- {
- case 0x8D:
- ValidArea = false;
- break;
- case 0x94:
- ValidArea = false;
- break;
- case 0x2CE:
- ValidArea = false;
- break;
- case 0x2CF:
- ValidArea = false;
- break;
- default:
- if (dmgZone / 5 & 0x408)
- ValidArea = true;
- }
-
- // if is valid area and is not gamemaster then deal damage
- if ( ValidArea && !isGameMaster() )
- EnvironmentalDamage(guid, DAMAGE_LAVA, damage);
-
- m_breathTimer = 1000;
- }
-
- }
- //Death timer disabled and WaterFlags reset
- else if (m_deathState == DEAD)
- {
- m_breathTimer = 0;
- m_isunderwater = 0;
- }
-}
-
-///The player sobers by 256 every 10 seconds
-void Player::HandleSobering()
-{
- m_drunkTimer = 0;
-
- uint32 drunk = (m_drunk <= 256) ? 0 : (m_drunk - 256);
- SetDrunkValue(drunk);
-}
-
-DrunkenState Player::GetDrunkenstateByValue(uint16 value)
-{
- if(value >= 23000)
- return DRUNKEN_SMASHED;
- if(value >= 12800)
- return DRUNKEN_DRUNK;
- if(value & 0xFFFE)
- return DRUNKEN_TIPSY;
- return DRUNKEN_SOBER;
-}
-
-void Player::SetDrunkValue(uint16 newDrunkenValue, uint32 itemId)
-{
- uint32 oldDrunkenState = Player::GetDrunkenstateByValue(m_drunk);
-
- m_drunk = newDrunkenValue;
- SetUInt32Value(PLAYER_BYTES_3,(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFF0001) | (m_drunk & 0xFFFE));
-
- uint32 newDrunkenState = Player::GetDrunkenstateByValue(m_drunk);
-
- // special drunk invisibility detection
- if(newDrunkenState >= DRUNKEN_DRUNK)
- m_detectInvisibilityMask |= (1<<6);
- else
- m_detectInvisibilityMask &= ~(1<<6);
-
- if(newDrunkenState == oldDrunkenState)
- return;
-
- WorldPacket data(SMSG_CROSSED_INEBRIATION_THRESHOLD, (8+4+4));
- data << GetGUID();
- data << uint32(newDrunkenState);
- data << uint32(itemId);
-
- SendMessageToSet(&data, true);
-}
-
-void Player::Update( uint32 p_time )
-{
- if(!IsInWorld())
- return;
-
- // undelivered mail
- if(m_nextMailDelivereTime && m_nextMailDelivereTime <= time(NULL))
- {
- SendNewMail();
- ++unReadMails;
-
- // It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated)
- m_nextMailDelivereTime = 0;
- }
-
- Unit::Update( p_time );
-
- // update player only attacks
- if(uint32 ranged_att = getAttackTimer(RANGED_ATTACK))
- {
- setAttackTimer(RANGED_ATTACK, (p_time >= ranged_att ? 0 : ranged_att - p_time) );
- }
-
- if(uint32 off_att = getAttackTimer(OFF_ATTACK))
- {
- setAttackTimer(OFF_ATTACK, (p_time >= off_att ? 0 : off_att - p_time) );
- }
-
- time_t now = time (NULL);
-
- UpdatePvPFlag(now);
-
- UpdateContestedPvP(p_time);
-
- UpdateDuelFlag(now);
-
- CheckDuelDistance(now);
-
- UpdateAfkReport(now);
-
- CheckExploreSystem();
-
- // Update items that have just a limited lifetime
- if (now>m_Last_tick)
- UpdateItemDuration(uint32(now- m_Last_tick));
-
- if (!m_timedquests.empty())
- {
- std::set<uint32>::iterator iter = m_timedquests.begin();
- while (iter != m_timedquests.end())
- {
- QuestStatusData& q_status = mQuestStatus[*iter];
- if( q_status.m_timer <= p_time )
- {
- uint32 quest_id = *iter;
- ++iter; // current iter will be removed in FailTimedQuest
- FailTimedQuest( quest_id );
- }
- else
- {
- q_status.m_timer -= p_time;
- if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
- ++iter;
- }
- }
- }
-
- if (hasUnitState(UNIT_STAT_MELEE_ATTACKING))
- {
- Unit *pVictim = getVictim();
- if( !IsNonMeleeSpellCasted(false) && pVictim)
- {
- // default combat reach 10
- // TODO add weapon,skill check
-
- float pldistance = ATTACK_DISTANCE;
-
- if (isAttackReady(BASE_ATTACK))
- {
- if(!IsWithinDistInMap(pVictim, pldistance))
- {
- setAttackTimer(BASE_ATTACK,100);
- if(m_swingErrorMsg != 1) // send single time (client auto repeat)
- {
- SendAttackSwingNotInRange();
- m_swingErrorMsg = 1;
- }
- }
- //120 degrees of radiant range
- else if( !HasInArc( 2*M_PI/3, pVictim ))
- {
- setAttackTimer(BASE_ATTACK,100);
- if(m_swingErrorMsg != 2) // send single time (client auto repeat)
- {
- SendAttackSwingBadFacingAttack();
- m_swingErrorMsg = 2;
- }
- }
- else
- {
- m_swingErrorMsg = 0; // reset swing error state
-
- // prevent base and off attack in same time, delay attack at 0.2 sec
- if(haveOffhandWeapon())
- {
- uint32 off_att = getAttackTimer(OFF_ATTACK);
- if(off_att < ATTACK_DISPLAY_DELAY)
- setAttackTimer(OFF_ATTACK,ATTACK_DISPLAY_DELAY);
- }
- AttackerStateUpdate(pVictim, BASE_ATTACK);
- resetAttackTimer(BASE_ATTACK);
- }
- }
-
- if ( haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
- {
- if(!IsWithinDistInMap(pVictim, pldistance))
- {
- setAttackTimer(OFF_ATTACK,100);
- }
- else if( !HasInArc( 2*M_PI/3, pVictim ))
- {
- setAttackTimer(OFF_ATTACK,100);
- }
- else
- {
- // prevent base and off attack in same time, delay attack at 0.2 sec
- uint32 base_att = getAttackTimer(BASE_ATTACK);
- if(base_att < ATTACK_DISPLAY_DELAY)
- setAttackTimer(BASE_ATTACK,ATTACK_DISPLAY_DELAY);
- // do attack
- AttackerStateUpdate(pVictim, OFF_ATTACK);
- resetAttackTimer(OFF_ATTACK);
- }
- }
-
- Unit *owner = pVictim->GetOwner();
- Unit *u = owner ? owner : pVictim;
- if(u->IsPvP() && (!duel || duel->opponent != u))
- {
- UpdatePvP(true);
- RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
- }
- }
- }
-
- if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
- {
- if(roll_chance_i(3) && GetTimeInnEnter() > 0) //freeze update
- {
- int time_inn = time(NULL)-GetTimeInnEnter();
- if (time_inn >= 10) //freeze update
- {
- float bubble = 0.125*sWorld.getRate(RATE_REST_INGAME);
- //speed collect rest bonus (section/in hour)
- SetRestBonus( GetRestBonus()+ time_inn*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble );
- UpdateInnerTime(time(NULL));
- }
- }
- }
-
- if(m_regenTimer > 0)
- {
- if(p_time >= m_regenTimer)
- m_regenTimer = 0;
- else
- m_regenTimer -= p_time;
- }
-
- if (m_weaponChangeTimer > 0)
- {
- if(p_time >= m_weaponChangeTimer)
- m_weaponChangeTimer = 0;
- else
- m_weaponChangeTimer -= p_time;
- }
-
- if (m_zoneUpdateTimer > 0)
- {
- if(p_time >= m_zoneUpdateTimer)
- {
- uint32 newzone = GetZoneId();
- if( m_zoneUpdateId != newzone )
- UpdateZone(newzone); // also update area
- else
- {
- // use area updates as well
- // needed for free far all arenas for example
- uint32 newarea = GetAreaId();
- if( m_areaUpdateId != newarea )
- UpdateArea(newarea);
-
- m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
- }
- }
- else
- m_zoneUpdateTimer -= p_time;
- }
-
- if (isAlive())
- {
- RegenerateAll();
- }
-
- if (m_deathState == JUST_DIED)
- {
- KillPlayer();
- }
-
- if(m_nextSave > 0)
- {
- if(p_time >= m_nextSave)
- {
- // m_nextSave reseted in SaveToDB call
- SaveToDB();
- sLog.outDetail("Player '%s' (GUID: %u) saved", GetName(), GetGUIDLow());
- }
- else
- {
- m_nextSave -= p_time;
- }
- }
-
- //Breathtimer
- if(m_breathTimer > 0)
- {
- if(p_time >= m_breathTimer)
- m_breathTimer = 0;
- else
- m_breathTimer -= p_time;
-
- }
-
- //Handle Water/drowning
- HandleDrowning();
-
- //Handle lava
- HandleLava();
-
- //Handle detect stealth players
- if (m_DetectInvTimer > 0)
- {
- if (p_time >= m_DetectInvTimer)
- {
- m_DetectInvTimer = 3000;
- HandleStealthedUnitsDetection();
- }
- else
- m_DetectInvTimer -= p_time;
- }
-
- // Played time
- if (now > m_Last_tick)
- {
- uint32 elapsed = uint32(now - m_Last_tick);
- m_Played_time[0] += elapsed; // Total played time
- m_Played_time[1] += elapsed; // Level played time
- m_Last_tick = now;
- }
-
- if (m_drunk)
- {
- m_drunkTimer += p_time;
-
- if (m_drunkTimer > 10000)
- HandleSobering();
- }
-
- // not auto-free ghost from body in instances
- if(m_deathTimer > 0 && !GetBaseMap()->Instanceable())
- {
- if(p_time >= m_deathTimer)
- {
- m_deathTimer = 0;
- BuildPlayerRepop();
- RepopAtGraveyard();
- }
- else
- m_deathTimer -= p_time;
- }
-
- UpdateEnchantTime(p_time);
- UpdateHomebindTime(p_time);
-
- // group update
- SendUpdateToOutOfRangeGroupMembers();
-
- Pet* pet = GetPet();
- if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE))
- {
- RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
- return;
- }
-}
-
-void Player::setDeathState(DeathState s)
-{
- uint32 ressSpellId = 0;
-
- bool cur = isAlive();
-
- if(s == JUST_DIED && cur)
- {
- // drunken state is cleared on death
- SetDrunkValue(0);
- // lost combo points at any target (targeted combo points clear in Unit::setDeathState)
- ClearComboPoints();
-
- clearResurrectRequestData();
-
- // remove form before other mods to prevent incorrect stats calculation
- RemoveAurasDueToSpell(m_ShapeShiftFormSpellId);
-
- //FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
- RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
-
- // remove uncontrolled pets
- RemoveMiniPet();
- RemoveGuardians();
-
- // save value before aura remove in Unit::setDeathState
- ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL);
-
- // passive spell
- if(!ressSpellId)
- ressSpellId = GetResurrectionSpellId();
- }
- Unit::setDeathState(s);
-
- // restore resurrection spell id for player after aura remove
- if(s == JUST_DIED && cur && ressSpellId)
- SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId);
-
- if(isAlive() && !cur)
- {
- //clear aura case after resurrection by another way (spells will be applied before next death)
- SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
-
- // restore default warrior stance
- if(getClass()== CLASS_WARRIOR)
- CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true);
- }
-}
-
-void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
-{
- *p_data << GetGUID();
- *p_data << m_name;
-
- *p_data << getRace();
- uint8 pClass = getClass();
- *p_data << pClass;
- *p_data << getGender();
-
- uint32 bytes = GetUInt32Value(PLAYER_BYTES);
- *p_data << uint8(bytes);
- *p_data << uint8(bytes >> 8);
- *p_data << uint8(bytes >> 16);
- *p_data << uint8(bytes >> 24);
-
- bytes = GetUInt32Value(PLAYER_BYTES_2);
- *p_data << uint8(bytes);
-
- *p_data << uint8(getLevel()); // player level
- // do not use GetMap! it will spawn a new instance since the bound instances are not loaded
- uint32 zoneId = MapManager::Instance().GetZoneId(GetMapId(), GetPositionX(),GetPositionY());
-
- *p_data << zoneId;
- *p_data << GetMapId();
-
- *p_data << GetPositionX();
- *p_data << GetPositionY();
- *p_data << GetPositionZ();
-
- *p_data << GetUInt32Value(PLAYER_GUILDID); // guild id
-
- uint32 char_flags = 0;
- if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
- char_flags |= CHARACTER_FLAG_HIDE_HELM;
- if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
- char_flags |= CHARACTER_FLAG_HIDE_CLOAK;
- if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
- char_flags |= CHARACTER_FLAG_GHOST;
- if(HasAtLoginFlag(AT_LOGIN_RENAME))
- char_flags |= CHARACTER_FLAG_RENAME;
- // always send the flag if declined names aren't used
- // to let the client select a default method of declining the name
- if(!sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) || (result && result->Fetch()[12].GetCppString() != ""))
- char_flags |= CHARACTER_FLAG_DECLINED;
-
- *p_data << (uint32)char_flags; // character flags
-
- *p_data << (uint8)1; // unknown
-
- // Pets info
- {
- uint32 petDisplayId = 0;
- uint32 petLevel = 0;
- uint32 petFamily = 0;
-
- // show pet at selection character in character list only for non-ghost character
- if(result && isAlive() && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER))
- {
- Field* fields = result->Fetch();
-
- uint32 entry = fields[9].GetUInt32();
- CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(entry);
- if(cInfo)
- {
- petDisplayId = fields[10].GetUInt32();
- petLevel = fields[11].GetUInt32();
- petFamily = cInfo->family;
- }
- }
-
- *p_data << (uint32)petDisplayId;
- *p_data << (uint32)petLevel;
- *p_data << (uint32)petFamily;
- }
-
- /*ItemPrototype const *items[EQUIPMENT_SLOT_END];
- for (int i = 0; i < EQUIPMENT_SLOT_END; i++)
- items[i] = NULL;
-
- QueryResult *result = CharacterDatabase.PQuery("SELECT slot,item_template FROM character_inventory WHERE guid = '%u' AND bag = 0",GetGUIDLow());
- if (result)
- {
- do
- {
- Field *fields = result->Fetch();
- uint8 slot = fields[0].GetUInt8() & 255;
- uint32 item_id = fields[1].GetUInt32();
- if( slot >= EQUIPMENT_SLOT_END )
- continue;
-
- items[slot] = objmgr.GetItemPrototype(item_id);
- if(!items[slot])
- {
- sLog.outError( "Player::BuildEnumData: Player %s have unknown item (id: #%u) in inventory, skipped.", GetName(),item_id );
- continue;
- }
- } while (result->NextRow());
- delete result;
- }*/
-
- for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++)
- {
- uint32 visualbase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET);
- uint32 item_id = GetUInt32Value(visualbase);
- const ItemPrototype * proto = objmgr.GetItemPrototype(item_id);
- SpellItemEnchantmentEntry const *enchant = NULL;
-
- for(uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot<=TEMP_ENCHANTMENT_SLOT; enchantSlot++)
- {
- uint32 enchantId = GetUInt32Value(visualbase+1+enchantSlot);
- if(enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId))
- break;
- }
-
- if (proto != NULL)
- {
- *p_data << (uint32)proto->DisplayInfoID;
- *p_data << (uint8)proto->InventoryType;
- *p_data << (uint32)(enchant?enchant->aura_id:0);
- }
- else
- {
- *p_data << (uint32)0;
- *p_data << (uint8)0;
- *p_data << (uint32)0; // enchant?
- }
- }
- *p_data << (uint32)0; // first bag display id
- *p_data << (uint8)0; // first bag inventory type
- *p_data << (uint32)0; // enchant?
-}
-
-bool Player::ToggleAFK()
-{
- ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);
-
- bool state = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);
-
- // afk player not allowed in battleground
- if(state && InBattleGround())
- LeaveBattleground();
-
- return state;
-}
-
-bool Player::ToggleDND()
-{
- ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);
-
- return HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);
-}
-
-uint8 Player::chatTag() const
-{
- // it's bitmask
- // 0x8 - ??
- // 0x4 - gm
- // 0x2 - dnd
- // 0x1 - afk
- if(isGMChat())
- return 4;
- else if(isDND())
- return 3;
- if(isAFK())
- return 1;
- else
- return 0;
-}
-
-bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options)
-{
- if(!MapManager::IsValidMapCoord(mapid, x, y, z, orientation))
- {
- sLog.outError("TeleportTo: invalid map %d or absent instance template.", mapid);
- return false;
- }
-
- // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
- Pet* pet = GetPet();
-
- MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
-
- // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)...
- // don't let gm level > 1 either
- if(!InBattleGround() && mEntry->IsBattleGroundOrArena())
- return false;
-
- bool tbc = GetSession()->IsTBC() && sWorld.getConfig(CONFIG_EXPANSION) > 0;
-
- // normal client and TBC map
- if(!tbc && mEntry->IsExpansionMap())
- {
- sLog.outDebug("Player %s using Normal client and tried teleport to non existing map %u", GetName(), mapid);
-
- if(GetTransport())
- RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :)
-
- SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL1);
-
- return false; // normal client can't teleport to this map...
- }
- else if(tbc) // can teleport to any existing map
- {
- sLog.outDebug("Player %s have TBC client and will teleported to map %u", GetName(), mapid);
- }
- else
- {
- sLog.outDebug("Player %s have normal client and will teleported to standard map %u", GetName(), mapid);
- }
- /*
- only TBC (no 0x80000 and 0x10 flags...)
- 3604590=0x37006E=0x200000 + 0x100000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x8 + 0x4 + 0x2
-
- Kharazan (normal/TBC??), but not have 0x10 flag (accessible by normal client?)
- 4128878=0x3F006E=0x200000 + 0x100000 + 0x80000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x8 + 0x4 + 0x2
-
- normal+TBC maps
- 4128894=0x3F007E=0x200000 + 0x100000 + 0x80000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x10 + 0x8 + 0x4 + 0x2
-
- normal+TBC maps
- 8323198=0x7F007E=0x400000 + 0x200000 + 0x100000 + 0x80000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x10 + 0x8 + 0x4 + 0x2
- */
-
- // if we were on a transport, leave
- if (!(options & TELE_TO_NOT_LEAVE_TRANSPORT) && m_transport)
- {
- m_transport->RemovePassenger(this);
- m_transport = NULL;
- m_movementInfo.t_x = 0.0f;
- m_movementInfo.t_y = 0.0f;
- m_movementInfo.t_z = 0.0f;
- m_movementInfo.t_o = 0.0f;
- m_movementInfo.t_time = 0;
- }
-
- SetSemaphoreTeleport(true);
-
- // The player was ported to another map and looses the duel immediatly.
- // We have to perform this check before the teleport, otherwise the
- // ObjectAccessor won't find the flag.
- if (duel && this->GetMapId()!=mapid)
- {
- GameObject* obj = ObjectAccessor::GetGameObject(*this, GetUInt64Value(PLAYER_DUEL_ARBITER));
- if (obj)
- DuelComplete(DUEL_FLED);
- }
-
- // reset movement flags at teleport, because player will continue move with these flags after teleport
- SetUnitMovementFlags(0);
-
- if ((this->GetMapId() == mapid) && (!m_transport))
- {
- // prepare zone change detect
- uint32 old_zone = GetZoneId();
-
- // near teleport
- if(!GetSession()->PlayerLogout())
- {
- WorldPacket data;
- BuildTeleportAckMsg(&data, x, y, z, orientation);
- GetSession()->SendPacket(&data);
- SetPosition( x, y, z, orientation, true);
- }
- else
- // this will be used instead of the current location in SaveToDB
- m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
-
- //BuildHeartBeatMsg(&data);
- //SendMessageToSet(&data, true);
- if (!(options & TELE_TO_NOT_UNSUMMON_PET))
- {
- //same map, only remove pet if out of range
- if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE))
- {
- if(pet->isControlled() && !pet->isTemporarySummoned() )
- m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
- else
- m_temporaryUnsummonedPetNumber = 0;
-
- RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
- }
- }
-
- if(!(options & TELE_TO_NOT_LEAVE_COMBAT))
- CombatStop();
-
- if (!(options & TELE_TO_NOT_UNSUMMON_PET))
- {
- // resummon pet
- if(pet && m_temporaryUnsummonedPetNumber)
- {
- Pet* NewPet = new Pet;
- if(!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
- delete NewPet;
-
- m_temporaryUnsummonedPetNumber = 0;
- }
- }
-
- 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())
- {
- // honorless target
- if(pvpInfo.inHostileArea)
- CastSpell(this, 2479, true);
- }
- }
- else
- {
- // far teleport to another map
- Map* oldmap = IsInWorld() ? MapManager::Instance().GetMap(GetMapId(), this) : NULL;
- // check if we can enter before stopping combat / removing pet / totems / interrupting spells
-
- // Check enter rights before map getting to avoid creating instance copy for player
- // this check not dependent from map instance copy and same for all instance copies of selected map
- if (!MapManager::Instance().CanPlayerEnter(mapid, this))
- {
- SetSemaphoreTeleport(false);
- return false;
- }
-
- // If the map is not created, assume it is possible to enter it.
- // It will be created in the WorldPortAck.
- Map *map = MapManager::Instance().FindMap(mapid);
- if (!map || map->CanEnter(this))
- {
- SetSelection(0);
-
- CombatStop();
-
- ResetContestedPvP();
-
- // remove player from battleground on far teleport (when changing maps)
- if(BattleGround const* bg = GetBattleGround())
- {
- // Note: at battleground join battleground id set before teleport
- // and we already will found "current" battleground
- // just need check that this is targeted map or leave
- if(bg->GetMapId() != mapid)
- LeaveBattleground(false); // don't teleport to entry point
- }
-
- // remove pet on map change
- if (pet)
- {
- //leaving map -> delete pet right away (doing this later will cause problems)
- if(pet->isControlled() && !pet->isTemporarySummoned())
- m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
- else
- m_temporaryUnsummonedPetNumber = 0;
-
- RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
- }
-
- // remove all dyn objects
- RemoveAllDynObjects();
-
- // stop spellcasting
- // not attempt interrupt teleportation spell at caster teleport
- if(!(options & TELE_TO_SPELL))
- if(IsNonMeleeSpellCasted(true))
- InterruptNonMeleeSpells(true);
-
- if(!GetSession()->PlayerLogout())
- {
- // send transfer packets
- WorldPacket data(SMSG_TRANSFER_PENDING, (4+4+4));
- data << uint32(mapid);
- if (m_transport)
- {
- data << m_transport->GetEntry() << GetMapId();
- }
- GetSession()->SendPacket(&data);
-
- data.Initialize(SMSG_NEW_WORLD, (20));
- if (m_transport)
- {
- data << (uint32)mapid << m_movementInfo.t_x << m_movementInfo.t_y << m_movementInfo.t_z << m_movementInfo.t_o;
- }
- else
- {
- data << (uint32)mapid << (float)x << (float)y << (float)z << (float)orientation;
- }
- GetSession()->SendPacket( &data );
- SendSavedInstances();
-
- // remove from old map now
- if(oldmap) oldmap->Remove(this, false);
- }
-
- // new final coordinates
- float final_x = x;
- float final_y = y;
- float final_z = z;
- float final_o = orientation;
-
- if(m_transport)
- {
- final_x += m_movementInfo.t_x;
- final_y += m_movementInfo.t_y;
- final_z += m_movementInfo.t_z;
- final_o += m_movementInfo.t_o;
- }
-
- m_teleport_dest = WorldLocation(mapid, final_x, final_y, final_z, final_o);
- // if the player is saved before worldportack (at logout for example)
- // this will be used instead of the current location in SaveToDB
-
- RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP);
-
- // move packet sent by client always after far teleport
- // SetPosition(final_x, final_y, final_z, final_o, true);
- SetDontMove(true);
-
- // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
- }
- else
- return false;
- }
- return true;
-}
-
-void Player::AddToWorld()
-{
- ///- Do not add/remove the player from the object storage
- ///- It will crash when updating the ObjectAccessor
- ///- The player should only be added when logging in
- Unit::AddToWorld();
-
- for(int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++)
- {
- if(m_items[i])
- m_items[i]->AddToWorld();
- }
-}
-
-void Player::RemoveFromWorld()
-{
- // cleanup
- if(IsInWorld())
- {
- ///- Release charmed creatures, unsummon totems and remove pets/guardians
- Uncharm();
- UnsummonAllTotems();
- RemoveMiniPet();
- RemoveGuardians();
- }
-
- for(int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++)
- {
- if(m_items[i])
- m_items[i]->RemoveFromWorld();
- }
-
- ///- Do not add/remove the player from the object storage
- ///- It will crash when updating the ObjectAccessor
- ///- The player should only be removed when logging out
- Unit::RemoveFromWorld();
-}
-
-void Player::RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker )
-{
- float addRage;
-
- float rageconversion = ((0.0091107836 * getLevel()*getLevel())+3.225598133*getLevel())+4.2652911;
-
- if(attacker)
- {
- addRage = ((damage/rageconversion*7.5 + weaponSpeedHitFactor)/2);
-
- // talent who gave more rage on attack
- addRage *= 1.0f + GetTotalAuraModifier(SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT) / 100.0f;
- }
- else
- {
- addRage = damage/rageconversion*2.5;
-
- // Berserker Rage effect
- if(HasAura(18499,0))
- addRage *= 1.3;
- }
-
- addRage *= sWorld.getRate(RATE_POWER_RAGE_INCOME);
-
- ModifyPower(POWER_RAGE, uint32(addRage*10));
-}
-
-void Player::RegenerateAll()
-{
- if (m_regenTimer != 0)
- return;
- uint32 regenDelay = 2000;
-
- // Not in combat or they have regeneration
- if( !isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) ||
- HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT) || IsPolymorphed() )
- {
- RegenerateHealth();
- if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN))
- Regenerate(POWER_RAGE);
- }
-
- Regenerate( POWER_ENERGY );
-
- Regenerate( POWER_MANA );
-
- m_regenTimer = regenDelay;
-}
-
-void Player::Regenerate(Powers power)
-{
- uint32 curValue = GetPower(power);
- uint32 maxValue = GetMaxPower(power);
-
- float addvalue = 0.0f;
-
- switch (power)
- {
- case POWER_MANA:
- {
- bool recentCast = IsUnderLastManaUseEffect();
- float ManaIncreaseRate = sWorld.getRate(RATE_POWER_MANA);
- if (recentCast)
- {
- // Mangos Updates Mana in intervals of 2s, which is correct
- addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT) * ManaIncreaseRate * 2.00f;
- }
- else
- {
- addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN) * ManaIncreaseRate * 2.00f;
- }
- } break;
- case POWER_RAGE: // Regenerate rage
- {
- float RageDecreaseRate = sWorld.getRate(RATE_POWER_RAGE_LOSS);
- addvalue = 30 * RageDecreaseRate; // 3 rage by tick
- } break;
- case POWER_ENERGY: // Regenerate energy (rogue)
- addvalue = 20;
- break;
- case POWER_FOCUS:
- case POWER_HAPPINESS:
- break;
- }
-
- // Mana regen calculated in Player::UpdateManaRegen()
- // Exist only for POWER_MANA, POWER_ENERGY, POWER_FOCUS auras
- if(power != POWER_MANA)
- {
- AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
- for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
- if ((*i)->GetModifier()->m_miscvalue == power)
- addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f;
- }
-
- if (power != POWER_RAGE)
- {
- curValue += uint32(addvalue);
- if (curValue > maxValue)
- curValue = maxValue;
- }
- else
- {
- if(curValue <= uint32(addvalue))
- curValue = 0;
- else
- curValue -= uint32(addvalue);
- }
- SetPower(power, curValue);
-}
-
-void Player::RegenerateHealth()
-{
- uint32 curValue = GetHealth();
- uint32 maxValue = GetMaxHealth();
-
- if (curValue >= maxValue) return;
-
- float HealthIncreaseRate = sWorld.getRate(RATE_HEALTH);
-
- float addvalue = 0.0f;
-
- // polymorphed case
- if ( IsPolymorphed() )
- addvalue = GetMaxHealth()/3;
- // normal regen case (maybe partly in combat case)
- else if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) )
- {
- addvalue = OCTRegenHPPerSpirit()* HealthIncreaseRate;
- if (!isInCombat())
- {
- AuraList const& mModHealthRegenPct = GetAurasByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
- for(AuraList::const_iterator i = mModHealthRegenPct.begin(); i != mModHealthRegenPct.end(); ++i)
- addvalue *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f;
- }
- else if(HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT))
- addvalue *= GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT) / 100.0f;
-
- if(!IsStandState())
- addvalue *= 1.5;
- }
-
- // always regeneration bonus (including combat)
- addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT);
-
- if(addvalue < 0)
- addvalue = 0;
-
- ModifyHealth(int32(addvalue));
-}
-
-bool Player::CanInteractWithNPCs(bool alive) const
-{
- if(alive && !isAlive())
- return false;
- if(isInFlight())
- return false;
-
- return true;
-}
-
-bool Player::IsUnderWater() const
-{
- return IsInWater() &&
- GetPositionZ() < (MapManager::Instance().GetBaseMap(GetMapId())->GetWaterLevel(GetPositionX(),GetPositionY())-2);
-}
-
-void Player::SetInWater(bool apply)
-{
- if(m_isInWater==apply)
- return;
-
- //define player in water by opcodes
- //move player's guid into HateOfflineList of those mobs
- //which can't swim and move guid back into ThreatList when
- //on surface.
- //TODO: exist also swimming mobs, and function must be symmetric to enter/leave water
- m_isInWater = apply;
-
- // remove auras that need water/land
- RemoveAurasWithInterruptFlags(apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
-
- getHostilRefManager().updateThreatTables();
-}
-
-void Player::SetGameMaster(bool on)
-{
- if(on)
- {
- m_ExtraFlags |= PLAYER_EXTRA_GM_ON;
- setFaction(35);
- SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
-
- RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
- ResetContestedPvP();
-
- getHostilRefManager().setOnlineOfflineState(false);
- CombatStop();
- }
- else
- {
- m_ExtraFlags &= ~ PLAYER_EXTRA_GM_ON;
- setFactionForRace(getRace());
- RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
-
- // restore FFA PvP Server state
- if(sWorld.IsFFAPvPRealm())
- SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
-
- // restore FFA PvP area state, remove not allowed for GM mounts
- UpdateArea(m_areaUpdateId);
-
- getHostilRefManager().setOnlineOfflineState(true);
- }
-
- ObjectAccessor::UpdateVisibilityForPlayer(this);
-}
-
-void Player::SetGMVisible(bool on)
-{
- if(on)
- {
- m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; //remove flag
-
- // Reapply stealth/invisibility if active or show if not any
- if(HasAuraType(SPELL_AURA_MOD_STEALTH))
- SetVisibility(VISIBILITY_GROUP_STEALTH);
- else if(HasAuraType(SPELL_AURA_MOD_INVISIBILITY))
- SetVisibility(VISIBILITY_GROUP_INVISIBILITY);
- else
- SetVisibility(VISIBILITY_ON);
- }
- else
- {
- m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE; //add flag
-
- SetAcceptWhispers(false);
- SetGameMaster(true);
-
- SetVisibility(VISIBILITY_OFF);
- }
-}
-
-bool Player::IsGroupVisibleFor(Player* p) const
-{
- switch(sWorld.getConfig(CONFIG_GROUP_VISIBILITY))
- {
- default: return IsInSameGroupWith(p);
- case 1: return IsInSameRaidWith(p);
- case 2: return GetTeam()==p->GetTeam();
- }
-}
-
-bool Player::IsInSameGroupWith(Player const* p) const
-{
- return p==this || GetGroup() != NULL &&
- GetGroup() == p->GetGroup() &&
- GetGroup()->SameSubGroup((Player*)this, (Player*)p);
-}
-
-///- If the player is invited, remove him. If the group if then only 1 person, disband the group.
-/// \todo Shouldn't we also check if there is no other invitees before disbanding the group?
-void Player::UninviteFromGroup()
-{
- if(GetGroupInvite()) // uninvited invitee
- {
- Group* group = GetGroupInvite();
- group->RemoveInvite(this);
-
- if(group->GetMembersCount() <= 1) // group has just 1 member => disband
- {
- if(group->IsCreated())
- {
- group->Disband(true);
- objmgr.RemoveGroup(group);
- }
- else
- group->RemoveAllInvites();
-
- delete group;
- }
- }
-}
-
-void Player::RemoveFromGroup(Group* group, uint64 guid)
-{
- if(group)
- {
- if (group->RemoveMember(guid, 0) <= 1)
- {
- // group->Disband(); already disbanded in RemoveMember
- objmgr.RemoveGroup(group);
- delete group;
- // removemember sets the player's group pointer to NULL
- }
- }
-}
-
-void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 RestXP)
-{
- WorldPacket data(SMSG_LOG_XPGAIN, 21);
- data << uint64(victim ? victim->GetGUID() : 0); // guid
- data << uint32(GivenXP+RestXP); // given experience
- data << uint8(victim ? 0 : 1); // 00-kill_xp type, 01-non_kill_xp type
- if(victim)
- {
- data << uint32(GivenXP); // experience without rested bonus
- data << float(1); // 1 - none 0 - 100% group bonus output
- }
- data << uint8(0); // new 2.4.0
- GetSession()->SendPacket(&data);
-}
-
-void Player::GiveXP(uint32 xp, Unit* victim)
-{
- if ( xp < 1 )
- return;
-
- if(!isAlive())
- return;
-
- uint32 level = getLevel();
-
- // XP to money conversion processed in Player::RewardQuest
- if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- return;
-
- // handle SPELL_AURA_MOD_XP_PCT auras
- Unit::AuraList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_XP_PCT);
- for(Unit::AuraList::const_iterator i = ModXPPctAuras.begin();i != ModXPPctAuras.end(); ++i)
- xp = uint32(xp*(1.0f + (*i)->GetModifier()->m_amount / 100.0f));
-
- // XP resting bonus for kill
- uint32 rested_bonus_xp = victim ? GetXPRestBonus(xp) : 0;
-
- SendLogXPGain(xp,victim,rested_bonus_xp);
-
- uint32 curXP = GetUInt32Value(PLAYER_XP);
- uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
- uint32 newXP = curXP + xp + rested_bonus_xp;
-
- while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
- {
- newXP -= nextLvlXP;
-
- if ( level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
- GiveLevel(level + 1);
-
- level = getLevel();
- nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
- }
-
- SetUInt32Value(PLAYER_XP, newXP);
-}
-
-// Update player to next level
-// Current player experience not update (must be update by caller)
-void Player::GiveLevel(uint32 level)
-{
- if ( level == getLevel() )
- return;
-
- PlayerLevelInfo info;
- objmgr.GetPlayerLevelInfo(getRace(),getClass(),level,&info);
-
- PlayerClassLevelInfo classInfo;
- objmgr.GetPlayerClassLevelInfo(getClass(),level,&classInfo);
-
- // send levelup info to client
- WorldPacket data(SMSG_LEVELUP_INFO, (4+4+MAX_POWERS*4+MAX_STATS*4));
- data << uint32(level);
- data << uint32(int32(classInfo.basehealth) - int32(GetCreateHealth()));
- // for(int i = 0; i < MAX_POWERS; ++i) // Powers loop (0-6)
- data << uint32(int32(classInfo.basemana) - int32(GetCreateMana()));
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- data << uint32(0);
- // end for
- for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) // Stats loop (0-4)
- data << uint32(int32(info.stats[i]) - GetCreateStat(Stats(i)));
-
- GetSession()->SendPacket(&data);
-
- SetUInt32Value(PLAYER_NEXT_LEVEL_XP, MaNGOS::XP::xp_to_level(level));
-
- //update level, max level of skills
- if(getLevel()!= level)
- m_Played_time[1] = 0; // Level Played Time reset
- SetLevel(level);
- UpdateMaxSkills();
-
- // save base values (bonuses already included in stored stats
- for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
- SetCreateStat(Stats(i), info.stats[i]);
-
- SetCreateHealth(classInfo.basehealth);
- SetCreateMana(classInfo.basemana);
-
- InitTalentForLevel();
- InitTaxiNodesForLevel();
-
- UpdateAllStats();
-
- if(sWorld.getConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up
- UpdateSkillsToMaxSkillsForLevel();
-
- // set current level health and mana/energy to maximum after applying all mods.
- SetHealth(GetMaxHealth());
- SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
- SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
- if(GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
- SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
- SetPower(POWER_FOCUS, 0);
- SetPower(POWER_HAPPINESS, 0);
-
- // give level to summoned pet
- Pet* pet = GetPet();
- if(pet && pet->getPetType()==SUMMON_PET)
- pet->GivePetLevel(level);
-}
-
-void Player::InitTalentForLevel()
-{
- uint32 level = getLevel();
- // talents base at level diff ( talents = level - 9 but some can be used already)
- if(level < 10)
- {
- // Remove all talent points
- if(m_usedTalentCount > 0) // Free any used talents
- {
- resetTalents(true);
- SetFreeTalentPoints(0);
- }
- }
- else
- {
- uint32 talentPointsForLevel = uint32((level-9)*sWorld.getRate(RATE_TALENT));
- // if used more that have then reset
- if(m_usedTalentCount > talentPointsForLevel)
- {
- if (GetSession()->GetSecurity() < SEC_ADMINISTRATOR)
- resetTalents(true);
- else
- SetFreeTalentPoints(0);
- }
- // else update amount of free points
- else
- SetFreeTalentPoints(talentPointsForLevel-m_usedTalentCount);
- }
-}
-
-void Player::InitStatsForLevel(bool reapplyMods)
-{
- if(reapplyMods) //reapply stats values only on .reset stats (level) command
- _RemoveAllStatBonuses();
-
- PlayerClassLevelInfo classInfo;
- objmgr.GetPlayerClassLevelInfo(getClass(),getLevel(),&classInfo);
-
- PlayerLevelInfo info;
- objmgr.GetPlayerLevelInfo(getRace(),getClass(),getLevel(),&info);
-
- SetUInt32Value(PLAYER_FIELD_MAX_LEVEL, sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) );
- SetUInt32Value(PLAYER_NEXT_LEVEL_XP, MaNGOS::XP::xp_to_level(getLevel()));
-
- UpdateMaxSkills ();
-
- // set default cast time multiplier
- SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
-
- // reset size before reapply auras
- SetFloatValue(OBJECT_FIELD_SCALE_X,1.0f);
-
- // save base values (bonuses already included in stored stats
- for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
- SetCreateStat(Stats(i), info.stats[i]);
-
- for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
- SetStat(Stats(i), info.stats[i]);
-
- SetCreateHealth(classInfo.basehealth);
-
- //set create powers
- SetCreateMana(classInfo.basemana);
-
- SetArmor(int32(m_createStats[STAT_AGILITY]*2));
-
- InitStatBuffMods();
-
- //reset rating fields values
- for(uint16 index = PLAYER_FIELD_COMBAT_RATING_1; index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index)
- SetUInt32Value(index, 0);
-
- SetUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS,0);
- for (int i = 0; i < 7; i++)
- {
- SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i, 0);
- SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, 0);
- SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+i, 1.00f);
- }
-
- //reset attack power, damage and attack speed fields
- SetFloatValue(UNIT_FIELD_BASEATTACKTIME, 2000.0f );
- SetFloatValue(UNIT_FIELD_BASEATTACKTIME + 1, 2000.0f ); // offhand attack time
- SetFloatValue(UNIT_FIELD_RANGEDATTACKTIME, 2000.0f );
-
- SetFloatValue(UNIT_FIELD_MINDAMAGE, 0.0f );
- SetFloatValue(UNIT_FIELD_MAXDAMAGE, 0.0f );
- SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, 0.0f );
- SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, 0.0f );
- SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, 0.0f );
- SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, 0.0f );
-
- SetUInt32Value(UNIT_FIELD_ATTACK_POWER, 0 );
- SetUInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, 0 );
- SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER,0.0f);
- SetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0 );
- SetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MODS,0 );
- SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER,0.0f);
-
- // Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
- SetFloatValue(PLAYER_CRIT_PERCENTAGE,0.0f);
- SetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE,0.0f);
- SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE,0.0f);
-
- // Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
- for (uint8 i = 0; i < 7; ++i)
- SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1+i, 0.0f);
-
- SetFloatValue(PLAYER_PARRY_PERCENTAGE, 0.0f);
- SetFloatValue(PLAYER_BLOCK_PERCENTAGE, 0.0f);
- SetUInt32Value(PLAYER_SHIELD_BLOCK, 0);
-
- // Dodge percentage
- SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f);
-
- // set armor (resistance 0) to original value (create_agility*2)
- SetArmor(int32(m_createStats[STAT_AGILITY]*2));
- SetResistanceBuffMods(SpellSchools(0), true, 0.0f);
- SetResistanceBuffMods(SpellSchools(0), false, 0.0f);
- // set other resistance to original value (0)
- for (int i = 1; i < MAX_SPELL_SCHOOL; i++)
- {
- SetResistance(SpellSchools(i), 0);
- SetResistanceBuffMods(SpellSchools(i), true, 0.0f);
- SetResistanceBuffMods(SpellSchools(i), false, 0.0f);
- }
-
- SetUInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,0);
- SetUInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,0);
- for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
- {
- SetFloatValue(UNIT_FIELD_POWER_COST_MODIFIER+i,0.0f);
- SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,0.0f);
- }
- // Init data for form but skip reapply item mods for form
- InitDataForForm(reapplyMods);
-
- // save new stats
- for (int i = POWER_MANA; i < MAX_POWERS; i++)
- SetMaxPower(Powers(i), uint32(GetCreatePowers(Powers(i))));
-
- SetMaxHealth(classInfo.basehealth); // stamina bonus will applied later
-
- // cleanup mounted state (it will set correctly at aura loading if player saved at mount.
- SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
-
- // cleanup unit flags (will be re-applied if need at aura load).
- RemoveFlag( UNIT_FIELD_FLAGS,
- UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 |
- UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED |
- UNIT_FLAG_DISABLE_ROTATE | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED |
- UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_NOT_SELECTABLE |
- UNIT_FLAG_SKINNABLE | UNIT_FLAG_MOUNT | UNIT_FLAG_TAXI_FLIGHT );
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE ); // must be set
-
- // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example.
- RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST | PLAYER_FLAGS_FFA_PVP);
-
- SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x00); // one form stealth modified bytes
-
- // restore if need some important flags
- SetUInt32Value(PLAYER_FIELD_BYTES2, 0 ); // flags empty by default
-
- if(reapplyMods) //reapply stats values only on .reset stats (level) command
- _ApplyAllStatBonuses();
-
- // set current level health and mana/energy to maximum after applying all mods.
- SetHealth(GetMaxHealth());
- SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
- SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
- if(GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
- SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
- SetPower(POWER_FOCUS, 0);
- SetPower(POWER_HAPPINESS, 0);
-}
-
-void Player::SendInitialSpells()
-{
- uint16 spellCount = 0;
-
- WorldPacket data(SMSG_INITIAL_SPELLS, (1+2+4*m_spells.size()+2+m_spellCooldowns.size()*(2+2+2+4+4)));
- data << uint8(0);
-
- size_t countPos = data.wpos();
- data << uint16(spellCount); // spell count placeholder
-
- for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- {
- if(itr->second->state == PLAYERSPELL_REMOVED)
- continue;
-
- if(!itr->second->active || itr->second->disabled)
- continue;
-
- data << uint16(itr->first);
- //data << uint16(itr->second->slotId);
- data << uint16(0); // it's not slot id
-
- spellCount +=1;
- }
-
- data.put<uint16>(countPos,spellCount); // write real count value
-
- uint16 spellCooldowns = m_spellCooldowns.size();
- data << uint16(spellCooldowns);
- for(SpellCooldowns::const_iterator itr=m_spellCooldowns.begin(); itr!=m_spellCooldowns.end(); itr++)
- {
- SpellEntry const *sEntry = sSpellStore.LookupEntry(itr->first);
- if(!sEntry)
- continue;
-
- data << uint16(itr->first);
-
- time_t cooldown = 0;
- time_t curTime = time(NULL);
- if(itr->second.end > curTime)
- cooldown = (itr->second.end-curTime)*1000;
-
- data << uint16(itr->second.itemid); // cast item id
- data << uint16(sEntry->Category); // spell category
- if(sEntry->Category) // may be wrong, but anyway better than nothing...
- {
- data << uint32(0);
- data << uint32(cooldown);
- }
- else
- {
- data << uint32(cooldown);
- data << uint32(0);
- }
- }
-
- GetSession()->SendPacket(&data);
-
- sLog.outDetail( "CHARACTER: Sent Initial Spells" );
-}
-
-void Player::RemoveMail(uint32 id)
-{
- for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();++itr)
- {
- if ((*itr)->messageID == id)
- {
- //do not delete item, because Player::removeMail() is called when returning mail to sender.
- m_mail.erase(itr);
- return;
- }
- }
-}
-
-void Player::SendMailResult(uint32 mailId, uint32 mailAction, uint32 mailError, uint32 equipError, uint32 item_guid, uint32 item_count)
-{
- WorldPacket data(SMSG_SEND_MAIL_RESULT, (4+4+4+(mailError == MAIL_ERR_BAG_FULL?4:(mailAction == MAIL_ITEM_TAKEN?4+4:0))));
- data << (uint32) mailId;
- data << (uint32) mailAction;
- data << (uint32) mailError;
- if ( mailError == MAIL_ERR_BAG_FULL )
- data << (uint32) equipError;
- else if( mailAction == MAIL_ITEM_TAKEN )
- {
- data << (uint32) item_guid; // item guid low?
- data << (uint32) item_count; // item count?
- }
- GetSession()->SendPacket(&data);
-}
-
-void Player::SendNewMail()
-{
- // deliver undelivered mail
- WorldPacket data(SMSG_RECEIVED_MAIL, 4);
- data << (uint32) 0;
- GetSession()->SendPacket(&data);
-}
-
-void Player::UpdateNextMailTimeAndUnreads()
-{
- // calculate next delivery time (min. from non-delivered mails
- // and recalculate unReadMail
- time_t cTime = time(NULL);
- m_nextMailDelivereTime = 0;
- unReadMails = 0;
- for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
- {
- if((*itr)->deliver_time > cTime)
- {
- if(!m_nextMailDelivereTime || m_nextMailDelivereTime > (*itr)->deliver_time)
- m_nextMailDelivereTime = (*itr)->deliver_time;
- }
- else if(((*itr)->checked & MAIL_CHECK_MASK_READ) == 0)
- ++unReadMails;
- }
-}
-
-void Player::AddNewMailDeliverTime(time_t deliver_time)
-{
- if(deliver_time <= time(NULL)) // ready now
- {
- ++unReadMails;
- SendNewMail();
- }
- else // not ready and no have ready mails
- {
- if(!m_nextMailDelivereTime || m_nextMailDelivereTime > deliver_time)
- m_nextMailDelivereTime = deliver_time;
- }
-}
-
-bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, uint16 slot_id, bool disabled)
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
- if (!spellInfo)
- {
- // do character spell book cleanup (all characters)
- if(loading && !learning) // spell load case
- {
- sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.",spell_id);
- CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id);
- }
- else
- sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
-
- return false;
- }
-
- if(!SpellMgr::IsSpellValid(spellInfo,this,false))
- {
- // do character spell book cleanup (all characters)
- if(loading && !learning) // spell load case
- {
- sLog.outError("Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.",spell_id);
- CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id);
- }
- else
- sLog.outError("Player::addSpell: Broken spell #%u learning not allowed.",spell_id);
-
- return false;
- }
-
- PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
-
- bool disabled_case = false;
- bool superceded_old = false;
-
- PlayerSpellMap::iterator itr = m_spells.find(spell_id);
- if (itr != m_spells.end())
- {
- // update active state for known spell
- if(itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled)
- {
- itr->second->active = active;
-
- // loading && !learning == explicitly load from DB and then exist in it already and set correctly
- if(loading && !learning)
- itr->second->state = PLAYERSPELL_UNCHANGED;
- else if(itr->second->state != PLAYERSPELL_NEW)
- itr->second->state = PLAYERSPELL_CHANGED;
-
- if(!active)
- {
- WorldPacket data(SMSG_REMOVED_SPELL, 4);
- data << uint16(spell_id);
- GetSession()->SendPacket(&data);
- }
- return active; // learn (show in spell book if active now)
- }
-
- if(itr->second->disabled != disabled && itr->second->state != PLAYERSPELL_REMOVED)
- {
- if(itr->second->state != PLAYERSPELL_NEW)
- itr->second->state = PLAYERSPELL_CHANGED;
- itr->second->disabled = disabled;
-
- if(disabled)
- return false;
-
- disabled_case = true;
- }
- else switch(itr->second->state)
- {
- case PLAYERSPELL_UNCHANGED: // known saved spell
- return false;
- case PLAYERSPELL_REMOVED: // re-learning removed not saved spell
- {
- delete itr->second;
- m_spells.erase(itr);
- state = PLAYERSPELL_CHANGED;
- break; // need re-add
- }
- default: // known not saved yet spell (new or modified)
- {
- // can be in case spell loading but learned at some previous spell loading
- if(loading && !learning)
- itr->second->state = PLAYERSPELL_UNCHANGED;
-
- return false;
- }
- }
- }
-
- if(!disabled_case) // skip new spell adding if spell already known (disabled spells case)
- {
- // talent: unlearn all other talent ranks (high and low)
- if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id))
- {
- if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
- {
- for(int i=0; i <5; ++i)
- {
- // skip learning spell and no rank spell case
- uint32 rankSpellId = talentInfo->RankID[i];
- if(!rankSpellId || rankSpellId==spell_id)
- continue;
-
- // skip unknown ranks
- if(!HasSpell(rankSpellId))
- continue;
-
- removeSpell(rankSpellId);
- }
- }
- }
- // non talent spell: learn low ranks (recursive call)
- else if(uint32 prev_spell = spellmgr.GetPrevSpellInChain(spell_id))
- {
- if(loading) // at spells loading, no output, but allow save
- addSpell(prev_spell,active,true,loading,SPELL_WITHOUT_SLOT_ID,disabled);
- else // at normal learning
- learnSpell(prev_spell);
- }
-
- PlayerSpell *newspell = new PlayerSpell;
- newspell->active = active;
- newspell->state = state;
- newspell->disabled = disabled;
-
- // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
- if(newspell->active && !newspell->disabled && !SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0)
- {
- for( PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr )
- {
- if(itr->second->state == PLAYERSPELL_REMOVED) continue;
- SpellEntry const *i_spellInfo = sSpellStore.LookupEntry(itr->first);
- if(!i_spellInfo) continue;
-
- if( spellmgr.IsRankSpellDueToSpell(spellInfo,itr->first) )
- {
- if(itr->second->active)
- {
- if(spellmgr.IsHighRankOfSpell(spell_id,itr->first))
- {
- if(!loading) // not send spell (re-/over-)learn packets at loading
- {
- WorldPacket data(SMSG_SUPERCEDED_SPELL, (4));
- data << uint16(itr->first);
- data << uint16(spell_id);
- GetSession()->SendPacket( &data );
- }
-
- // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
- itr->second->active = false;
- itr->second->state = PLAYERSPELL_CHANGED;
- superceded_old = true; // new spell replace old in action bars and spell book.
- }
- else if(spellmgr.IsHighRankOfSpell(itr->first,spell_id))
- {
- if(!loading) // not send spell (re-/over-)learn packets at loading
- {
- WorldPacket data(SMSG_SUPERCEDED_SPELL, (4));
- data << uint16(spell_id);
- data << uint16(itr->first);
- GetSession()->SendPacket( &data );
- }
-
- // mark new spell as disable (not learned yet for client and will not learned)
- newspell->active = false;
- if(newspell->state != PLAYERSPELL_NEW)
- newspell->state = PLAYERSPELL_CHANGED;
- }
- }
- }
- }
- }
-
- uint16 tmpslot=slot_id;
-
- if (tmpslot == SPELL_WITHOUT_SLOT_ID)
- {
- uint16 maxid = 0;
- PlayerSpellMap::iterator itr;
- for (itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- {
- if(itr->second->state == PLAYERSPELL_REMOVED)
- continue;
- if (itr->second->slotId > maxid)
- maxid = itr->second->slotId;
- }
- tmpslot = maxid + 1;
- }
-
- newspell->slotId = tmpslot;
- m_spells[spell_id] = newspell;
-
- // return false if spell disabled
- if (newspell->disabled)
- return false;
- }
-
- uint32 talentCost = GetTalentSpellCost(spell_id);
-
- // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
- // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
- if( talentCost > 0 && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_LEARN_SPELL) )
- {
- // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
- CastSpell(this, spell_id, true);
- }
- // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
- else if (IsPassiveSpell(spell_id))
- {
- // if spell doesn't require a stance or the player is in the required stance
- if( ( !spellInfo->Stances &&
- spell_id != 5420 && spell_id != 5419 && spell_id != 7376 &&
- spell_id != 7381 && spell_id != 21156 && spell_id != 21009 &&
- spell_id != 21178 && spell_id != 33948 && spell_id != 40121 ) ||
- m_form != 0 && (spellInfo->Stances & (1<<(m_form-1))) ||
- (spell_id == 5420 && m_form == FORM_TREE) ||
- (spell_id == 5419 && m_form == FORM_TRAVEL) ||
- (spell_id == 7376 && m_form == FORM_DEFENSIVESTANCE) ||
- (spell_id == 7381 && m_form == FORM_BERSERKERSTANCE) ||
- (spell_id == 21156 && m_form == FORM_BATTLESTANCE)||
- (spell_id == 21178 && (m_form == FORM_BEAR || m_form == FORM_DIREBEAR) ) ||
- (spell_id == 33948 && m_form == FORM_FLIGHT) ||
- (spell_id == 40121 && m_form == FORM_FLIGHT_EPIC) )
- //Check CasterAuraStates
- if (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState)))
- CastSpell(this, spell_id, true);
- }
- else if( IsSpellHaveEffect(spellInfo,SPELL_EFFECT_SKILL_STEP) )
- {
- CastSpell(this, spell_id, true);
- return false;
- }
-
- // update used talent points count
- m_usedTalentCount += talentCost;
-
- // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
- if(uint32 freeProfs = GetFreePrimaryProffesionPoints())
- {
- if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell_id))
- SetFreePrimaryProffesions(freeProfs-1);
- }
-
- // add dependent skills
- uint16 maxskill = GetMaxSkillValueForLevel();
-
- SpellLearnSkillNode const* spellLearnSkill = spellmgr.GetSpellLearnSkill(spell_id);
-
- if(spellLearnSkill)
- {
- uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
- uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
-
- if(skill_value < spellLearnSkill->value)
- skill_value = spellLearnSkill->value;
-
- uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue;
-
- if(skill_max_value < new_skill_max_value)
- skill_max_value = new_skill_max_value;
-
- SetSkill(spellLearnSkill->skill,skill_value,skill_max_value);
- }
- else
- {
- // not ranked skills
- SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id);
- SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id);
-
- for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
- {
- SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
- if(!pSkill)
- continue;
-
- if(HasSkill(pSkill->id))
- continue;
-
- if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
- // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
- pSkill->id==SKILL_POISONS && _spell_idx->second->max_value==0 ||
- // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
- pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 )
- {
- switch(GetSkillRangeType(pSkill,_spell_idx->second->racemask!=0))
- {
- case SKILL_RANGE_LANGUAGE:
- SetSkill(pSkill->id, 300, 300 );
- break;
- case SKILL_RANGE_LEVEL:
- SetSkill(pSkill->id, 1, GetMaxSkillValueForLevel() );
- break;
- case SKILL_RANGE_MONO:
- SetSkill(pSkill->id, 1, 1 );
- break;
- default:
- break;
- }
- }
- }
- }
-
- // learn dependent spells
- SpellLearnSpellMap::const_iterator spell_begin = spellmgr.GetBeginSpellLearnSpell(spell_id);
- SpellLearnSpellMap::const_iterator spell_end = spellmgr.GetEndSpellLearnSpell(spell_id);
-
- for(SpellLearnSpellMap::const_iterator itr = spell_begin; itr != spell_end; ++itr)
- {
- if(!itr->second.autoLearned)
- {
- if(loading) // at spells loading, no output, but allow save
- addSpell(itr->second.spell,true,true,loading);
- else // at normal learning
- learnSpell(itr->second.spell);
- }
- }
-
- // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell
- return active && !disabled && !superceded_old;
-}
-
-void Player::learnSpell(uint32 spell_id)
-{
- PlayerSpellMap::iterator itr = m_spells.find(spell_id);
-
- bool disabled = (itr != m_spells.end()) ? itr->second->disabled : false;
- bool active = disabled ? itr->second->active : true;
-
- bool learning = addSpell(spell_id,active);
-
- // learn all disabled higher ranks (recursive)
- SpellChainMapNext const& nextMap = spellmgr.GetSpellChainNext();
- for(SpellChainMapNext::const_iterator i = nextMap.lower_bound(spell_id); i != nextMap.upper_bound(spell_id); ++i)
- {
- PlayerSpellMap::iterator iter = m_spells.find(i->second);
- if (disabled && iter != m_spells.end() && iter->second->disabled)
- learnSpell(i->second);
- }
-
- // prevent duplicated entires in spell book
- if(!learning)
- return;
-
- WorldPacket data(SMSG_LEARNED_SPELL, 4);
- data << uint32(spell_id);
- GetSession()->SendPacket(&data);
-}
-
-void Player::removeSpell(uint32 spell_id, bool disabled)
-{
- PlayerSpellMap::iterator itr = m_spells.find(spell_id);
- if (itr == m_spells.end())
- return;
-
- if(itr->second->state == PLAYERSPELL_REMOVED || disabled && itr->second->disabled)
- return;
-
- // unlearn non talent higher ranks (recursive)
- SpellChainMapNext const& nextMap = spellmgr.GetSpellChainNext();
- for(SpellChainMapNext::const_iterator itr2 = nextMap.lower_bound(spell_id); itr2 != nextMap.upper_bound(spell_id); ++itr2)
- if(HasSpell(itr2->second) && !GetTalentSpellPos(itr2->second))
- removeSpell(itr2->second,disabled);
-
- // removing
- WorldPacket data(SMSG_REMOVED_SPELL, 4);
- data << uint16(spell_id);
- GetSession()->SendPacket(&data);
-
- if (disabled)
- {
- itr->second->disabled = disabled;
- if(itr->second->state != PLAYERSPELL_NEW)
- itr->second->state = PLAYERSPELL_CHANGED;
- }
- else
- {
- if(itr->second->state == PLAYERSPELL_NEW)
- {
- delete itr->second;
- m_spells.erase(itr);
- }
- else
- itr->second->state = PLAYERSPELL_REMOVED;
- }
-
- RemoveAurasDueToSpell(spell_id);
-
- // remove pet auras
- if(PetAura const* petSpell = spellmgr.GetPetAura(spell_id))
- RemovePetAura(petSpell);
-
- // free talent points
- uint32 talentCosts = GetTalentSpellCost(spell_id);
- if(talentCosts > 0)
- {
- if(talentCosts < m_usedTalentCount)
- m_usedTalentCount -= talentCosts;
- else
- m_usedTalentCount = 0;
- }
-
- // update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
- if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell_id))
- {
- uint32 freeProfs = GetFreePrimaryProffesionPoints()+1;
- if(freeProfs <= sWorld.getConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL))
- SetFreePrimaryProffesions(freeProfs);
- }
-
- // remove dependent skill
- SpellLearnSkillNode const* spellLearnSkill = spellmgr.GetSpellLearnSkill(spell_id);
- if(spellLearnSkill)
- {
- uint32 prev_spell = spellmgr.GetPrevSpellInChain(spell_id);
- if(!prev_spell) // first rank, remove skill
- SetSkill(spellLearnSkill->skill,0,0);
- else
- {
- // search prev. skill setting by spell ranks chain
- SpellLearnSkillNode const* prevSkill = spellmgr.GetSpellLearnSkill(prev_spell);
- while(!prevSkill && prev_spell)
- {
- prev_spell = spellmgr.GetPrevSpellInChain(prev_spell);
- prevSkill = spellmgr.GetSpellLearnSkill(spellmgr.GetFirstSpellInChain(prev_spell));
- }
-
- if(!prevSkill) // not found prev skill setting, remove skill
- SetSkill(spellLearnSkill->skill,0,0);
- else // set to prev. skill setting values
- {
- uint32 skill_value = GetPureSkillValue(prevSkill->skill);
- uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill);
-
- if(skill_value > prevSkill->value)
- skill_value = prevSkill->value;
-
- uint32 new_skill_max_value = prevSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : prevSkill->maxvalue;
-
- if(skill_max_value > new_skill_max_value)
- skill_max_value = new_skill_max_value;
-
- SetSkill(prevSkill->skill,skill_value,skill_max_value);
- }
- }
-
- }
- else
- {
- // not ranked skills
- SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id);
- SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id);
-
- for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
- {
- SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
- if(!pSkill)
- continue;
-
- if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
- // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
- pSkill->id==SKILL_POISONS && _spell_idx->second->max_value==0 ||
- // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
- pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 )
- {
- // not reset skills for professions and racial abilities
- if( (pSkill->categoryId==SKILL_CATEGORY_SECONDARY || pSkill->categoryId==SKILL_CATEGORY_PROFESSION) &&
- (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask!=0) )
- continue;
-
- SetSkill(pSkill->id, 0, 0 );
- }
- }
- }
-
- // remove dependent spells
- SpellLearnSpellMap::const_iterator spell_begin = spellmgr.GetBeginSpellLearnSpell(spell_id);
- SpellLearnSpellMap::const_iterator spell_end = spellmgr.GetEndSpellLearnSpell(spell_id);
-
- for(SpellLearnSpellMap::const_iterator itr2 = spell_begin; itr2 != spell_end; ++itr2)
- removeSpell(itr2->second.spell, disabled);
-}
-
-void Player::RemoveArenaSpellCooldowns()
-{
- // remove cooldowns on spells that has < 15 min CD
- SpellCooldowns::iterator itr, next;
- // iterate spell cooldowns
- for(itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); itr = next)
- {
- next = itr;
- ++next;
- SpellEntry const * entry = sSpellStore.LookupEntry(itr->first);
- // check if spellentry is present and if the cooldown is less than 15 mins
- if( entry &&
- entry->RecoveryTime <= 15 * MINUTE * 1000 &&
- entry->CategoryRecoveryTime <= 15 * MINUTE * 1000 )
- {
- // notify player
- WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
- data << uint32(itr->first);
- data << GetGUID();
- GetSession()->SendPacket(&data);
- // remove cooldown
- m_spellCooldowns.erase(itr);
- }
- }
-}
-
-void Player::RemoveAllSpellCooldown()
-{
- if(!m_spellCooldowns.empty())
- {
- for(SpellCooldowns::const_iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); ++itr)
- {
- WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
- data << uint32(itr->first);
- data << uint64(GetGUID());
- GetSession()->SendPacket(&data);
- }
- m_spellCooldowns.clear();
- }
-}
-
-void Player::_LoadSpellCooldowns(QueryResult *result)
-{
- m_spellCooldowns.clear();
-
- //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'",GetGUIDLow());
-
- if(result)
- {
- time_t curTime = time(NULL);
-
- do
- {
- Field *fields = result->Fetch();
-
- uint32 spell_id = fields[0].GetUInt32();
- uint32 item_id = fields[1].GetUInt32();
- time_t db_time = (time_t)fields[2].GetUInt64();
-
- if(!sSpellStore.LookupEntry(spell_id))
- {
- sLog.outError("Player %u have unknown spell %u in `character_spell_cooldown`, skipping.",GetGUIDLow(),spell_id);
- continue;
- }
-
- // skip outdated cooldown
- if(db_time <= curTime)
- continue;
-
- AddSpellCooldown(spell_id, item_id, db_time);
-
- sLog.outDebug("Player (GUID: %u) spell %u, item %u cooldown loaded (%u secs).", GetGUIDLow(), spell_id, item_id, uint32(db_time-curTime));
- }
- while( result->NextRow() );
-
- delete result;
- }
-}
-
-void Player::_SaveSpellCooldowns()
-{
- CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow());
-
- time_t curTime = time(NULL);
-
- // remove outdated and save active
- for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();)
- {
- if(itr->second.end <= curTime)
- m_spellCooldowns.erase(itr++);
- else
- {
- CharacterDatabase.PExecute("INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES ('%u', '%u', '%u', '" I64FMTD "')", GetGUIDLow(), itr->first, itr->second.itemid, uint64(itr->second.end));
- ++itr;
- }
- }
-}
-
-uint32 Player::resetTalentsCost() const
-{
- // The first time reset costs 1 gold
- if(m_resetTalentsCost < 1*GOLD)
- return 1*GOLD;
- // then 5 gold
- else if(m_resetTalentsCost < 5*GOLD)
- return 5*GOLD;
- // After that it increases in increments of 5 gold
- else if(m_resetTalentsCost < 10*GOLD)
- return 10*GOLD;
- else
- {
- uint32 months = (sWorld.GetGameTime() - m_resetTalentsTime)/MONTH;
- if(months > 0)
- {
- // This cost will be reduced by a rate of 5 gold per month
- int32 new_cost = int32(m_resetTalentsCost) - 5*GOLD*months;
- // to a minimum of 10 gold.
- return (new_cost < 10*GOLD ? 10*GOLD : new_cost);
- }
- else
- {
- // After that it increases in increments of 5 gold
- int32 new_cost = m_resetTalentsCost + 5*GOLD;
- // until it hits a cap of 50 gold.
- if(new_cost > 50*GOLD)
- new_cost = 50*GOLD;
- return new_cost;
- }
- }
-}
-
-bool Player::resetTalents(bool no_cost)
-{
- // not need after this call
- if(HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
- {
- m_atLoginFlags = m_atLoginFlags & ~AT_LOGIN_RESET_TALENTS;
- CharacterDatabase.PExecute("UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'", uint32(AT_LOGIN_RESET_TALENTS), GetGUIDLow());
- }
-
- uint32 level = getLevel();
- uint32 talentPointsForLevel = level < 10 ? 0 : uint32((level-9)*sWorld.getRate(RATE_TALENT));
-
- if (m_usedTalentCount == 0)
- {
- SetFreeTalentPoints(talentPointsForLevel);
- return false;
- }
-
- uint32 cost = 0;
-
- if(!no_cost)
- {
- cost = resetTalentsCost();
-
- if (GetMoney() < cost)
- {
- SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
- return false;
- }
- }
-
- for (unsigned int i = 0; i < sTalentStore.GetNumRows(); i++)
- {
- TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
-
- if (!talentInfo) continue;
-
- TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
-
- if(!talentTabInfo)
- continue;
-
- // unlearn only talents for character class
- // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
- // to prevent unexpected lost normal learned spell skip another class talents
- if( (getClassMask() & talentTabInfo->ClassMask) == 0 )
- continue;
-
- for (int j = 0; j < 5; j++)
- {
- for(PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();)
- {
- if(itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled)
- {
- ++itr;
- continue;
- }
-
- // remove learned spells (all ranks)
- uint32 itrFirstId = spellmgr.GetFirstSpellInChain(itr->first);
-
- // unlearn if first rank is talent or learned by talent
- if (itrFirstId == talentInfo->RankID[j] || spellmgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId))
- {
- removeSpell(itr->first,!IsPassiveSpell(itr->first));
- itr = GetSpellMap().begin();
- continue;
- }
- else
- ++itr;
- }
- }
- }
-
- SetFreeTalentPoints(talentPointsForLevel);
-
- if(!no_cost)
- {
- ModifyMoney(-(int32)cost);
-
- m_resetTalentsCost = cost;
- m_resetTalentsTime = time(NULL);
- }
-
- //FIXME: remove pet before or after unlearn spells? for now after unlearn to allow removing of talent related, pet affecting auras
- RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
-
- return true;
-}
-
-bool Player::_removeSpell(uint16 spell_id)
-{
- PlayerSpellMap::iterator itr = m_spells.find(spell_id);
- if (itr != m_spells.end())
- {
- delete itr->second;
- m_spells.erase(itr);
- return true;
- }
- return false;
-}
-
-Mail* Player::GetMail(uint32 id)
-{
- for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); itr++)
- {
- if ((*itr)->messageID == id)
- {
- return (*itr);
- }
- }
- return NULL;
-}
-
-void Player::_SetCreateBits(UpdateMask *updateMask, Player *target) const
-{
- if(target == this)
- {
- Object::_SetCreateBits(updateMask, target);
- }
- else
- {
- for(uint16 index = 0; index < m_valuesCount; index++)
- {
- if(GetUInt32Value(index) != 0 && updateVisualBits.GetBit(index))
- updateMask->SetBit(index);
- }
- }
-}
-
-void Player::_SetUpdateBits(UpdateMask *updateMask, Player *target) const
-{
- if(target == this)
- {
- Object::_SetUpdateBits(updateMask, target);
- }
- else
- {
- Object::_SetUpdateBits(updateMask, target);
- *updateMask &= updateVisualBits;
- }
-}
-
-void Player::InitVisibleBits()
-{
- updateVisualBits.SetCount(PLAYER_END);
-
- updateVisualBits.SetBit(OBJECT_FIELD_GUID);
- updateVisualBits.SetBit(OBJECT_FIELD_TYPE);
- updateVisualBits.SetBit(OBJECT_FIELD_SCALE_X);
-
- updateVisualBits.SetBit(UNIT_FIELD_CHARM);
- updateVisualBits.SetBit(UNIT_FIELD_CHARM+1);
-
- updateVisualBits.SetBit(UNIT_FIELD_SUMMON);
- updateVisualBits.SetBit(UNIT_FIELD_SUMMON+1);
-
- updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY);
-
- updateVisualBits.SetBit(UNIT_FIELD_TARGET);
- updateVisualBits.SetBit(UNIT_FIELD_TARGET+1);
-
- updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT);
- updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT+1);
-
- updateVisualBits.SetBit(UNIT_FIELD_HEALTH);
- updateVisualBits.SetBit(UNIT_FIELD_POWER1);
- updateVisualBits.SetBit(UNIT_FIELD_POWER2);
- updateVisualBits.SetBit(UNIT_FIELD_POWER3);
- updateVisualBits.SetBit(UNIT_FIELD_POWER4);
- updateVisualBits.SetBit(UNIT_FIELD_POWER5);
-
- updateVisualBits.SetBit(UNIT_FIELD_MAXHEALTH);
- updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER1);
- updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER2);
- updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER3);
- updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER4);
- updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER5);
-
- updateVisualBits.SetBit(UNIT_FIELD_LEVEL);
- updateVisualBits.SetBit(UNIT_FIELD_FACTIONTEMPLATE);
- updateVisualBits.SetBit(UNIT_FIELD_BYTES_0);
- updateVisualBits.SetBit(UNIT_FIELD_FLAGS);
- updateVisualBits.SetBit(UNIT_FIELD_FLAGS_2);
- for(uint16 i = UNIT_FIELD_AURA; i < UNIT_FIELD_AURASTATE; ++i)
- updateVisualBits.SetBit(i);
- updateVisualBits.SetBit(UNIT_FIELD_AURASTATE);
- updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME);
- updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 1);
- updateVisualBits.SetBit(UNIT_FIELD_RANGEDATTACKTIME);
- updateVisualBits.SetBit(UNIT_FIELD_BOUNDINGRADIUS);
- updateVisualBits.SetBit(UNIT_FIELD_COMBATREACH);
- updateVisualBits.SetBit(UNIT_FIELD_DISPLAYID);
- updateVisualBits.SetBit(UNIT_FIELD_NATIVEDISPLAYID);
- updateVisualBits.SetBit(UNIT_FIELD_MOUNTDISPLAYID);
- updateVisualBits.SetBit(UNIT_FIELD_BYTES_1);
- updateVisualBits.SetBit(UNIT_FIELD_MOUNTDISPLAYID);
- updateVisualBits.SetBit(UNIT_FIELD_PETNUMBER);
- updateVisualBits.SetBit(UNIT_FIELD_PET_NAME_TIMESTAMP);
- updateVisualBits.SetBit(UNIT_DYNAMIC_FLAGS);
- updateVisualBits.SetBit(UNIT_CHANNEL_SPELL);
- updateVisualBits.SetBit(UNIT_MOD_CAST_SPEED);
- updateVisualBits.SetBit(UNIT_FIELD_BYTES_2);
-
- updateVisualBits.SetBit(PLAYER_FLAGS);
- updateVisualBits.SetBit(PLAYER_BYTES);
- updateVisualBits.SetBit(PLAYER_BYTES_2);
- updateVisualBits.SetBit(PLAYER_BYTES_3);
- updateVisualBits.SetBit(PLAYER_GUILDID);
- updateVisualBits.SetBit(PLAYER_GUILDRANK);
- updateVisualBits.SetBit(PLAYER_GUILD_TIMESTAMP);
- updateVisualBits.SetBit(PLAYER_DUEL_TEAM);
- updateVisualBits.SetBit(PLAYER_DUEL_ARBITER);
- updateVisualBits.SetBit(PLAYER_DUEL_ARBITER+1);
-
- // PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)...
- for(uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i+=4)
- updateVisualBits.SetBit(i);
-
- //Players visible items are not inventory stuff
- //431) = 884 (0x374) = main weapon
- for(uint16 i = 0; i < EQUIPMENT_SLOT_END; i++)
- {
- // item creator
- updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i*MAX_VISIBLE_ITEM_OFFSET) + 0);
- updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i*MAX_VISIBLE_ITEM_OFFSET) + 1);
-
- uint16 visual_base = PLAYER_VISIBLE_ITEM_1_0 + (i*MAX_VISIBLE_ITEM_OFFSET);
-
- // item entry
- updateVisualBits.SetBit(visual_base + 0);
-
- // item enchantment IDs
- for(uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j)
- updateVisualBits.SetBit(visual_base + 1 + j);
-
- // random properties
- updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 0 + (i*MAX_VISIBLE_ITEM_OFFSET));
- updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (i*MAX_VISIBLE_ITEM_OFFSET));
- }
-
- updateVisualBits.SetBit(PLAYER_CHOSEN_TITLE);
-
- updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY);
- updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + 1);
- updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + 2);
- updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO);
- updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 1);
- updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 2);
- updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 3);
- updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 4);
- updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 5);
-}
-
-void Player::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const
-{
- for(int i = 0; i < EQUIPMENT_SLOT_END; i++)
- {
- if(m_items[i] == NULL)
- continue;
-
- m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
- }
-
- if(target == this)
- {
-
- for(int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
- {
- if(m_items[i] == NULL)
- continue;
-
- m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
- }
- for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
- {
- if(m_items[i] == NULL)
- continue;
-
- m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
- }
- }
-
- Unit::BuildCreateUpdateBlockForPlayer( data, target );
-}
-
-void Player::DestroyForPlayer( Player *target ) const
-{
- Unit::DestroyForPlayer( target );
-
- for(int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
- {
- if(m_items[i] == NULL)
- continue;
-
- m_items[i]->DestroyForPlayer( target );
- }
-
- if(target == this)
- {
-
- for(int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
- {
- if(m_items[i] == NULL)
- continue;
-
- m_items[i]->DestroyForPlayer( target );
- }
- for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
- {
- if(m_items[i] == NULL)
- continue;
-
- m_items[i]->DestroyForPlayer( target );
- }
- }
-}
-
-bool Player::HasSpell(uint32 spell) const
-{
- PlayerSpellMap::const_iterator itr = m_spells.find((uint16)spell);
- return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled);
-}
-
-TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell) const
-{
- if (!trainer_spell)
- return TRAINER_SPELL_RED;
-
- if (!trainer_spell->spell)
- return TRAINER_SPELL_RED;
-
- // known spell
- if(HasSpell(trainer_spell->spell))
- return TRAINER_SPELL_GRAY;
-
- // check race/class requirement
- if(!IsSpellFitByClassAndRace(trainer_spell->spell))
- return TRAINER_SPELL_RED;
-
- // check level requirement
- if(getLevel() < trainer_spell->reqlevel)
- return TRAINER_SPELL_RED;
-
- if(SpellChainNode const* spell_chain = spellmgr.GetSpellChainNode(trainer_spell->spell))
- {
- // check prev.rank requirement
- if(spell_chain->prev && !HasSpell(spell_chain->prev))
- return TRAINER_SPELL_RED;
-
- // check additional spell requirement
- if(spell_chain->req && !HasSpell(spell_chain->req))
- return TRAINER_SPELL_RED;
- }
-
- // check skill requirement
- if(trainer_spell->reqskill && GetBaseSkillValue(trainer_spell->reqskill) < trainer_spell->reqskillvalue)
- return TRAINER_SPELL_RED;
-
- // exist, already checked at loading
- SpellEntry const* spell = sSpellStore.LookupEntry(trainer_spell->spell);
-
- // secondary prof. or not prof. spell
- uint32 skill = spell->EffectMiscValue[1];
-
- if(spell->Effect[1] != SPELL_EFFECT_SKILL || !IsPrimaryProfessionSkill(skill))
- return TRAINER_SPELL_GREEN;
-
- // check primary prof. limit
- if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell->Id) && GetFreePrimaryProffesionPoints() == 0)
- return TRAINER_SPELL_RED;
-
- return TRAINER_SPELL_GREEN;
-}
-
-void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars)
-{
- uint32 guid = GUID_LOPART(playerguid);
-
- // convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry)
- // bones will be deleted by corpse/bones deleting thread shortly
- ObjectAccessor::Instance().ConvertCorpseForPlayer(playerguid);
-
- // remove from guild
- uint32 guildId = GetGuildIdFromDB(playerguid);
- if(guildId != 0)
- {
- Guild* guild = objmgr.GetGuildById(guildId);
- if(guild)
- guild->DelMember(guid);
- }
-
- // remove from arena teams
- uint32 at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_2v2);
- if(at_id != 0)
- {
- ArenaTeam * at = objmgr.GetArenaTeamById(at_id);
- if(at)
- at->DelMember(playerguid);
- }
- at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_3v3);
- if(at_id != 0)
- {
- ArenaTeam * at = objmgr.GetArenaTeamById(at_id);
- if(at)
- at->DelMember(playerguid);
- }
- at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_5v5);
- if(at_id != 0)
- {
- ArenaTeam * at = objmgr.GetArenaTeamById(at_id);
- if(at)
- at->DelMember(playerguid);
- }
-
- // the player was uninvited already on logout so just remove from group
- QueryResult *resultGroup = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", guid);
- if(resultGroup)
- {
- uint64 leaderGuid = MAKE_NEW_GUID((*resultGroup)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
- delete resultGroup;
- Group* group = objmgr.GetGroupByLeader(leaderGuid);
- if(group)
- {
- RemoveFromGroup(group, playerguid);
- }
- }
-
- // remove signs from petitions (also remove petitions if owner);
- RemovePetitionsAndSigns(playerguid, 10);
-
- // return back all mails with COD and Item 0 1 2 3 4 5 6
- QueryResult *resultMail = CharacterDatabase.PQuery("SELECT id,mailTemplateId,sender,subject,itemTextId,money,has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0", guid);
- if(resultMail)
- {
- do
- {
- Field *fields = resultMail->Fetch();
-
- uint32 mail_id = fields[0].GetUInt32();
- uint16 mailTemplateId= fields[1].GetUInt16();
- uint32 sender = fields[2].GetUInt32();
- std::string subject = fields[3].GetCppString();
- uint32 itemTextId = fields[4].GetUInt32();
- uint32 money = fields[5].GetUInt32();
- bool has_items = fields[6].GetBool();
-
- //we can return mail now
- //so firstly delete the old one
- CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id);
-
- MailItemsInfo mi;
- if(has_items)
- {
- QueryResult *resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", mail_id);
- if(resultItems)
- {
- do
- {
- Field *fields2 = resultItems->Fetch();
-
- uint32 item_guidlow = fields2[0].GetUInt32();
- uint32 item_template = fields2[1].GetUInt32();
-
- ItemPrototype const* itemProto = objmgr.GetItemPrototype(item_template);
- if(!itemProto)
- {
- CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guidlow);
- continue;
- }
-
- Item *pItem = NewItemOrBag(itemProto);
- if(!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER)))
- {
- pItem->FSetState(ITEM_REMOVED);
- pItem->SaveToDB(); // it also deletes item object !
- continue;
- }
-
- mi.AddItem(item_guidlow, item_template, pItem);
- }
- while (resultItems->NextRow());
-
- delete resultItems;
- }
- }
-
- CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
-
- uint32 pl_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
-
- WorldSession::SendReturnToSender(MAIL_NORMAL, pl_account, guid, sender, subject, itemTextId, &mi, money, 0, mailTemplateId);
- }
- while (resultMail->NextRow());
-
- delete resultMail;
- }
-
- // unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
- // Get guids of character's pets, will deleted in transaction
- QueryResult *resultPets = CharacterDatabase.PQuery("SELECT id FROM character_pet WHERE owner = '%u'",guid);
-
- // NOW we can finally clear other DB data related to character
- CharacterDatabase.BeginTransaction();
- if (resultPets)
- {
- do
- {
- Field *fields3 = resultPets->Fetch();
- uint32 petguidlow = fields3[0].GetUInt32();
- Pet::DeleteFromDB(petguidlow);
- } while (resultPets->NextRow());
- delete resultPets;
- }
-
- CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_queststatus WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'",guid,guid);
- CharacterDatabase.PExecute("DELETE FROM mail WHERE receiver = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM mail_items WHERE receiver = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'",guid);
- CharacterDatabase.PExecute("DELETE FROM has_logged_in_before WHERE guid = %u",guid);
- CharacterDatabase.CommitTransaction();
-
- //loginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID);
- if(updateRealmChars) sWorld.UpdateRealmCharCount(accountId);
-}
-
-void Player::SetMovement(PlayerMovementType pType)
-{
- WorldPacket data;
- switch(pType)
- {
- case MOVE_ROOT: data.Initialize(SMSG_FORCE_MOVE_ROOT, GetPackGUID().size()+4); break;
- case MOVE_UNROOT: data.Initialize(SMSG_FORCE_MOVE_UNROOT, GetPackGUID().size()+4); break;
- case MOVE_WATER_WALK: data.Initialize(SMSG_MOVE_WATER_WALK, GetPackGUID().size()+4); break;
- case MOVE_LAND_WALK: data.Initialize(SMSG_MOVE_LAND_WALK, GetPackGUID().size()+4); break;
- default:
- sLog.outError("Player::SetMovement: Unsupported move type (%d), data not sent to client.",pType);
- return;
- }
- data.append(GetPackGUID());
- data << uint32(0);
- GetSession()->SendPacket( &data );
-}
-
-/* Preconditions:
- - a resurrectable corpse must not be loaded for the player (only bones)
- - the player must be in world
-*/
-void Player::BuildPlayerRepop()
-{
- if(getRace() == RACE_NIGHTELF)
- CastSpell(this, 20584, true); // 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)
- CastSpell(this, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
-
- // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK
- // there must be SMSG.STOP_MIRROR_TIMER
- // there we must send 888 opcode
-
- // the player cannot have a corpse already, only bones which are not returned by GetCorpse
- if(GetCorpse())
- {
- sLog.outError("BuildPlayerRepop: player %s(%d) already has a corpse", GetName(), GetGUIDLow());
- assert(false);
- }
-
- // create a corpse and place it at the player's location
- CreateCorpse();
- Corpse *corpse = GetCorpse();
- if(!corpse)
- {
- sLog.outError("Error creating corpse for Player %s [%u]", GetName(), GetGUIDLow());
- return;
- }
- GetMap()->Add(corpse);
-
- // convert player body to ghost
- SetHealth( 1 );
-
- SetMovement(MOVE_WATER_WALK);
- if(!GetSession()->isLogingOut())
- SetMovement(MOVE_UNROOT);
-
- // BG - remove insignia related
- RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
-
- SendCorpseReclaimDelay();
-
- // to prevent cheating
- corpse->ResetGhostTime();
-
- StopMirrorTimers(); //disable timers(bars)
-
- SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (float)1.0); //see radius of death player?
-
- SetByteValue(UNIT_FIELD_BYTES_1, 3, PLAYER_STATE_FLAG_ALWAYS_STAND);
-}
-
-void Player::SendDelayResponse(const uint32 ml_seconds)
-{
- WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
- data << (uint32)time(NULL);
- data << (uint32)0;
- GetSession()->SendPacket( &data );
-}
-
-void Player::ResurrectPlayer(float restore_percent, bool updateToWorld, bool applySickness)
-{
- WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // remove spirit healer position
- data << uint32(-1);
- data << float(0);
- data << float(0);
- data << float(0);
- GetSession()->SendPacket(&data);
-
- // speed change, land walk
-
- // remove death flag + set aura
- SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00);
- if(getRace() == RACE_NIGHTELF)
- RemoveAurasDueToSpell(20584); // speed bonuses
- RemoveAurasDueToSpell(8326); // SPELL_AURA_GHOST
-
- setDeathState(ALIVE);
-
- SetMovement(MOVE_LAND_WALK);
- SetMovement(MOVE_UNROOT);
-
- m_deathTimer = 0;
-
- // set health/powers (0- will be set in caller)
- if(restore_percent>0.0f)
- {
- SetHealth(uint32(GetMaxHealth()*restore_percent));
- SetPower(POWER_MANA, uint32(GetMaxPower(POWER_MANA)*restore_percent));
- SetPower(POWER_RAGE, 0);
- SetPower(POWER_ENERGY, uint32(GetMaxPower(POWER_ENERGY)*restore_percent));
- }
-
- // update visbility
- ObjectAccessor::UpdateVisibilityForPlayer(this);
-
- // some items limited to specific map
- DestroyZoneLimitedItem( true, GetZoneId());
-
- if(sWorld.getConfig(CONFIG_DISABLE_RES_SICKNESS) || !applySickness || getLevel() <= 10)
- return;
-
- //Characters from level 1-10 are not affected by resurrection sickness.
- //Characters from level 11-19 will suffer from one minute of sickness
- //for each level they are above 10.
- //Characters level 20 and up suffer from ten minutes of sickness.
- int32 startLevel = sWorld.getConfig(CONFIG_DEATH_SICKNESS_LEVEL);
-
- if(int32(getLevel()) >= startLevel)
- {
- // set resurrection sickness
- CastSpell(this,SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,true);
-
- // not full duration
- if(int32(getLevel()) < startLevel+9)
- {
- int32 delta = (int32(getLevel()) - startLevel + 1)*MINUTE;
-
- for(int i =0; i < 3; ++i)
- {
- if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,i))
- {
- Aur->SetAuraDuration(delta*1000);
- Aur->UpdateAuraDuration();
- }
- }
- }
- }
-}
-
-void Player::KillPlayer()
-{
- SetMovement(MOVE_ROOT);
-
- StopMirrorTimers(); //disable timers(bars)
-
- setDeathState(CORPSE);
- //SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_IN_PVP );
-
- SetFlag(UNIT_DYNAMIC_FLAGS, 0x00);
- ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(GetMapId())->Instanceable());
-
- // 6 minutes until repop at graveyard
- m_deathTimer = 6*MINUTE*1000;
-
- UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill
-
- // don't create corpse at this moment, player might be falling
-
- // update visibility
- ObjectAccessor::UpdateObjectVisibility(this);
-}
-
-void Player::CreateCorpse()
-{
- // prevent existence 2 corpse for player
- SpawnCorpseBones();
-
- uint32 _uf, _pb, _pb2, _cfb1, _cfb2;
-
- Corpse *corpse = new Corpse( (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE );
- SetPvPDeath(false);
-
- if(!corpse->Create(objmgr.GenerateLowGuid(HIGHGUID_CORPSE), this, GetMapId(), GetPositionX(),
- GetPositionY(), GetPositionZ(), GetOrientation()))
- {
- delete corpse;
- return;
- }
-
- _uf = GetUInt32Value(UNIT_FIELD_BYTES_0);
- _pb = GetUInt32Value(PLAYER_BYTES);
- _pb2 = GetUInt32Value(PLAYER_BYTES_2);
-
- uint8 race = (uint8)(_uf);
- uint8 skin = (uint8)(_pb);
- uint8 face = (uint8)(_pb >> 8);
- uint8 hairstyle = (uint8)(_pb >> 16);
- uint8 haircolor = (uint8)(_pb >> 24);
- uint8 facialhair = (uint8)(_pb2);
-
- _cfb1 = ((0x00) | (race << 8) | (getGender() << 16) | (skin << 24));
- _cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));
-
- corpse->SetUInt32Value( CORPSE_FIELD_BYTES_1, _cfb1 );
- corpse->SetUInt32Value( CORPSE_FIELD_BYTES_2, _cfb2 );
-
- uint32 flags = CORPSE_FLAG_UNK2;
- if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
- flags |= CORPSE_FLAG_HIDE_HELM;
- if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
- flags |= CORPSE_FLAG_HIDE_CLOAK;
- if(InBattleGround())
- flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia
- corpse->SetUInt32Value( CORPSE_FIELD_FLAGS, flags );
-
- corpse->SetUInt32Value( CORPSE_FIELD_DISPLAY_ID, GetNativeDisplayId() );
-
- corpse->SetUInt32Value( CORPSE_FIELD_GUILD, GetGuildId() );
-
- uint32 iDisplayID;
- uint16 iIventoryType;
- uint32 _cfi;
- for (int i = 0; i < EQUIPMENT_SLOT_END; i++)
- {
- if(m_items[i])
- {
- iDisplayID = m_items[i]->GetProto()->DisplayInfoID;
- iIventoryType = (uint16)m_items[i]->GetProto()->InventoryType;
-
- _cfi = (uint16(iDisplayID)) | (iIventoryType)<< 24;
- corpse->SetUInt32Value(CORPSE_FIELD_ITEM + i,_cfi);
- }
- }
-
- // we don't SaveToDB for players in battlegrounds so don't do it for corpses either
- const MapEntry *entry = sMapStore.LookupEntry(corpse->GetMapId());
- assert(entry);
- if(entry->map_type != MAP_BATTLEGROUND)
- corpse->SaveToDB();
-
- // register for player, but not show
- ObjectAccessor::Instance().AddCorpse(corpse);
-}
-
-void Player::SpawnCorpseBones()
-{
- if(ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID()))
- SaveToDB(); // prevent loading as ghost without corpse
-}
-
-Corpse* Player::GetCorpse() const
-{
- return ObjectAccessor::Instance().GetCorpseForPlayerGUID(GetGUID());
-}
-
-void Player::DurabilityLossAll(double percent, bool inventory)
-{
- for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
- if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- DurabilityLoss(pItem,percent);
-
- if(inventory)
- {
- // bags not have durability
- // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
-
- for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
- if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- DurabilityLoss(pItem,percent);
-
- // keys not have durability
- //for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
-
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- if(ItemPrototype const *pBagProto = pBag->GetProto())
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- if(Item* pItem = GetItemByPos( i, j ))
- DurabilityLoss(pItem,percent);
- }
-}
-
-void Player::DurabilityLoss(Item* item, double percent)
-{
- if(!item )
- return;
-
- uint32 pMaxDurability = item ->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
-
- if(!pMaxDurability)
- return;
-
- uint32 pDurabilityLoss = uint32(pMaxDurability*percent);
-
- if(pDurabilityLoss < 1 )
- pDurabilityLoss = 1;
-
- DurabilityPointsLoss(item,pDurabilityLoss);
-}
-
-void Player::DurabilityPointsLossAll(int32 points, bool inventory)
-{
- for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
- if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- DurabilityPointsLoss(pItem,points);
-
- if(inventory)
- {
- // bags not have durability
- // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
-
- for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
- if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- DurabilityPointsLoss(pItem,points);
-
- // keys not have durability
- //for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
-
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- if(ItemPrototype const *pBagProto = pBag->GetProto())
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- if(Item* pItem = GetItemByPos( i, j ))
- DurabilityPointsLoss(pItem,points);
- }
-}
-
-void Player::DurabilityPointsLoss(Item* item, int32 points)
-{
- int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
- int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
- int32 pNewDurability = pOldDurability - points;
-
- if (pNewDurability < 0)
- pNewDurability = 0;
- else if (pNewDurability > pMaxDurability)
- pNewDurability = pMaxDurability;
-
- if (pOldDurability != pNewDurability)
- {
- // modify item stats _before_ Durability set to 0 to pass _ApplyItemMods internal check
- if ( pNewDurability == 0 && pOldDurability > 0 && item->IsEquipped())
- _ApplyItemMods(item,item->GetSlot(), false);
-
- item->SetUInt32Value(ITEM_FIELD_DURABILITY, pNewDurability);
-
- // modify item stats _after_ restore durability to pass _ApplyItemMods internal check
- if ( pNewDurability > 0 && pOldDurability == 0 && item->IsEquipped())
- _ApplyItemMods(item,item->GetSlot(), true);
-
- item->SetState(ITEM_CHANGED, this);
- }
-}
-
-void Player::DurabilityPointLossForEquipSlot(EquipmentSlots slot)
-{
- if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot ))
- DurabilityPointsLoss(pItem,1);
-}
-
-uint32 Player::DurabilityRepairAll(bool cost, float discountMod, bool guildBank)
-{
- uint32 TotalCost = 0;
- // equipped, backpack, bags itself
- for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
- TotalCost += DurabilityRepair(( (INVENTORY_SLOT_BAG_0 << 8) | i ),cost,discountMod, guildBank);
-
- // bank, buyback and keys not repaired
-
- // items in inventory bags
- for(int j = INVENTORY_SLOT_BAG_START; j < INVENTORY_SLOT_BAG_END; j++)
- for(int i = 0; i < MAX_BAG_SIZE; i++)
- TotalCost += DurabilityRepair(( (j << 8) | i ),cost,discountMod, guildBank);
- return TotalCost;
-}
-
-uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank)
-{
- Item* item = GetItemByPos(pos);
-
- uint32 TotalCost = 0;
- if(!item)
- return TotalCost;
-
- uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
- if(!maxDurability)
- return TotalCost;
-
- uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
-
- if(cost)
- {
- uint32 LostDurability = maxDurability - curDurability;
- if(LostDurability>0)
- {
- ItemPrototype const *ditemProto = sItemStorage.LookupEntry<ItemPrototype>(item->GetEntry());
- if(!ditemProto)
- {
- sLog.outError("ERROR: RepairDurability: Unknown item id %u", ditemProto);
- return TotalCost;
- }
-
- DurabilityCostsEntry const *dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
- if(!dcost)
- {
- sLog.outError("ERROR: RepairDurability: Wrong item lvl %u", dcost);
- return TotalCost;
- }
-
- DurabilityQualityEntry const *dQualitymodEntry = sDurabilityQualityStore.LookupEntry((ditemProto->Quality+1)*2);
- if(!dQualitymodEntry)
- {
- sLog.outError("ERROR: RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntry);
- return TotalCost;
- }
-
- uint32 dmultiplier = dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class,ditemProto->SubClass)];
- uint32 costs = uint32(LostDurability*dmultiplier*double(dQualitymodEntry->quality_mod));
-
- costs = uint32(costs * discountMod);
-
- if (costs==0) //fix for ITEM_QUALITY_ARTIFACT
- costs = 1;
-
- if (guildBank)
- {
- if (GetGuildId()==0)
- {
- DEBUG_LOG("You are not member of a guild");
- return TotalCost;
- }
-
- Guild *pGuild = objmgr.GetGuildById(GetGuildId());
- if (!pGuild)
- return TotalCost;
-
- if (!pGuild->HasRankRight(GetRank(), GR_RIGHT_WITHDRAW_REPAIR))
- {
- DEBUG_LOG("You do not have rights to withdraw for repairs");
- return TotalCost;
- }
-
- if (pGuild->GetMemberMoneyWithdrawRem(GetGUIDLow()) < costs)
- {
- DEBUG_LOG("You do not have enough money withdraw amount remaining");
- return TotalCost;
- }
-
- if (pGuild->GetGuildBankMoney() < costs)
- {
- DEBUG_LOG("There is not enough money in bank");
- return TotalCost;
- }
-
- pGuild->MemberMoneyWithdraw(costs, GetGUIDLow());
- TotalCost = costs;
- }
- else if (GetMoney() < costs)
- {
- DEBUG_LOG("You do not have enough money");
- return TotalCost;
- }
- else
- ModifyMoney( -int32(costs) );
- }
- }
-
- item->SetUInt32Value(ITEM_FIELD_DURABILITY, maxDurability);
- item->SetState(ITEM_CHANGED, this);
-
- // reapply mods for total broken and repaired item if equipped
- if(IsEquipmentPos(pos) && !curDurability)
- _ApplyItemMods(item,pos & 255, true);
- return TotalCost;
-}
-
-void Player::RepopAtGraveyard()
-{
- // note: this can be called also when the player is alive
- // for example from WorldSession::HandleMovementOpcodes
-
- AreaTableEntry const *zone = GetAreaEntryByAreaID(GetAreaId());
-
- // Such zones are considered unreachable as a ghost and the player must be automatically revived
- if(!isAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY || GetTransport())
- {
- ResurrectPlayer(0.5f);
- SpawnCorpseBones();
- }
-
- WorldSafeLocsEntry const *ClosestGrave = NULL;
-
- // Special handle for battleground maps
- BattleGround *bg = sBattleGroundMgr.GetBattleGround(GetBattleGroundId());
-
- if(bg && (bg->GetTypeID() == BATTLEGROUND_AB || bg->GetTypeID() == BATTLEGROUND_EY))
- ClosestGrave = bg->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetTeam());
- else
- ClosestGrave = objmgr.GetClosestGraveYard( GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam() );
-
- // stop countdown until repop
- m_deathTimer = 0;
-
- // if no grave found, stay at the current location
- // and don't show spirit healer location
- if(ClosestGrave)
- {
- TeleportTo(ClosestGrave->map_id, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, GetOrientation());
- if(isDead()) // not send if alive, because it used in TeleportTo()
- {
- WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // show spirit healer position on minimap
- data << ClosestGrave->map_id;
- data << ClosestGrave->x;
- data << ClosestGrave->y;
- data << ClosestGrave->z;
- GetSession()->SendPacket(&data);
- }
- }
-}
-
-void Player::JoinedChannel(Channel *c)
-{
- m_channels.push_back(c);
-}
-
-void Player::LeftChannel(Channel *c)
-{
- m_channels.remove(c);
-}
-
-void Player::CleanupChannels()
-{
- while(!m_channels.empty())
- {
- Channel* ch = *m_channels.begin();
- m_channels.erase(m_channels.begin()); // remove from player's channel list
- ch->Leave(GetGUID(), false); // not send to client, not remove from player's channel list
- if (ChannelMgr* cMgr = channelMgr(GetTeam()))
- cMgr->LeftChannel(ch->GetName()); // deleted channel if empty
-
- }
- sLog.outDebug("Player: channels cleaned up!");
-}
-
-void Player::UpdateLocalChannels(uint32 newZone )
-{
- if(m_channels.empty())
- return;
-
- AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone);
- if(!current_zone)
- return;
-
- ChannelMgr* cMgr = channelMgr(GetTeam());
- if(!cMgr)
- return;
-
- std::string current_zone_name = current_zone->area_name[GetSession()->GetSessionDbcLocale()];
-
- for(JoinedChannelsList::iterator i = m_channels.begin(), next; i != m_channels.end(); i = next)
- {
- next = i; ++next;
-
- // skip non built-in channels
- if(!(*i)->IsConstant())
- continue;
-
- ChatChannelsEntry const* ch = GetChannelEntryFor((*i)->GetChannelId());
- if(!ch)
- continue;
-
- if((ch->flags & 4) == 4) // global channel without zone name in pattern
- continue;
-
- // new channel
- char new_channel_name_buf[100];
- snprintf(new_channel_name_buf,100,ch->pattern[m_session->GetSessionDbcLocale()],current_zone_name.c_str());
- Channel* new_channel = cMgr->GetJoinChannel(new_channel_name_buf,ch->ChannelID);
-
- if((*i)!=new_channel)
- {
- new_channel->Join(GetGUID(),""); // will output Changed Channel: N. Name
-
- // leave old channel
- (*i)->Leave(GetGUID(),false); // not send leave channel, it already replaced at client
- std::string name = (*i)->GetName(); // stroe name, (*i)erase in LeftChannel
- LeftChannel(*i); // remove from player's channel list
- cMgr->LeftChannel(name); // delete if empty
- }
- }
- sLog.outDebug("Player: channels cleaned up!");
-}
-
-void Player::LeaveLFGChannel()
-{
- for(JoinedChannelsList::iterator i = m_channels.begin(); i != m_channels.end(); ++i )
- {
- if((*i)->IsLFG())
- {
- (*i)->Leave(GetGUID());
- break;
- }
- }
-}
-
-void Player::UpdateDefense()
-{
- uint32 defense_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_DEFENSE);
-
- if(UpdateSkill(SKILL_DEFENSE,defense_skill_gain))
- {
- // update dependent from defense skill part
- UpdateDefenseBonusesMod();
- }
-}
-
-void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply, bool affectStats)
-{
- if(modGroup >= BASEMOD_END || modType >= MOD_END)
- {
- sLog.outError("ERROR in HandleBaseModValue(): nonexisted BaseModGroup of wrong BaseModType!");
- return;
- }
-
- float val = 1.0f;
-
- switch(modType)
- {
- case FLAT_MOD:
- m_auraBaseMod[modGroup][modType] += apply ? amount : -amount;
- break;
- case PCT_MOD:
- if(amount <= -100.0f)
- amount = -200.0f;
-
- val = (100.0f + amount) / 100.0f;
- m_auraBaseMod[modGroup][modType] *= apply ? val : (1.0f/val);
- break;
- }
-
- if(!CanModifyStats())
- return;
-
- switch(modGroup)
- {
- case CRIT_PERCENTAGE: UpdateCritPercentage(BASE_ATTACK); break;
- case RANGED_CRIT_PERCENTAGE: UpdateCritPercentage(RANGED_ATTACK); break;
- case OFFHAND_CRIT_PERCENTAGE: UpdateCritPercentage(OFF_ATTACK); break;
- case SHIELD_BLOCK_VALUE: UpdateShieldBlockValue(); break;
- default: break;
- }
-}
-
-float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const
-{
- if(modGroup >= BASEMOD_END || modType > MOD_END)
- {
- sLog.outError("ERROR: trial to access nonexisted BaseModGroup or wrong BaseModType!");
- return 0.0f;
- }
-
- if(modType == PCT_MOD && m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
- return 0.0f;
-
- return m_auraBaseMod[modGroup][modType];
-}
-
-float Player::GetTotalBaseModValue(BaseModGroup modGroup) const
-{
- if(modGroup >= BASEMOD_END)
- {
- sLog.outError("ERROR: wrong BaseModGroup in GetTotalBaseModValue()!");
- return 0.0f;
- }
-
- if(m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
- return 0.0f;
-
- return m_auraBaseMod[modGroup][FLAT_MOD] * m_auraBaseMod[modGroup][PCT_MOD];
-}
-
-uint32 Player::GetShieldBlockValue() const
-{
- BaseModGroup modGroup = SHIELD_BLOCK_VALUE;
-
- float value = GetTotalBaseModValue(modGroup) + GetStat(STAT_STRENGTH)/20 - 1;
-
- value = (value < 0) ? 0 : value;
-
- return uint32(value);
-}
-
-float Player::GetMeleeCritFromAgility()
-{
- uint32 level = getLevel();
- uint32 pclass = getClass();
-
- if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
-
- GtChanceToMeleeCritBaseEntry const *critBase = sGtChanceToMeleeCritBaseStore.LookupEntry(pclass-1);
- GtChanceToMeleeCritEntry const *critRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
- if (critBase==NULL || critRatio==NULL)
- return 0.0f;
-
- float crit=critBase->base + GetStat(STAT_AGILITY)*critRatio->ratio;
- return crit*100.0f;
-}
-
-float Player::GetDodgeFromAgility()
-{
- // Table for base dodge values
- float dodge_base[MAX_CLASSES] = {
- 0.0075f, // Warrior
- 0.00652f, // Paladin
- -0.0545f, // Hunter
- -0.0059f, // Rogue
- 0.03183f, // Priest
- 0.0114f, // DK
- 0.0167f, // Shaman
- 0.034575f, // Mage
- 0.02011f, // Warlock
- 0.0f, // ??
- -0.0187f // Druid
- };
- // Crit/agility to dodge/agility coefficient multipliers
- float crit_to_dodge[MAX_CLASSES] = {
- 1.1f, // Warrior
- 1.0f, // Paladin
- 1.6f, // Hunter
- 2.0f, // Rogue
- 1.0f, // Priest
- 1.0f, // DK?
- 1.0f, // Shaman
- 1.0f, // Mage
- 1.0f, // Warlock
- 0.0f, // ??
- 1.7f // Druid
- };
-
- uint32 level = getLevel();
- uint32 pclass = getClass();
-
- if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
-
- // Dodge per agility for most classes equal crit per agility (but for some classes need apply some multiplier)
- GtChanceToMeleeCritEntry const *dodgeRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
- if (dodgeRatio==NULL || pclass > MAX_CLASSES)
- return 0.0f;
-
- float dodge=dodge_base[pclass-1] + GetStat(STAT_AGILITY) * dodgeRatio->ratio * crit_to_dodge[pclass-1];
- return dodge*100.0f;
-}
-
-float Player::GetSpellCritFromIntellect()
-{
- uint32 level = getLevel();
- uint32 pclass = getClass();
-
- if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
-
- GtChanceToSpellCritBaseEntry const *critBase = sGtChanceToSpellCritBaseStore.LookupEntry(pclass-1);
- GtChanceToSpellCritEntry const *critRatio = sGtChanceToSpellCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
- if (critBase==NULL || critRatio==NULL)
- return 0.0f;
-
- float crit=critBase->base + GetStat(STAT_INTELLECT)*critRatio->ratio;
- return crit*100.0f;
-}
-
-float Player::GetRatingCoefficient(CombatRating cr) const
-{
- uint32 level = getLevel();
-
- if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
-
- GtCombatRatingsEntry const *Rating = sGtCombatRatingsStore.LookupEntry(cr*GT_MAX_LEVEL+level-1);
- if (Rating == NULL)
- return 1.0f; // By default use minimum coefficient (not must be called)
-
- return Rating->ratio;
-}
-
-float Player::GetRatingBonusValue(CombatRating cr) const
-{
- return float(GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr)) / GetRatingCoefficient(cr);
-}
-
-uint32 Player::GetMeleeCritDamageReduction(uint32 damage) const
-{
- float melee = GetRatingBonusValue(CR_CRIT_TAKEN_MELEE)*2.0f;
- if (melee>25.0f) melee = 25.0f;
- return uint32 (melee * damage /100.0f);
-}
-
-uint32 Player::GetRangedCritDamageReduction(uint32 damage) const
-{
- float ranged = GetRatingBonusValue(CR_CRIT_TAKEN_RANGED)*2.0f;
- if (ranged>25.0f) ranged=25.0f;
- return uint32 (ranged * damage /100.0f);
-}
-
-uint32 Player::GetSpellCritDamageReduction(uint32 damage) const
-{
- float spell = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2.0f;
- // In wow script resilience limited to 25%
- if (spell>25.0f)
- spell = 25.0f;
- return uint32 (spell * damage / 100.0f);
-}
-
-uint32 Player::GetDotDamageReduction(uint32 damage) const
-{
- float spellDot = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL);
- // Dot resilience not limited (limit it by 100%)
- if (spellDot > 100.0f)
- spellDot = 100.0f;
- return uint32 (spellDot * damage / 100.0f);
-}
-
-float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const
-{
- switch (attType)
- {
- case BASE_ATTACK:
- return GetUInt32Value(PLAYER_EXPERTISE) / 4.0f;
- case OFF_ATTACK:
- return GetUInt32Value(PLAYER_OFFHAND_EXPERTISE) / 4.0f;
- default:
- break;
- }
- return 0.0f;
-}
-
-float Player::OCTRegenHPPerSpirit()
-{
- uint32 level = getLevel();
- uint32 pclass = getClass();
-
- if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
-
- GtOCTRegenHPEntry const *baseRatio = sGtOCTRegenHPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
- GtRegenHPPerSptEntry const *moreRatio = sGtRegenHPPerSptStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
- if (baseRatio==NULL || moreRatio==NULL)
- return 0.0f;
-
- // Formula from PaperDollFrame script
- float spirit = GetStat(STAT_SPIRIT);
- float baseSpirit = spirit;
- if (baseSpirit>50) baseSpirit = 50;
- float moreSpirit = spirit - baseSpirit;
- float regen = baseSpirit * baseRatio->ratio + moreSpirit * moreRatio->ratio;
- return regen;
-}
-
-float Player::OCTRegenMPPerSpirit()
-{
- uint32 level = getLevel();
- uint32 pclass = getClass();
-
- if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
-
-// GtOCTRegenMPEntry const *baseRatio = sGtOCTRegenMPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
- GtRegenMPPerSptEntry const *moreRatio = sGtRegenMPPerSptStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
- if (moreRatio==NULL)
- return 0.0f;
-
- // Formula get from PaperDollFrame script
- float spirit = GetStat(STAT_SPIRIT);
- float regen = spirit * moreRatio->ratio;
- return regen;
-}
-
-void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply)
-{
- ApplyModUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, value, apply);
-
- float RatingCoeffecient = GetRatingCoefficient(cr);
- float RatingChange = 0.0f;
-
- bool affectStats = CanModifyStats();
-
- switch (cr)
- {
- case CR_WEAPON_SKILL: // Implemented in Unit::RollMeleeOutcomeAgainst
- case CR_DEFENSE_SKILL:
- UpdateDefenseBonusesMod();
- break;
- case CR_DODGE:
- UpdateDodgePercentage();
- break;
- case CR_PARRY:
- UpdateParryPercentage();
- break;
- case CR_BLOCK:
- UpdateBlockPercentage();
- break;
- case CR_HIT_MELEE:
- RatingChange = value / RatingCoeffecient;
- m_modMeleeHitChance += apply ? RatingChange : -RatingChange;
- break;
- case CR_HIT_RANGED:
- RatingChange = value / RatingCoeffecient;
- m_modRangedHitChance += apply ? RatingChange : -RatingChange;
- break;
- case CR_HIT_SPELL:
- RatingChange = value / RatingCoeffecient;
- m_modSpellHitChance += apply ? RatingChange : -RatingChange;
- break;
- case CR_CRIT_MELEE:
- if(affectStats)
- {
- UpdateCritPercentage(BASE_ATTACK);
- UpdateCritPercentage(OFF_ATTACK);
- }
- break;
- case CR_CRIT_RANGED:
- if(affectStats)
- UpdateCritPercentage(RANGED_ATTACK);
- break;
- case CR_CRIT_SPELL:
- if(affectStats)
- UpdateAllSpellCritChances();
- break;
- case CR_HIT_TAKEN_MELEE: // Implemented in Unit::MeleeMissChanceCalc
- case CR_HIT_TAKEN_RANGED:
- break;
- case CR_HIT_TAKEN_SPELL: // Implemented in Unit::MagicSpellHitResult
- break;
- case CR_CRIT_TAKEN_MELEE: // Implemented in Unit::RollMeleeOutcomeAgainst (only for chance to crit)
- case CR_CRIT_TAKEN_RANGED:
- break;
- case CR_CRIT_TAKEN_SPELL: // Implemented in Unit::SpellCriticalBonus (only for chance to crit)
- break;
- case CR_HASTE_MELEE:
- RatingChange = value / RatingCoeffecient;
- ApplyAttackTimePercentMod(BASE_ATTACK,RatingChange,apply);
- ApplyAttackTimePercentMod(OFF_ATTACK,RatingChange,apply);
- break;
- case CR_HASTE_RANGED:
- RatingChange = value / RatingCoeffecient;
- ApplyAttackTimePercentMod(RANGED_ATTACK, RatingChange, apply);
- break;
- case CR_HASTE_SPELL:
- RatingChange = value / RatingCoeffecient;
- ApplyCastTimePercentMod(RatingChange,apply);
- break;
- case CR_WEAPON_SKILL_MAINHAND: // Implemented in Unit::RollMeleeOutcomeAgainst
- case CR_WEAPON_SKILL_OFFHAND:
- case CR_WEAPON_SKILL_RANGED:
- break;
- case CR_EXPERTISE:
- if(affectStats)
- {
- UpdateExpertise(BASE_ATTACK);
- UpdateExpertise(OFF_ATTACK);
- }
- break;
- }
-}
-
-void Player::SetRegularAttackTime()
-{
- for(int i = 0; i < MAX_ATTACK; ++i)
- {
- Item *tmpitem = GetWeaponForAttack(WeaponAttackType(i));
- if(tmpitem && !tmpitem->IsBroken())
- {
- ItemPrototype const *proto = tmpitem->GetProto();
- if(proto->Delay)
- SetAttackTime(WeaponAttackType(i), proto->Delay);
- else
- SetAttackTime(WeaponAttackType(i), BASE_ATTACK_TIME);
- }
- }
-}
-
-//skill+step, checking for max value
-bool Player::UpdateSkill(uint32 skill_id, uint32 step)
-{
- if(!skill_id)
- return false;
-
- uint16 i=0;
- for (; i < PLAYER_MAX_SKILLS; i++)
- if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill_id)
- break;
-
- if(i>=PLAYER_MAX_SKILLS)
- return false;
-
- uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
- uint32 value = SKILL_VALUE(data);
- uint32 max = SKILL_MAX(data);
-
- if ((!max) || (!value) || (value >= max))
- return false;
-
- if (value*512 < max*urand(0,512))
- {
- uint32 new_value = value+step;
- if(new_value > max)
- new_value = max;
-
- SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,max));
- return true;
- }
-
- return false;
-}
-
-inline int SkillGainChance(uint32 SkillValue, uint32 GrayLevel, uint32 GreenLevel, uint32 YellowLevel)
-{
- if ( SkillValue >= GrayLevel )
- return sWorld.getConfig(CONFIG_SKILL_CHANCE_GREY)*10;
- if ( SkillValue >= GreenLevel )
- return sWorld.getConfig(CONFIG_SKILL_CHANCE_GREEN)*10;
- if ( SkillValue >= YellowLevel )
- return sWorld.getConfig(CONFIG_SKILL_CHANCE_YELLOW)*10;
- return sWorld.getConfig(CONFIG_SKILL_CHANCE_ORANGE)*10;
-}
-
-bool Player::UpdateCraftSkill(uint32 spellid)
-{
- sLog.outDebug("UpdateCraftSkill spellid %d", spellid);
-
- SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid);
- SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid);
-
- for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
- {
- if(_spell_idx->second->skillId)
- {
- uint32 SkillValue = GetPureSkillValue(_spell_idx->second->skillId);
-
- // Alchemy Discoveries here
- SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellid);
- if(spellEntry && spellEntry->Mechanic==MECHANIC_DISCOVERY)
- {
- if(uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this))
- learnSpell(discoveredSpell);
- }
-
- uint32 craft_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_CRAFTING);
-
- return UpdateSkillPro(_spell_idx->second->skillId, SkillGainChance(SkillValue,
- _spell_idx->second->max_value,
- (_spell_idx->second->max_value + _spell_idx->second->min_value)/2,
- _spell_idx->second->min_value),
- craft_skill_gain);
- }
- }
- return false;
-}
-
-bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator )
-{
- sLog.outDebug("UpdateGatherSkill(SkillId %d SkillLevel %d RedLevel %d)", SkillId, SkillValue, RedLevel);
-
- uint32 gathering_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_GATHERING);
-
- // For skinning and Mining chance decrease with level. 1-74 - no decrease, 75-149 - 2 times, 225-299 - 8 times
- switch (SkillId)
- {
- case SKILL_HERBALISM:
- case SKILL_LOCKPICKING:
- case SKILL_JEWELCRAFTING:
- return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
- case SKILL_SKINNING:
- if( sWorld.getConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS)==0)
- return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
- else
- return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator) >> (SkillValue/sWorld.getConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS)), gathering_skill_gain);
- case SKILL_MINING:
- if (sWorld.getConfig(CONFIG_SKILL_CHANCE_MINING_STEPS)==0)
- return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
- else
- return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator) >> (SkillValue/sWorld.getConfig(CONFIG_SKILL_CHANCE_MINING_STEPS)),gathering_skill_gain);
- }
- return false;
-}
-
-bool Player::UpdateFishingSkill()
-{
- sLog.outDebug("UpdateFishingSkill");
-
- uint32 SkillValue = GetPureSkillValue(SKILL_FISHING);
-
- int32 chance = SkillValue < 75 ? 100 : 2500/(SkillValue-50);
-
- uint32 gathering_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_GATHERING);
-
- return UpdateSkillPro(SKILL_FISHING,chance*10,gathering_skill_gain);
-}
-
-bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step)
-{
- sLog.outDebug("UpdateSkillPro(SkillId %d, Chance %3.1f%%)", SkillId, Chance/10.0);
- if ( !SkillId )
- return false;
-
- if(Chance <= 0) // speedup in 0 chance case
- {
- sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance/10.0);
- return false;
- }
-
- uint16 i=0;
- for (; i < PLAYER_MAX_SKILLS; i++)
- if ( SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_INDEX(i))) == SkillId ) break;
- if ( i >= PLAYER_MAX_SKILLS )
- return false;
-
- uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
- uint16 SkillValue = SKILL_VALUE(data);
- uint16 MaxValue = SKILL_MAX(data);
-
- if ( !MaxValue || !SkillValue || SkillValue >= MaxValue )
- return false;
-
- int32 Roll = irand(1,1000);
-
- if ( Roll <= Chance )
- {
- uint32 new_value = SkillValue+step;
- if(new_value > MaxValue)
- new_value = MaxValue;
-
- SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,MaxValue));
- sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% taken", Chance/10.0);
- return true;
- }
-
- sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance/10.0);
- return false;
-}
-
-void Player::UpdateWeaponSkill (WeaponAttackType attType)
-{
- // no skill gain in pvp
- Unit *pVictim = getVictim();
- if(pVictim && pVictim->GetTypeId() == TYPEID_PLAYER)
- return;
-
- if(IsInFeralForm())
- return; // always maximized SKILL_FERAL_COMBAT in fact
-
- if(m_form == FORM_TREE)
- return; // use weapon but not skill up
-
- uint32 weapon_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_WEAPON);
-
- switch(attType)
- {
- case BASE_ATTACK:
- {
- Item *tmpitem = GetWeaponForAttack(attType,true);
-
- if (!tmpitem)
- UpdateSkill(SKILL_UNARMED,weapon_skill_gain);
- else if(tmpitem->GetProto()->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE)
- UpdateSkill(tmpitem->GetSkill(),weapon_skill_gain);
- break;
- }
- case OFF_ATTACK:
- case RANGED_ATTACK:
- {
- Item *tmpitem = GetWeaponForAttack(attType,true);
- if (tmpitem)
- UpdateSkill(tmpitem->GetSkill(),weapon_skill_gain);
- break;
- }
- }
- UpdateAllCritPercentages();
-}
-
-void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, MeleeHitOutcome outcome, bool defence)
-{
- switch(outcome)
- {
- case MELEE_HIT_CRIT:
- case MELEE_HIT_DODGE:
- case MELEE_HIT_PARRY:
- case MELEE_HIT_BLOCK:
- case MELEE_HIT_BLOCK_CRIT:
- return;
-
- default:
- break;
- }
-
- uint32 plevel = getLevel(); // if defense than pVictim == attacker
- uint32 greylevel = MaNGOS::XP::GetGrayLevel(plevel);
- uint32 moblevel = pVictim->getLevelForTarget(this);
- if(moblevel < greylevel)
- return;
-
- if (moblevel > plevel + 5)
- moblevel = plevel + 5;
-
- uint32 lvldif = moblevel - greylevel;
- if(lvldif < 3)
- lvldif = 3;
-
- uint32 skilldif = 5 * plevel - (defence ? GetBaseDefenseSkillValue() : GetBaseWeaponSkillValue(attType));
- if(skilldif <= 0)
- return;
-
- float chance = float(3 * lvldif * skilldif) / plevel;
- if(!defence)
- {
- if(getClass() == CLASS_WARRIOR || getClass() == CLASS_ROGUE)
- chance *= 0.1f * GetStat(STAT_INTELLECT);
- }
-
- chance = chance < 1.0f ? 1.0f : chance; //minimum chance to increase skill is 1%
-
- if(roll_chance_f(chance))
- {
- if(defence)
- UpdateDefense();
- else
- UpdateWeaponSkill(attType);
- }
- else
- return;
-}
-
-void Player::ModifySkillBonus(uint32 skillid,int32 val, bool talent)
-{
- for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
- if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skillid)
- {
- uint32 bonus_val = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));
- int16 temp_bonus = SKILL_TEMP_BONUS(bonus_val);
- int16 perm_bonus = SKILL_PERM_BONUS(bonus_val);
-
- if(talent) // permanent bonus stored in high part
- SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),MAKE_SKILL_BONUS(temp_bonus,perm_bonus+val));
- else // temporary/item bonus stored in low part
- SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),MAKE_SKILL_BONUS(temp_bonus+val,perm_bonus));
- return;
- }
-}
-
-void Player::UpdateMaxSkills()
-{
- uint16 maxconfskill = sWorld.GetConfigMaxSkillValue();
-
- for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
- if (GetUInt32Value(PLAYER_SKILL_INDEX(i)))
- {
- uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
-
- SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(pskill);
- if(!pSkill)
- continue;
-
- if(GetSkillRangeType(pSkill,false) != SKILL_RANGE_LEVEL)
- continue;
-
- uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
- uint32 max = SKILL_MAX(data);
- uint32 val = SKILL_VALUE(data);
-
- // update only level dependent max skill values
- if(max!=1 && max != maxconfskill)
- {
- uint32 max_Skill = GetMaxSkillValueForLevel();
- SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(val,max_Skill));
- }
- }
-}
-
-void Player::UpdateSkillsToMaxSkillsForLevel()
-{
- for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
- if (GetUInt32Value(PLAYER_SKILL_INDEX(i)))
- {
- uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
- if( IsProfessionSkill(pskill) || pskill == SKILL_RIDING )
- continue;
- uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
-
- uint32 max = SKILL_MAX(data);
-
- if(max > 1)
- SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(max,max));
-
- if(pskill == SKILL_DEFENSE)
- UpdateDefenseBonusesMod();
- }
-}
-
-// This functions sets a skill line value (and adds if doesn't exist yet)
-// To "remove" a skill line, set it's values to zero
-void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)
-{
- if(!id)
- return;
-
- uint16 i=0;
- for (; i < PLAYER_MAX_SKILLS; i++)
- if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == id) break;
-
- if(i<PLAYER_MAX_SKILLS) //has skill
- {
- if(currVal)
- SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal));
- else //remove
- {
- // clear skill fields
- SetUInt32Value(PLAYER_SKILL_INDEX(i),0);
- SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),0);
- SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
-
- // remove spells that depend on this skill when removing the skill
- for (PlayerSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
- {
- ++next;
- if(itr->second->state == PLAYERSPELL_REMOVED)
- continue;
-
- SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first);
- SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(itr->first);
-
- for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
- {
- if (_spell_idx->second->skillId == id)
- {
- // this may remove more than one spell (dependants)
- removeSpell(itr->first);
- next = m_spells.begin();
- break;
- }
- }
- }
- }
- }
- else if(currVal) //add
- {
- for (i=0; i < PLAYER_MAX_SKILLS; i++)
- if (!GetUInt32Value(PLAYER_SKILL_INDEX(i)))
- {
- SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id);
- if(!pSkill)
- {
- sLog.outError("Skill not found in SkillLineStore: skill #%u", id);
- return;
- }
- // enable unlearn button for primary professions only
- if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
- SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,1));
- else
- SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0));
- SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal));
-
- // apply skill bonuses
- SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
-
- // temporary bonuses
- AuraList const& mModSkill = GetAurasByType(SPELL_AURA_MOD_SKILL);
- for(AuraList::const_iterator i = mModSkill.begin(); i != mModSkill.end(); ++i)
- if ((*i)->GetModifier()->m_miscvalue == int32(id))
- (*i)->ApplyModifier(true);
-
- // permanent bonuses
- AuraList const& mModSkillTalent = GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT);
- for(AuraList::const_iterator i = mModSkillTalent.begin(); i != mModSkillTalent.end(); ++i)
- if ((*i)->GetModifier()->m_miscvalue == int32(id))
- (*i)->ApplyModifier(true);
-
- // Learn all spells for skill
- learnSkillRewardedSpells(id);
- return;
- }
- }
-}
-
-bool Player::HasSkill(uint32 skill) const
-{
- if(!skill)return false;
- for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
- {
- if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
- {
- return true;
- }
- }
- return false;
-}
-
-uint16 Player::GetSkillValue(uint32 skill) const
-{
- if(!skill)
- return 0;
-
- for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
- {
- if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
- {
- uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));
-
- int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));
- result += SKILL_TEMP_BONUS(bonus);
- result += SKILL_PERM_BONUS(bonus);
- return result < 0 ? 0 : result;
- }
- }
- return 0;
-}
-
-uint16 Player::GetMaxSkillValue(uint32 skill) const
-{
- if(!skill)return 0;
- for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
- {
- if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
- {
- uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));
-
- int32 result = int32(SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));
- result += SKILL_TEMP_BONUS(bonus);
- result += SKILL_PERM_BONUS(bonus);
- return result < 0 ? 0 : result;
- }
- }
- return 0;
-}
-
-uint16 Player::GetPureMaxSkillValue(uint32 skill) const
-{
- if(!skill)return 0;
- for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
- {
- if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
- {
- return SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)));
- }
- }
- return 0;
-}
-
-uint16 Player::GetBaseSkillValue(uint32 skill) const
-{
- if(!skill)return 0;
- for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
- {
- if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
- {
- int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));
- result += SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));
- return result < 0 ? 0 : result;
- }
- }
- return 0;
-}
-
-uint16 Player::GetPureSkillValue(uint32 skill) const
-{
- if(!skill)return 0;
- for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
- {
- if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
- {
- return SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)));
- }
- }
- return 0;
-}
-
-int16 Player::GetSkillTempBonusValue(uint32 skill) const
-{
- if(!skill)
- return 0;
-
- for (int i = 0; i < PLAYER_MAX_SKILLS; i++)
- {
- if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
- {
- return SKILL_TEMP_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));
- }
- }
-
- return 0;
-}
-
-void Player::SendInitialActionButtons()
-{
- sLog.outDetail( "Initializing Action Buttons for '%u'", GetGUIDLow() );
-
- WorldPacket data(SMSG_ACTION_BUTTONS, (MAX_ACTION_BUTTONS*4));
- for(int button = 0; button < MAX_ACTION_BUTTONS; ++button)
- {
- ActionButtonList::const_iterator itr = m_actionButtons.find(button);
- if(itr != m_actionButtons.end() && itr->second.uState != ACTIONBUTTON_DELETED)
- {
- data << uint16(itr->second.action);
- data << uint8(itr->second.misc);
- data << uint8(itr->second.type);
- }
- else
- {
- data << uint32(0);
- }
- }
-
- GetSession()->SendPacket( &data );
- sLog.outDetail( "Action Buttons for '%u' Initialized", GetGUIDLow() );
-}
-
-void Player::addActionButton(const uint8 button, const uint16 action, const uint8 type, const uint8 misc)
-{
- if(button >= MAX_ACTION_BUTTONS)
- {
- sLog.outError( "Action %u not added into button %u for player %s: button must be < 132", action, button, GetName() );
- return;
- }
-
- // check cheating with adding non-known spells to action bar
- if(type==ACTION_BUTTON_SPELL)
- {
- if(!sSpellStore.LookupEntry(action))
- {
- sLog.outError( "Action %u not added into button %u for player %s: spell not exist", action, button, GetName() );
- return;
- }
-
- if(!HasSpell(action))
- {
- sLog.outError( "Action %u not added into button %u for player %s: player don't known this spell", action, button, GetName() );
- return;
- }
- }
-
- ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
-
- if (buttonItr==m_actionButtons.end())
- { // just add new button
- m_actionButtons[button] = ActionButton(action,type,misc);
- }
- else
- { // change state of current button
- ActionButtonUpdateState uState = buttonItr->second.uState;
- buttonItr->second = ActionButton(action,type,misc);
- if (uState != ACTIONBUTTON_NEW) buttonItr->second.uState = ACTIONBUTTON_CHANGED;
- };
-
- sLog.outDetail( "Player '%u' Added Action '%u' to Button '%u'", GetGUIDLow(), action, button );
-}
-
-void Player::removeActionButton(uint8 button)
-{
- ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
- if (buttonItr==m_actionButtons.end())
- return;
-
- if(buttonItr->second.uState==ACTIONBUTTON_NEW)
- m_actionButtons.erase(buttonItr); // new and not saved
- else
- buttonItr->second.uState = ACTIONBUTTON_DELETED; // saved, will deleted at next save
-
- sLog.outDetail( "Action Button '%u' Removed from Player '%u'", button, GetGUIDLow() );
-}
-
-void Player::SetDontMove(bool dontMove)
-{
- m_dontMove = dontMove;
-}
-
-bool Player::SetPosition(float x, float y, float z, float orientation, bool teleport)
-{
- // prevent crash when a bad coord is sent by the client
- if(!MaNGOS::IsValidMapCoord(x,y,z,orientation))
- {
- sLog.outDebug("Player::SetPosition(%f, %f, %f, %f, %d) .. bad coordinates for player %d!",x,y,z,orientation,teleport,GetGUIDLow());
- return false;
- }
-
- Map *m = MapManager::Instance().GetMap(GetMapId(), this);
-
- const float old_x = GetPositionX();
- const float old_y = GetPositionY();
- const float old_z = GetPositionZ();
- const float old_r = GetOrientation();
-
- if( teleport || old_x != x || old_y != y || old_z != z || old_r != orientation )
- {
- if (teleport || old_x != x || old_y != y || old_z != z)
- RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING);
- else
- RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);
-
- // move and update visible state if need
- m->PlayerRelocation(this, x, y, z, orientation);
-
- // reread after Map::Relocation
- m = MapManager::Instance().GetMap(GetMapId(), this);
- x = GetPositionX();
- y = GetPositionY();
- z = GetPositionZ();
- }
-
- // code block for underwater state update
- UpdateUnderwaterState(m, x, y, z);
-
-
- CheckExploreSystem();
-
- // group update
- if(GetGroup())
- SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
-
- return true;
-}
-
-void Player::SaveRecallPosition()
-{
- m_recallMap = GetMapId();
- m_recallX = GetPositionX();
- m_recallY = GetPositionY();
- m_recallZ = GetPositionZ();
- m_recallO = GetOrientation();
-}
-
-void Player::SendMessageToSet(WorldPacket *data, bool self)
-{
- MapManager::Instance().GetMap(GetMapId(), this)->MessageBroadcast(this, data, self);
-}
-
-void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self)
-{
- MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self);
-}
-
-void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only)
-{
- MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self,own_team_only);
-}
-
-void Player::SendDirectMessage(WorldPacket *data)
-{
- GetSession()->SendPacket(data);
-}
-
-void Player::CheckExploreSystem()
-{
- if (!isAlive())
- return;
-
- if (isInFlight())
- return;
-
- uint16 areaFlag=MapManager::Instance().GetBaseMap(GetMapId())->GetAreaFlag(GetPositionX(),GetPositionY());
- if(areaFlag==0xffff)
- return;
- int offset = areaFlag / 32;
-
- if(offset >= 128)
- {
- sLog.outError("ERROR: Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < 64 ).",areaFlag,GetPositionX(),GetPositionY(),offset,offset);
- return;
- }
-
- uint32 val = (uint32)(1 << (areaFlag % 32));
- uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
-
- if( !(currFields & val) )
- {
- SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val));
-
- AreaTableEntry const *p = GetAreaEntryByAreaFlagAndMap(areaFlag,GetMapId());
- if(!p)
- {
- sLog.outError("PLAYER: Player %u discovered unknown area (x: %f y: %f map: %u", GetGUIDLow(), GetPositionX(),GetPositionY(),GetMapId());
- }
- else if(p->area_level > 0)
- {
- uint32 area = p->ID;
- if (getLevel() >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- {
- SendExplorationExperience(area,0);
- }
- else
- {
- int32 diff = int32(getLevel()) - p->area_level;
- uint32 XP = 0;
- if (diff < -5)
- {
- XP = uint32(objmgr.GetBaseXP(getLevel()+5)*sWorld.getRate(RATE_XP_EXPLORE));
- }
- else if (diff > 5)
- {
- int32 exploration_percent = (100-((diff-5)*5));
- if (exploration_percent > 100)
- exploration_percent = 100;
- else if (exploration_percent < 0)
- exploration_percent = 0;
-
- XP = uint32(objmgr.GetBaseXP(p->area_level)*exploration_percent/100*sWorld.getRate(RATE_XP_EXPLORE));
- }
- else
- {
- XP = uint32(objmgr.GetBaseXP(p->area_level)*sWorld.getRate(RATE_XP_EXPLORE));
- }
-
- GiveXP( XP, NULL );
- SendExplorationExperience(area,XP);
- }
- sLog.outDetail("PLAYER: Player %u discovered a new area: %u", GetGUIDLow(), area);
- }
- }
-}
-
-uint32 Player::TeamForRace(uint8 race)
-{
- ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
- if(!rEntry)
- {
- sLog.outError("Race %u not found in DBC: wrong DBC files?",uint32(race));
- return ALLIANCE;
- }
-
- switch(rEntry->TeamID)
- {
- case 7: return ALLIANCE;
- case 1: return HORDE;
- }
-
- sLog.outError("Race %u have wrong team id in DBC: wrong DBC files?",uint32(race),rEntry->TeamID);
- return ALLIANCE;
-}
-
-uint32 Player::getFactionForRace(uint8 race)
-{
- ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
- if(!rEntry)
- {
- sLog.outError("Race %u not found in DBC: wrong DBC files?",uint32(race));
- return 0;
- }
-
- return rEntry->FactionID;
-}
-
-void Player::setFactionForRace(uint8 race)
-{
- m_team = TeamForRace(race);
- setFaction( getFactionForRace(race) );
-}
-
-void Player::UpdateReputation() const
-{
- sLog.outDetail( "WORLD: Player::UpdateReputation" );
-
- for(FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
- {
- SendFactionState(&(itr->second));
- }
-}
-
-void Player::SendFactionState(FactionState const* faction) const
-{
- if(faction->Flags & FACTION_FLAG_VISIBLE) //If faction is visible then update it
- {
- WorldPacket data(SMSG_SET_FACTION_STANDING, (16)); // last check 2.4.0
- data << (float) 0; // unk 2.4.0
- data << (uint32) 1; // count
- // for
- data << (uint32) faction->ReputationListID;
- data << (uint32) faction->Standing;
- // end for
- GetSession()->SendPacket(&data);
- }
-}
-
-void Player::SendInitialReputations()
-{
- WorldPacket data(SMSG_INITIALIZE_FACTIONS, (4+128*5));
- data << uint32 (0x00000080);
-
- RepListID a = 0;
-
- for (FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); itr++)
- {
- // fill in absent fields
- for (; a != itr->first; a++)
- {
- data << uint8 (0x00);
- data << uint32 (0x00000000);
- }
-
- // fill in encountered data
- data << uint8 (itr->second.Flags);
- data << uint32 (itr->second.Standing);
-
- ++a;
- }
-
- // fill in absent fields
- for (; a != 128; a++)
- {
- data << uint8 (0x00);
- data << uint32 (0x00000000);
- }
-
- GetSession()->SendPacket(&data);
-}
-
-FactionState const* Player::GetFactionState( FactionEntry const* factionEntry) const
-{
- FactionStateList::const_iterator itr = m_factions.find(factionEntry->reputationListID);
- if (itr != m_factions.end())
- return &itr->second;
-
- return NULL;
-}
-
-void Player::SetFactionAtWar(FactionState* faction, bool atWar)
-{
- // not allow declare war to own faction
- if(atWar && (faction->Flags & FACTION_FLAG_PEACE_FORCED) )
- return;
-
- // already set
- if(((faction->Flags & FACTION_FLAG_AT_WAR) != 0) == atWar)
- return;
-
- if( atWar )
- faction->Flags |= FACTION_FLAG_AT_WAR;
- else
- faction->Flags &= ~FACTION_FLAG_AT_WAR;
-
- faction->Changed = true;
-}
-
-void Player::SetFactionInactive(FactionState* faction, bool inactive)
-{
- // always invisible or hidden faction can't be inactive
- if(inactive && ((faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN)) || !(faction->Flags & FACTION_FLAG_VISIBLE) ) )
- return;
-
- // already set
- if(((faction->Flags & FACTION_FLAG_INACTIVE) != 0) == inactive)
- return;
-
- if(inactive)
- faction->Flags |= FACTION_FLAG_INACTIVE;
- else
- faction->Flags &= ~FACTION_FLAG_INACTIVE;
-
- faction->Changed = true;
-}
-
-void Player::SetFactionVisibleForFactionTemplateId(uint32 FactionTemplateId)
-{
- FactionTemplateEntry const*factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId);
-
- if(!factionTemplateEntry)
- return;
-
- SetFactionVisibleForFactionId(factionTemplateEntry->faction);
-}
-
-void Player::SetFactionVisibleForFactionId(uint32 FactionId)
-{
- FactionEntry const *factionEntry = sFactionStore.LookupEntry(FactionId);
- if(!factionEntry)
- return;
-
- if(factionEntry->reputationListID < 0)
- return;
-
- FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
- if (itr == m_factions.end())
- return;
-
- SetFactionVisible(&itr->second);
-}
-
-void Player::SetFactionVisible(FactionState* faction)
-{
- // always invisible or hidden faction can't be make visible
- if(faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN))
- return;
-
- // already set
- if(faction->Flags & FACTION_FLAG_VISIBLE)
- return;
-
- faction->Flags |= FACTION_FLAG_VISIBLE;
- faction->Changed = true;
-
- if(!m_session->PlayerLoading())
- {
- // make faction visible in reputation list at client
- WorldPacket data(SMSG_SET_FACTION_VISIBLE, 4);
- data << faction->ReputationListID;
- GetSession()->SendPacket(&data);
- }
-}
-
-void Player::SetInitialFactions()
-{
- for(unsigned int i = 1; i < sFactionStore.GetNumRows(); i++)
- {
- FactionEntry const *factionEntry = sFactionStore.LookupEntry(i);
-
- if( factionEntry && (factionEntry->reputationListID >= 0))
- {
- FactionState newFaction;
- newFaction.ID = factionEntry->ID;
- newFaction.ReputationListID = factionEntry->reputationListID;
- newFaction.Standing = 0;
- newFaction.Flags = GetDefaultReputationFlags(factionEntry);
- newFaction.Changed = true;
-
- m_factions[newFaction.ReputationListID] = newFaction;
- }
- }
-}
-
-uint32 Player::GetDefaultReputationFlags(const FactionEntry *factionEntry) const
-{
- if (!factionEntry)
- return 0;
-
- uint32 raceMask = getRaceMask();
- uint32 classMask = getClassMask();
- for (int i=0; i < 4; i++)
- {
- if( (factionEntry->BaseRepRaceMask[i] & raceMask) &&
- (factionEntry->BaseRepClassMask[i]==0 ||
- (factionEntry->BaseRepClassMask[i] & classMask) ) )
- return factionEntry->ReputationFlags[i];
- }
- return 0;
-}
-
-int32 Player::GetBaseReputation(const FactionEntry *factionEntry) const
-{
- if (!factionEntry)
- return 0;
-
- uint32 raceMask = getRaceMask();
- uint32 classMask = getClassMask();
- for (int i=0; i < 4; i++)
- {
- if( (factionEntry->BaseRepRaceMask[i] & raceMask) &&
- (factionEntry->BaseRepClassMask[i]==0 ||
- (factionEntry->BaseRepClassMask[i] & classMask) ) )
- return factionEntry->BaseRepValue[i];
- }
-
- // in faction.dbc exist factions with (RepListId >=0, listed in character reputation list) with all BaseRepRaceMask[i]==0
- return 0;
-}
-
-int32 Player::GetReputation(uint32 faction_id) const
-{
- FactionEntry const *factionEntry = sFactionStore.LookupEntry(faction_id);
-
- if (!factionEntry)
- {
- sLog.outError("Player::GetReputation: Can't get reputation of %s for unknown faction (faction template id) #%u.",GetName(), faction_id);
- return 0;
- }
-
- return GetReputation(factionEntry);
-}
-
-int32 Player::GetReputation(const FactionEntry *factionEntry) const
-{
- // Faction without recorded reputation. Just ignore.
- if(!factionEntry)
- return 0;
-
- FactionStateList::const_iterator itr = m_factions.find(factionEntry->reputationListID);
- if (itr != m_factions.end())
- return GetBaseReputation(factionEntry) + itr->second.Standing;
-
- return 0;
-}
-
-ReputationRank Player::GetReputationRank(uint32 faction) const
-{
- FactionEntry const*factionEntry = sFactionStore.LookupEntry(faction);
- if(!factionEntry)
- return MIN_REPUTATION_RANK;
-
- return GetReputationRank(factionEntry);
-}
-
-ReputationRank Player::ReputationToRank(int32 standing) const
-{
- int32 Limit = Reputation_Cap + 1;
- for (int i = MAX_REPUTATION_RANK-1; i >= MIN_REPUTATION_RANK; --i)
- {
- Limit -= ReputationRank_Length[i];
- if (standing >= Limit )
- return ReputationRank(i);
- }
- return MIN_REPUTATION_RANK;
-}
-
-ReputationRank Player::GetReputationRank(const FactionEntry *factionEntry) const
-{
- int32 Reputation = GetReputation(factionEntry);
- return ReputationToRank(Reputation);
-}
-
-ReputationRank Player::GetBaseReputationRank(const FactionEntry *factionEntry) const
-{
- int32 Reputation = GetBaseReputation(factionEntry);
- return ReputationToRank(Reputation);
-}
-
-bool Player::ModifyFactionReputation(uint32 FactionTemplateId, int32 DeltaReputation)
-{
- FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId);
-
- if(!factionTemplateEntry)
- {
- sLog.outError("Player::ModifyFactionReputation: Can't update reputation of %s for unknown faction (faction template id) #%u.", GetName(), FactionTemplateId);
- return false;
- }
-
- FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction);
-
- // Faction without recorded reputation. Just ignore.
- if(!factionEntry)
- return false;
-
- return ModifyFactionReputation(factionEntry, DeltaReputation);
-}
-
-bool Player::ModifyFactionReputation(FactionEntry const* factionEntry, int32 standing)
-{
- SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID);
- if (flist)
- {
- bool res = false;
- for (SimpleFactionsList::const_iterator itr = flist->begin();itr != flist->end();++itr)
- {
- FactionEntry const *factionEntryCalc = sFactionStore.LookupEntry(*itr);
- if(factionEntryCalc)
- res = ModifyOneFactionReputation(factionEntryCalc, standing);
- }
- return res;
- }
- else
- return ModifyOneFactionReputation(factionEntry, standing);
-}
-
-bool Player::ModifyOneFactionReputation(FactionEntry const* factionEntry, int32 standing)
-{
- FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
- if (itr != m_factions.end())
- {
- int32 BaseRep = GetBaseReputation(factionEntry);
- int32 new_rep = BaseRep + itr->second.Standing + standing;
-
- if (new_rep > Reputation_Cap)
- new_rep = Reputation_Cap;
- else
- if (new_rep < Reputation_Bottom)
- new_rep = Reputation_Bottom;
-
- if(ReputationToRank(new_rep) <= REP_HOSTILE)
- SetFactionAtWar(&itr->second,true);
-
- itr->second.Standing = new_rep - BaseRep;
- itr->second.Changed = true;
-
- SetFactionVisible(&itr->second);
-
- for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
- {
- if(uint32 questid = GetQuestSlotQuestId(i))
- {
- Quest const* qInfo = objmgr.GetQuestTemplate(questid);
- if( qInfo && qInfo->GetRepObjectiveFaction() == factionEntry->ID )
- {
- QuestStatusData& q_status = mQuestStatus[questid];
- if( q_status.m_status == QUEST_STATUS_INCOMPLETE )
- {
- if(GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue())
- if ( CanCompleteQuest( questid ) )
- CompleteQuest( questid );
- }
- else if( q_status.m_status == QUEST_STATUS_COMPLETE )
- {
- if(GetReputation(factionEntry) < qInfo->GetRepObjectiveValue())
- IncompleteQuest( questid );
- }
- }
- }
- }
-
- SendFactionState(&(itr->second));
-
- return true;
- }
- return false;
-}
-
-bool Player::SetFactionReputation(uint32 FactionTemplateId, int32 standing)
-{
- FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId);
-
- if(!factionTemplateEntry)
- {
- sLog.outError("Player::SetFactionReputation: Can't set reputation of %s for unknown faction (faction template id) #%u.", GetName(), FactionTemplateId);
- return false;
- }
-
- FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction);
-
- // Faction without recorded reputation. Just ignore.
- if(!factionEntry)
- return false;
-
- return SetFactionReputation(factionEntry, standing);
-}
-
-bool Player::SetFactionReputation(FactionEntry const* factionEntry, int32 standing)
-{
- SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID);
- if (flist)
- {
- bool res = false;
- for (SimpleFactionsList::const_iterator itr = flist->begin();itr != flist->end();++itr)
- {
- FactionEntry const *factionEntryCalc = sFactionStore.LookupEntry(*itr);
- if(factionEntryCalc)
- res = SetOneFactionReputation(factionEntryCalc, standing);
- }
- return res;
- }
- else
- return SetOneFactionReputation(factionEntry, standing);
-}
-
-bool Player::SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing)
-{
- FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
- if (itr != m_factions.end())
- {
- if (standing > Reputation_Cap)
- standing = Reputation_Cap;
- else
- if (standing < Reputation_Bottom)
- standing = Reputation_Bottom;
-
- int32 BaseRep = GetBaseReputation(factionEntry);
- itr->second.Standing = standing - BaseRep;
- itr->second.Changed = true;
-
- SetFactionVisible(&itr->second);
-
- if(ReputationToRank(standing) <= REP_HOSTILE)
- SetFactionAtWar(&itr->second,true);
-
- SendFactionState(&(itr->second));
- return true;
- }
- return false;
-}
-
-//Calculate total reputation percent player gain with quest/creature level
-int32 Player::CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, bool for_quest)
-{
- // for grey creature kill received 20%, in other case 100.
- int32 percent = (!for_quest && (creatureOrQuestLevel <= MaNGOS::XP::GetGrayLevel(getLevel()))) ? 20 : 100;
-
- int32 repMod = GetTotalAuraModifier(SPELL_AURA_MOD_REPUTATION_GAIN);
-
- percent += rep > 0 ? repMod : -repMod;
-
- if(percent <=0)
- return 0;
-
- return int32(sWorld.getRate(RATE_REPUTATION_GAIN)*rep*percent/100);
-}
-
-//Calculates how many reputation points player gains in victim's enemy factions
-void Player::RewardReputation(Unit *pVictim, float rate)
-{
- if(!pVictim || pVictim->GetTypeId() == TYPEID_PLAYER)
- return;
-
- ReputationOnKillEntry const* Rep = objmgr.GetReputationOnKilEntry(((Creature*)pVictim)->GetCreatureInfo()->Entry);
-
- if(!Rep)
- return;
-
- if(Rep->repfaction1 && (!Rep->team_dependent || GetTeam()==ALLIANCE))
- {
- int32 donerep1 = CalculateReputationGain(pVictim->getLevel(),Rep->repvalue1,false);
- donerep1 = int32(donerep1*rate);
- FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(Rep->repfaction1);
- uint32 current_reputation_rank1 = GetReputationRank(factionEntry1);
- if(factionEntry1 && current_reputation_rank1 <= Rep->reputation_max_cap1)
- ModifyFactionReputation(factionEntry1, donerep1);
-
- // Wiki: Team factions value divided by 2
- if(Rep->is_teamaward1)
- {
- FactionEntry const *team1_factionEntry = sFactionStore.LookupEntry(factionEntry1->team);
- if(team1_factionEntry)
- ModifyFactionReputation(team1_factionEntry, donerep1 / 2);
- }
- }
-
- if(Rep->repfaction2 && (!Rep->team_dependent || GetTeam()==HORDE))
- {
- int32 donerep2 = CalculateReputationGain(pVictim->getLevel(),Rep->repvalue2,false);
- donerep2 = int32(donerep2*rate);
- FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(Rep->repfaction2);
- uint32 current_reputation_rank2 = GetReputationRank(factionEntry2);
- if(factionEntry2 && current_reputation_rank2 <= Rep->reputation_max_cap2)
- ModifyFactionReputation(factionEntry2, donerep2);
-
- // Wiki: Team factions value divided by 2
- if(Rep->is_teamaward2)
- {
- FactionEntry const *team2_factionEntry = sFactionStore.LookupEntry(factionEntry2->team);
- if(team2_factionEntry)
- ModifyFactionReputation(team2_factionEntry, donerep2 / 2);
- }
- }
-}
-
-//Calculate how many reputation points player gain with the quest
-void Player::RewardReputation(Quest const *pQuest)
-{
- // quest reputation reward/loss
- for(int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
- {
- if(pQuest->RewRepFaction[i] && pQuest->RewRepValue[i] )
- {
- int32 rep = CalculateReputationGain(pQuest->GetQuestLevel(),pQuest->RewRepValue[i],true);
- FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i]);
- if(factionEntry)
- ModifyFactionReputation(factionEntry, rep);
- }
- }
-
- // TODO: implement reputation spillover
-}
-
-void Player::UpdateArenaFields(void)
-{
- /* arena calcs go here */
-}
-
-void Player::UpdateHonorFields()
-{
- /// called when rewarding honor and at each save
- uint64 now = time(NULL);
- uint64 today = uint64(time(NULL) / DAY) * DAY;
-
- if(m_lastHonorUpdateTime < today)
- {
- uint64 yesterday = today - DAY;
-
- uint16 kills_today = PAIR32_LOPART(GetUInt32Value(PLAYER_FIELD_KILLS));
-
- // update yesterday's contribution
- if(m_lastHonorUpdateTime >= yesterday )
- {
- SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));
-
- // this is the first update today, reset today's contribution
- SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0);
- SetUInt32Value(PLAYER_FIELD_KILLS, MAKE_PAIR32(0,kills_today));
- }
- else
- {
- // no honor/kills yesterday or today, reset
- SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0);
- SetUInt32Value(PLAYER_FIELD_KILLS, 0);
- }
- }
-
- m_lastHonorUpdateTime = now;
-}
-
-///Calculate the amount of honor gained based on the victim
-///and the size of the group for which the honor is divided
-///An exact honor value can also be given (overriding the calcs)
-bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvptoken)
-{
- // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
- if(GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
- return false;
-
- uint64 victim_guid = 0;
- uint32 victim_rank = 0;
- time_t now = time(NULL);
-
- // need call before fields update to have chance move yesterday data to appropriate fields before today data change.
- UpdateHonorFields();
-
- // do not reward honor in arenas, but return true to enable onkill spellproc
- if(InBattleGround() && GetBattleGround() && GetBattleGround()->isArena())
- return true;
-
- if(honor <= 0)
- {
- if(!uVictim || uVictim == this || uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
- return false;
-
- victim_guid = uVictim->GetGUID();
-
- if( uVictim->GetTypeId() == TYPEID_PLAYER )
- {
- Player *pVictim = (Player *)uVictim;
-
- if( GetTeam() == pVictim->GetTeam() && !sWorld.IsFFAPvPRealm() )
- return false;
-
- float f = 1; //need for total kills (?? need more info)
- uint32 k_grey = 0;
- uint32 k_level = getLevel();
- uint32 v_level = pVictim->getLevel();
-
- {
- // PLAYER_CHOSEN_TITLE VALUES DESCRIPTION
- // [0] Just name
- // [1..14] Alliance honor titles and player name
- // [15..28] Horde honor titles and player name
- // [29..38] Other title and player name
- // [39+] Nothing
- uint32 victim_title = pVictim->GetUInt32Value(PLAYER_CHOSEN_TITLE);
- // Get Killer titles, CharTitlesEntry::bit_index
- // Ranks:
- // title[1..14] -> rank[5..18]
- // title[15..28] -> rank[5..18]
- // title[other] -> 0
- if (victim_title == 0)
- victim_guid = 0; // Don't show HK: <rank> message, only log.
- else if (victim_title < 15)
- victim_rank = victim_title + 4;
- else if (victim_title < 29)
- victim_rank = victim_title - 14 + 4;
- else
- victim_guid = 0; // Don't show HK: <rank> message, only log.
- }
-
- if(k_level <= 5)
- k_grey = 0;
- else if( k_level <= 39 )
- k_grey = k_level - 5 - k_level/10;
- else
- k_grey = k_level - 1 - k_level/5;
-
- if(v_level<=k_grey)
- return false;
-
- float diff_level = (k_level == k_grey) ? 1 : ((float(v_level) - float(k_grey)) / (float(k_level) - float(k_grey)));
-
- int32 v_rank =1; //need more info
-
- honor = ((f * diff_level * (190 + v_rank*10))/6);
- honor *= ((float)k_level) / 70.0f; //factor of dependence on levels of the killer
-
- // count the number of playerkills in one day
- ApplyModUInt32Value(PLAYER_FIELD_KILLS, 1, true);
- // and those in a lifetime
- ApplyModUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 1, true);
- }
- else
- {
- Creature *cVictim = (Creature *)uVictim;
-
- if (!cVictim->isRacialLeader())
- return false;
-
- honor = 100; // ??? need more info
- victim_rank = 19; // HK: Leader
- }
- }
-
- if (uVictim != NULL)
- {
- honor *= sWorld.getRate(RATE_HONOR);
-
- if(groupsize > 1)
- honor /= groupsize;
-
- honor *= (((float)urand(8,12))/10); // approx honor: 80% - 120% of real honor
- }
-
- // honor - for show honor points in log
- // victim_guid - for show victim name in log
- // victim_rank [1..4] HK: <dishonored rank>
- // victim_rank [5..19] HK: <alliance\horde rank>
- // victim_rank [0,20+] HK: <>
- WorldPacket data(SMSG_PVP_CREDIT,4+8+4);
- data << (uint32) honor;
- data << (uint64) victim_guid;
- data << (uint32) victim_rank;
-
- GetSession()->SendPacket(&data);
-
- // add honor points
- ModifyHonorPoints(int32(honor));
-
- ApplyModUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, uint32(honor), true);
-
- if( sWorld.getConfig(CONFIG_PVP_TOKEN_ENABLE) && pvptoken )
- {
- if(!uVictim || uVictim == this || uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
- return true;
-
- if(uVictim->GetTypeId() == TYPEID_PLAYER)
- {
- // Check if allowed to receive it in current map
- uint8 MapType = sWorld.getConfig(CONFIG_PVP_TOKEN_MAP_TYPE);
- if(MapType == 1 && !InBattleGround() && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) || MapType == 2 && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) || MapType == 3 && !InBattleGround())
- return true;
-
- uint32 noSpaceForCount = 0;
- uint32 itemId = sWorld.getConfig(CONFIG_PVP_TOKEN_ID);
- int32 count = sWorld.getConfig(CONFIG_PVP_TOKEN_COUNT);
-
- // check space and find places
- ItemPosCountVec dest;
- uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount );
- if( msg != EQUIP_ERR_OK ) // convert to possible store amount
- count = noSpaceForCount;
-
- if( count == 0 || dest.empty()) // can't add any
- {
- // -- TODO: Send to mailbox if no space
- ChatHandler(this).PSendSysMessage("You don't have any space in your bags for a token.");
- return true;
- }
-
- Item* item = StoreNewItem( dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
- SendNewItem(item,count,true,false);
- ChatHandler(this).PSendSysMessage("You have been awarded a token for slaying another player.");
- }
- }
-
- return true;
-}
-
-void Player::ModifyHonorPoints( int32 value )
-{
- if(value < 0)
- {
- if (GetHonorPoints() > sWorld.getConfig(CONFIG_MAX_HONOR_POINTS))
- SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, sWorld.getConfig(CONFIG_MAX_HONOR_POINTS) + value);
- else
- SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, GetHonorPoints() > uint32(-value) ? GetHonorPoints() + value : 0);
- }
- else
- SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, GetHonorPoints() < sWorld.getConfig(CONFIG_MAX_HONOR_POINTS) - value ? GetHonorPoints() + value : sWorld.getConfig(CONFIG_MAX_HONOR_POINTS));
-}
-
-void Player::ModifyArenaPoints( int32 value )
-{
- if(value < 0)
- {
- if (GetArenaPoints() > sWorld.getConfig(CONFIG_MAX_ARENA_POINTS))
- SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, sWorld.getConfig(CONFIG_MAX_ARENA_POINTS) + value);
- else
- SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, GetArenaPoints() > uint32(-value) ? GetArenaPoints() + value : 0);
- }
- else
- SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, GetArenaPoints() < sWorld.getConfig(CONFIG_MAX_ARENA_POINTS) - value ? GetArenaPoints() + value : sWorld.getConfig(CONFIG_MAX_ARENA_POINTS));
-}
-
-uint32 Player::GetGuildIdFromDB(uint64 guid)
-{
- std::ostringstream ss;
- ss<<"SELECT guildid FROM guild_member WHERE guid='"<<guid<<"'";
- QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
- if( result )
- {
- uint32 v = result->Fetch()[0].GetUInt32();
- delete result;
- return v;
- }
- else
- return 0;
-}
-
-uint32 Player::GetRankFromDB(uint64 guid)
-{
- std::ostringstream ss;
- ss<<"SELECT rank FROM guild_member WHERE guid='"<<guid<<"'";
- QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
- if( result )
- {
- uint32 v = result->Fetch()[0].GetUInt32();
- delete result;
- return v;
- }
- else
- return 0;
-}
-
-uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type)
-{
- QueryResult *result = CharacterDatabase.PQuery("SELECT arenateamid FROM arena_team_member WHERE guid='%u'", GUID_LOPART(guid));
- if(result)
- {
- bool found = false;
- // init id to find the type of the arenateam
- uint32 id = (*result)[0].GetUInt32();
- do
- {
- QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM arena_team WHERE arenateamid='%u'", id);
- if(result2)
- {
- uint8 dbtype = (*result2)[0].GetUInt32();
- delete result2;
- if(dbtype == type)
- {
- // if the type matches, we've found the id
- found = true;
- break;
- }
- }
- } while(result->NextRow());
- delete result;
- if(found) return id;
- }
- // no arenateam for the specified guid, return 0
- return 0;
-}
-
-uint32 Player::GetZoneIdFromDB(uint64 guid)
-{
- std::ostringstream ss;
-
- ss<<"SELECT zone FROM characters WHERE guid='"<<GUID_LOPART(guid)<<"'";
- QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
- if (!result)
- return 0;
- Field* fields = result->Fetch();
- uint32 zone = fields[0].GetUInt32();
- delete result;
-
- if (!zone)
- {
- // stored zone is zero, use generic and slow zone detection
- ss.str("");
- ss<<"SELECT map,position_x,position_y FROM characters WHERE guid='"<<GUID_LOPART(guid)<<"'";
- result = CharacterDatabase.Query(ss.str().c_str());
- if( !result )
- return 0;
- fields = result->Fetch();
- uint32 map = fields[0].GetUInt32();
- float posx = fields[1].GetFloat();
- float posy = fields[2].GetFloat();
- delete result;
-
- zone = MapManager::Instance().GetZoneId(map,posx,posy);
-
- ss.str("");
- ss << "UPDATE characters SET zone='"<<zone<<"' WHERE guid='"<<GUID_LOPART(guid)<<"'";
- CharacterDatabase.Execute(ss.str().c_str());
- }
-
- return zone;
-}
-
-void Player::UpdateArea(uint32 newArea)
-{
- // FFA_PVP flags are area and not zone id dependent
- // so apply them accordingly
- m_areaUpdateId = newArea;
-
- AreaTableEntry const* area = GetAreaEntryByAreaID(newArea);
-
- if(area && (area->flags & AREA_FLAG_ARENA))
- {
- if(!isGameMaster())
- SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
- }
- else
- {
- // remove ffa flag only if not ffapvp realm
- // removal in sanctuaries and capitals is handled in zone update
- if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && !sWorld.IsFFAPvPRealm())
- RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
- }
-
- UpdateAreaDependentAuras(newArea);
-}
-
-void Player::UpdateZone(uint32 newZone)
-{
- m_zoneUpdateId = newZone;
- m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
-
- // zone changed, so area changed as well, update it
- UpdateArea(GetAreaId());
-
- AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone);
- if(!zone)
- return;
-
- if (sWorld.getConfig(CONFIG_WEATHER))
- {
- Weather *wth = sWorld.FindWeather(zone->ID);
- if(wth)
- {
- wth->SendWeatherUpdateToPlayer(this);
- }
- else
- {
- if(!sWorld.AddWeather(zone->ID))
- {
- // send fine weather packet to remove old zone's weather
- Weather::SendFineWeatherUpdateToPlayer(this);
- }
- }
- }
-
- pvpInfo.inHostileArea =
- GetTeam() == ALLIANCE && zone->team == AREATEAM_HORDE ||
- GetTeam() == HORDE && zone->team == AREATEAM_ALLY ||
- sWorld.IsPvPRealm() && zone->team == AREATEAM_NONE ||
- InBattleGround(); // overwrite for battlegrounds, maybe batter some zone flags but current known not 100% fit to this
-
- if(pvpInfo.inHostileArea) // in hostile area
- {
- if(!IsPvP() || pvpInfo.endTimer != 0)
- UpdatePvP(true, true);
- }
- else // in friendly area
- {
- if(IsPvP() && !HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_IN_PVP) && pvpInfo.endTimer == 0)
- pvpInfo.endTimer = time(0); // start toggle-off
- }
-
- if(zone->flags & AREA_FLAG_SANCTUARY) // in sanctuary
- {
- SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY);
- if(sWorld.IsFFAPvPRealm())
- RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
- }
- else
- {
- RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY);
- }
-
- if(zone->flags & AREA_FLAG_CAPITAL) // in capital city
- {
- SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
- SetRestType(REST_TYPE_IN_CITY);
- InnEnter(time(0),GetMapId(),0,0,0);
-
- if(sWorld.IsFFAPvPRealm())
- RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
- }
- else // anywhere else
- {
- if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) // but resting (walk from city or maybe in tavern or leave tavern recently)
- {
- if(GetRestType()==REST_TYPE_IN_TAVERN) // has been in tavern. Is still in?
- {
- if(GetMapId()!=GetInnPosMapId() || sqrt((GetPositionX()-GetInnPosX())*(GetPositionX()-GetInnPosX())+(GetPositionY()-GetInnPosY())*(GetPositionY()-GetInnPosY())+(GetPositionZ()-GetInnPosZ())*(GetPositionZ()-GetInnPosZ()))>40)
- {
- RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
- SetRestType(REST_TYPE_NO);
-
- if(sWorld.IsFFAPvPRealm())
- SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
- }
- }
- else // not in tavern (leave city then)
- {
- RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
- SetRestType(REST_TYPE_NO);
-
- // Set player to FFA PVP when not in rested enviroment.
- if(sWorld.IsFFAPvPRealm())
- SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
- }
- }
- }
-
- // remove items with area/map limitations (delete only for alive player to allow back in ghost mode)
- // if player resurrected at teleport this will be applied in resurrect code
- if(isAlive())
- DestroyZoneLimitedItem( true, newZone );
-
- // recent client version not send leave/join channel packets for built-in local channels
- UpdateLocalChannels( newZone );
-
- // group update
- if(GetGroup())
- SetGroupUpdateFlag(GROUP_UPDATE_FLAG_ZONE);
-
- UpdateZoneDependentAuras(newZone);
-}
-
-//If players are too far way of duel flag... then player loose the duel
-void Player::CheckDuelDistance(time_t currTime)
-{
- if(!duel)
- return;
-
- uint64 duelFlagGUID = GetUInt64Value(PLAYER_DUEL_ARBITER);
- GameObject* obj = ObjectAccessor::GetGameObject(*this, duelFlagGUID);
- if(!obj)
- return;
-
- if(duel->outOfBound == 0)
- {
- if(!IsWithinDistInMap(obj, 50))
- {
- duel->outOfBound = currTime;
-
- WorldPacket data(SMSG_DUEL_OUTOFBOUNDS, 0);
- GetSession()->SendPacket(&data);
- }
- }
- else
- {
- if(IsWithinDistInMap(obj, 40))
- {
- duel->outOfBound = 0;
-
- WorldPacket data(SMSG_DUEL_INBOUNDS, 0);
- GetSession()->SendPacket(&data);
- }
- else if(currTime >= (duel->outOfBound+10))
- {
- DuelComplete(DUEL_FLED);
- }
- }
-}
-
-void Player::DuelComplete(DuelCompleteType type)
-{
- // duel not requested
- if(!duel)
- return;
-
- WorldPacket data(SMSG_DUEL_COMPLETE, (1));
- data << (uint8)((type != DUEL_INTERUPTED) ? 1 : 0);
- GetSession()->SendPacket(&data);
- duel->opponent->GetSession()->SendPacket(&data);
-
- if(type != DUEL_INTERUPTED)
- {
- data.Initialize(SMSG_DUEL_WINNER, (1+20)); // we guess size
- data << (uint8)((type==DUEL_WON) ? 0 : 1); // 0 = just won; 1 = fled
- data << duel->opponent->GetName();
- data << GetName();
- SendMessageToSet(&data,true);
- }
-
- // cool-down duel spell
- /*data.Initialize(SMSG_SPELL_COOLDOWN, 17);
-
- data<<GetGUID();
- data<<uint8(0x0);
-
- data<<(uint32)7266;
- data<<uint32(0x0);
- GetSession()->SendPacket(&data);
- data.Initialize(SMSG_SPELL_COOLDOWN, 17);
- data<<duel->opponent->GetGUID();
- data<<uint8(0x0);
- data<<(uint32)7266;
- data<<uint32(0x0);
- duel->opponent->GetSession()->SendPacket(&data);*/
-
- //Remove Duel Flag object
- GameObject* obj = ObjectAccessor::GetGameObject(*this, GetUInt64Value(PLAYER_DUEL_ARBITER));
- if(obj)
- duel->initiator->RemoveGameObject(obj,true);
-
- /* remove auras */
- std::vector<uint32> auras2remove;
- AuraMap const& vAuras = duel->opponent->GetAuras();
- for (AuraMap::const_iterator i = vAuras.begin(); i != vAuras.end(); i++)
- {
- if (!i->second->IsPositive() && i->second->GetCasterGUID() == GetGUID() && i->second->GetAuraApplyTime() >= duel->startTime)
- auras2remove.push_back(i->second->GetId());
- }
-
- for(size_t i=0; i<auras2remove.size(); i++)
- duel->opponent->RemoveAurasDueToSpell(auras2remove[i]);
-
- auras2remove.clear();
- AuraMap const& auras = GetAuras();
- for (AuraMap::const_iterator i = auras.begin(); i != auras.end(); i++)
- {
- if (!i->second->IsPositive() && i->second->GetCasterGUID() == duel->opponent->GetGUID() && i->second->GetAuraApplyTime() >= duel->startTime)
- auras2remove.push_back(i->second->GetId());
- }
- for(size_t i=0; i<auras2remove.size(); i++)
- RemoveAurasDueToSpell(auras2remove[i]);
-
- // cleanup combo points
- if(GetComboTarget()==duel->opponent->GetGUID())
- ClearComboPoints();
- else if(GetComboTarget()==duel->opponent->GetPetGUID())
- ClearComboPoints();
-
- if(duel->opponent->GetComboTarget()==GetGUID())
- duel->opponent->ClearComboPoints();
- else if(duel->opponent->GetComboTarget()==GetPetGUID())
- duel->opponent->ClearComboPoints();
-
- // Honor points after duel (the winner) - ImpConfig
- if(sWorld.getConfig(CONFIG_HONOR_AFTER_DUEL > 0))
- {
- uint32 amount = sWorld.getConfig(CONFIG_HONOR_AFTER_DUEL);
- duel->opponent->RewardHonor(NULL,1,amount);
- }
-
- //cleanups
- SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
- SetUInt32Value(PLAYER_DUEL_TEAM, 0);
- duel->opponent->SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
- duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0);
-
- delete duel->opponent->duel;
- duel->opponent->duel = NULL;
- delete duel;
- duel = NULL;
-}
-
-//---------------------------------------------------------//
-
-void Player::_ApplyItemMods(Item *item, uint8 slot,bool apply)
-{
- if(slot >= INVENTORY_SLOT_BAG_END || !item)
- return;
-
- // not apply/remove mods for broken item
- if(item->IsBroken())
- return;
-
- ItemPrototype const *proto = item->GetProto();
-
- if(!proto)
- return;
-
- sLog.outDetail("applying mods for item %u ",item->GetGUIDLow());
-
- uint32 attacktype = Player::GetAttackBySlot(slot);
- if(attacktype < MAX_ATTACK)
- _ApplyWeaponDependentAuraMods(item,WeaponAttackType(attacktype),apply);
-
- _ApplyItemBonuses(proto,slot,apply);
-
- if( slot==EQUIPMENT_SLOT_RANGED )
- _ApplyAmmoBonuses();
-
- ApplyItemEquipSpell(item,apply);
- ApplyEnchantment(item, apply);
-
- if(proto->Socket[0].Color) //only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items
- CorrectMetaGemEnchants(slot, apply);
-
- sLog.outDebug("_ApplyItemMods complete.");
-}
-
-void Player::_ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply)
-{
- if(slot >= INVENTORY_SLOT_BAG_END || !proto)
- return;
-
- for (int i = 0; i < 10; i++)
- {
- float val = float (proto->ItemStat[i].ItemStatValue);
-
- if(val==0)
- continue;
-
- switch (proto->ItemStat[i].ItemStatType)
- {
- case ITEM_MOD_MANA:
- HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply);
- break;
- case ITEM_MOD_HEALTH: // modify HP
- HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply);
- break;
- case ITEM_MOD_AGILITY: // modify agility
- HandleStatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply);
- ApplyStatBuffMod(STAT_AGILITY, val, apply);
- break;
- case ITEM_MOD_STRENGTH: //modify strength
- HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply);
- ApplyStatBuffMod(STAT_STRENGTH, val, apply);
- break;
- case ITEM_MOD_INTELLECT: //modify intellect
- HandleStatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply);
- ApplyStatBuffMod(STAT_INTELLECT, val, apply);
- break;
- case ITEM_MOD_SPIRIT: //modify spirit
- HandleStatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply);
- ApplyStatBuffMod(STAT_SPIRIT, val, apply);
- break;
- case ITEM_MOD_STAMINA: //modify stamina
- HandleStatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply);
- ApplyStatBuffMod(STAT_STAMINA, val, apply);
- break;
- case ITEM_MOD_DEFENSE_SKILL_RATING:
- ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply);
- break;
- case ITEM_MOD_DODGE_RATING:
- ApplyRatingMod(CR_DODGE, int32(val), apply);
- break;
- case ITEM_MOD_PARRY_RATING:
- ApplyRatingMod(CR_PARRY, int32(val), apply);
- break;
- case ITEM_MOD_BLOCK_RATING:
- ApplyRatingMod(CR_BLOCK, int32(val), apply);
- break;
- case ITEM_MOD_HIT_MELEE_RATING:
- ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
- break;
- case ITEM_MOD_HIT_RANGED_RATING:
- ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
- break;
- case ITEM_MOD_HIT_SPELL_RATING:
- ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_CRIT_MELEE_RATING:
- ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
- break;
- case ITEM_MOD_CRIT_RANGED_RATING:
- ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
- break;
- case ITEM_MOD_CRIT_SPELL_RATING:
- ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
- ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
- break;
- case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
- ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
- break;
- case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
- ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
- ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
- break;
- case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
- ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
- break;
- case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
- ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_HASTE_MELEE_RATING:
- ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
- break;
- case ITEM_MOD_HASTE_RANGED_RATING:
- ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
- break;
- case ITEM_MOD_HASTE_SPELL_RATING:
- ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_HIT_RATING:
- ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
- ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
- ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_CRIT_RATING:
- ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
- ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
- ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_HIT_TAKEN_RATING:
- ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
- ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
- ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_CRIT_TAKEN_RATING:
- ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
- ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
- ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_RESILIENCE_RATING:
- ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
- ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
- ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_HASTE_RATING:
- ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
- ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
- ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
- break;
- case ITEM_MOD_EXPERTISE_RATING:
- ApplyRatingMod(CR_EXPERTISE, int32(val), apply);
- break;
- }
- }
-
- if (proto->Armor)
- HandleStatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(proto->Armor), apply);
-
- if (proto->Block)
- HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(proto->Block), apply);
-
- if (proto->HolyRes)
- HandleStatModifier(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(proto->HolyRes), apply);
-
- if (proto->FireRes)
- HandleStatModifier(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(proto->FireRes), apply);
-
- if (proto->NatureRes)
- HandleStatModifier(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(proto->NatureRes), apply);
-
- if (proto->FrostRes)
- HandleStatModifier(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(proto->FrostRes), apply);
-
- if (proto->ShadowRes)
- HandleStatModifier(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(proto->ShadowRes), apply);
-
- if (proto->ArcaneRes)
- HandleStatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(proto->ArcaneRes), apply);
-
- WeaponAttackType attType = BASE_ATTACK;
- float damage = 0.0f;
-
- if( slot == EQUIPMENT_SLOT_RANGED && (
- proto->InventoryType == INVTYPE_RANGED || proto->InventoryType == INVTYPE_THROWN ||
- proto->InventoryType == INVTYPE_RANGEDRIGHT ))
- {
- attType = RANGED_ATTACK;
- }
- else if(slot==EQUIPMENT_SLOT_OFFHAND)
- {
- attType = OFF_ATTACK;
- }
-
- if (proto->Damage[0].DamageMin > 0 )
- {
- damage = apply ? proto->Damage[0].DamageMin : BASE_MINDAMAGE;
- SetBaseWeaponDamage(attType, MINDAMAGE, damage);
- //sLog.outError("applying mindam: assigning %f to weapon mindamage, now is: %f", damage, GetWeaponDamageRange(attType, MINDAMAGE));
- }
-
- if (proto->Damage[0].DamageMax > 0 )
- {
- damage = apply ? proto->Damage[0].DamageMax : BASE_MAXDAMAGE;
- SetBaseWeaponDamage(attType, MAXDAMAGE, damage);
- }
-
- if(!IsUseEquipedWeapon(slot==EQUIPMENT_SLOT_MAINHAND))
- return;
-
- if (proto->Delay)
- {
- if(slot == EQUIPMENT_SLOT_RANGED)
- SetAttackTime(RANGED_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
- else if(slot==EQUIPMENT_SLOT_MAINHAND)
- SetAttackTime(BASE_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
- else if(slot==EQUIPMENT_SLOT_OFFHAND)
- SetAttackTime(OFF_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
- }
-
- if(CanModifyStats() && (damage || proto->Delay))
- UpdateDamagePhysical(attType);
-}
-
-void Player::_ApplyWeaponDependentAuraMods(Item *item,WeaponAttackType attackType,bool apply)
-{
- AuraList const& auraCritList = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT);
- for(AuraList::const_iterator itr = auraCritList.begin(); itr!=auraCritList.end();++itr)
- _ApplyWeaponDependentAuraCritMod(item,attackType,*itr,apply);
-
- AuraList const& auraDamageFlatList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE);
- for(AuraList::const_iterator itr = auraDamageFlatList.begin(); itr!=auraDamageFlatList.end();++itr)
- _ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply);
-
- AuraList const& auraDamagePCTList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
- for(AuraList::const_iterator itr = auraDamagePCTList.begin(); itr!=auraDamagePCTList.end();++itr)
- _ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply);
-}
-
-void Player::_ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply)
-{
- // generic not weapon specific case processes in aura code
- if(aura->GetSpellProto()->EquippedItemClass == -1)
- return;
-
- BaseModGroup mod = BASEMOD_END;
- switch(attackType)
- {
- case BASE_ATTACK: mod = CRIT_PERCENTAGE; break;
- case OFF_ATTACK: mod = OFFHAND_CRIT_PERCENTAGE;break;
- case RANGED_ATTACK: mod = RANGED_CRIT_PERCENTAGE; break;
- default: return;
- }
-
- if (item->IsFitToSpellRequirements(aura->GetSpellProto()))
- {
- HandleBaseModValue(mod, FLAT_MOD, float (aura->GetModifier()->m_amount), apply);
- }
-}
-
-void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply)
-{
- // ignore spell mods for not wands
- Modifier const* modifier = aura->GetModifier();
- if((modifier->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)==0 && (getClassMask() & CLASSMASK_WAND_USERS)==0)
- return;
-
- // generic not weapon specific case processes in aura code
- if(aura->GetSpellProto()->EquippedItemClass == -1)
- return;
-
- UnitMods unitMod = UNIT_MOD_END;
- switch(attackType)
- {
- case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break;
- case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break;
- case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break;
- default: return;
- }
-
- UnitModifierType unitModType = TOTAL_VALUE;
- switch(modifier->m_auraname)
- {
- case SPELL_AURA_MOD_DAMAGE_DONE: unitModType = TOTAL_VALUE; break;
- case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: unitModType = TOTAL_PCT; break;
- default: return;
- }
-
- if (item->IsFitToSpellRequirements(aura->GetSpellProto()))
- {
- HandleStatModifier(unitMod, unitModType, float(modifier->m_amount),apply);
- }
-}
-
-void Player::ApplyItemEquipSpell(Item *item, bool apply, bool form_change)
-{
- if(!item)
- return;
-
- ItemPrototype const *proto = item->GetProto();
- if(!proto)
- return;
-
- for (int i = 0; i < 5; i++)
- {
- _Spell const& spellData = proto->Spells[i];
-
- // no spell
- if(!spellData.SpellId )
- continue;
-
- // wrong triggering type
- if(apply && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP)
- continue;
-
- // check if it is valid spell
- SpellEntry const* spellproto = sSpellStore.LookupEntry(spellData.SpellId);
- if(!spellproto)
- continue;
-
- ApplyEquipSpell(spellproto,item,apply,form_change);
- }
-}
-
-void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply, bool form_change)
-{
- if(apply)
- {
- // Cannot be used in this stance/form
- if(GetErrorAtShapeshiftedCast(spellInfo, m_form)!=0)
- return;
-
- if(form_change) // check aura active state from other form
- {
- bool found = false;
- for (int k=0; k < 3; ++k)
- {
- spellEffectPair spair = spellEffectPair(spellInfo->Id, k);
- for (AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair); ++iter)
- {
- if(!item || iter->second->GetCastItemGUID() == item->GetGUID())
- {
- found = true;
- break;
- }
- }
- if(found)
- break;
- }
-
- if(found) // and skip re-cast already active aura at form change
- return;
- }
-
- DEBUG_LOG("WORLD: cast %s Equip spellId - %i", (item ? "item" : "itemset"), spellInfo->Id);
-
- CastSpell(this,spellInfo,true,item);
- }
- else
- {
- if(form_change) // check aura compatibility
- {
- // Cannot be used in this stance/form
- if(GetErrorAtShapeshiftedCast(spellInfo, m_form)==0)
- return; // and remove only not compatible at form change
- }
-
- if(item)
- RemoveAurasDueToItemSpell(item,spellInfo->Id); // un-apply all spells , not only at-equipped
- else
- RemoveAurasDueToSpell(spellInfo->Id); // un-apply spell (item set case)
- }
-}
-
-void Player::UpdateEquipSpellsAtFormChange()
-{
- for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
- {
- if(m_items[i] && !m_items[i]->IsBroken())
- {
- ApplyItemEquipSpell(m_items[i],false,true); // remove spells that not fit to form
- ApplyItemEquipSpell(m_items[i],true,true); // add spells that fit form but not active
- }
- }
-
- // item set bonuses not dependent from item broken state
- for(size_t setindex = 0; setindex < ItemSetEff.size(); ++setindex)
- {
- ItemSetEffect* eff = ItemSetEff[setindex];
- if(!eff)
- continue;
-
- for(uint32 y=0;y<8; ++y)
- {
- SpellEntry const* spellInfo = eff->spells[y];
- if(!spellInfo)
- continue;
-
- ApplyEquipSpell(spellInfo,NULL,false,true); // remove spells that not fit to form
- ApplyEquipSpell(spellInfo,NULL,true,true); // add spells that fit form but not active
- }
- }
-}
-
-void Player::CastItemCombatSpell(Item *item,Unit* Target, WeaponAttackType attType)
-{
- if(!item || item->IsBroken())
- return;
-
- ItemPrototype const *proto = item->GetProto();
- if(!proto)
- return;
-
- if (!Target || Target == this )
- return;
-
- for (int i = 0; i < 5; i++)
- {
- _Spell const& spellData = proto->Spells[i];
-
- // no spell
- if(!spellData.SpellId )
- continue;
-
- // wrong triggering type
- if(spellData.SpellTrigger != ITEM_SPELLTRIGGER_CHANCE_ON_HIT)
- continue;
-
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId);
- if(!spellInfo)
- {
- sLog.outError("WORLD: unknown Item spellid %i", spellData.SpellId);
- continue;
- }
-
- // not allow proc extra attack spell at extra attack
- if( m_extraAttacks && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_ADD_EXTRA_ATTACKS) )
- return;
-
- float chance = spellInfo->procChance;
-
- if(spellData.SpellPPMRate)
- {
- uint32 WeaponSpeed = GetAttackTime(attType);
- chance = GetPPMProcChance(WeaponSpeed, spellData.SpellPPMRate);
- }
- else if(chance > 100.0f)
- {
- chance = GetWeaponProcChance();
- }
-
- if (roll_chance_f(chance))
- this->CastSpell(Target, spellInfo->Id, true, item);
- }
-
- // item combat enchantments
- for(int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot)
- {
- uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot));
- SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!pEnchant) continue;
- for (int s=0;s<3;s++)
- {
- if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
- continue;
-
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(pEnchant->spellid[s]);
- if (!spellInfo)
- {
- sLog.outError("Player::CastItemCombatSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]);
- continue;
- }
-
- float chance = pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance();
- if (roll_chance_f(chance))
- {
- if(IsPositiveSpell(pEnchant->spellid[s]))
- CastSpell(this, pEnchant->spellid[s], true, item);
- else
- CastSpell(Target, pEnchant->spellid[s], true, item);
- }
- }
- }
-}
-
-void Player::_RemoveAllItemMods()
-{
- sLog.outDebug("_RemoveAllItemMods start.");
-
- for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
- {
- if(m_items[i])
- {
- ItemPrototype const *proto = m_items[i]->GetProto();
- if(!proto)
- continue;
-
- // item set bonuses not dependent from item broken state
- if(proto->ItemSet)
- RemoveItemsSetItem(this,proto);
-
- if(m_items[i]->IsBroken())
- continue;
-
- ApplyItemEquipSpell(m_items[i],false);
- ApplyEnchantment(m_items[i], false);
- }
- }
-
- for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
- {
- if(m_items[i])
- {
- if(m_items[i]->IsBroken())
- continue;
- ItemPrototype const *proto = m_items[i]->GetProto();
- if(!proto)
- continue;
-
- uint32 attacktype = Player::GetAttackBySlot(i);
- if(attacktype < MAX_ATTACK)
- _ApplyWeaponDependentAuraMods(m_items[i],WeaponAttackType(attacktype),false);
-
- _ApplyItemBonuses(proto,i, false);
-
- if( i == EQUIPMENT_SLOT_RANGED )
- _ApplyAmmoBonuses();
- }
- }
-
- sLog.outDebug("_RemoveAllItemMods complete.");
-}
-
-void Player::_ApplyAllItemMods()
-{
- sLog.outDebug("_ApplyAllItemMods start.");
-
- for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
- {
- if(m_items[i])
- {
- if(m_items[i]->IsBroken())
- continue;
-
- ItemPrototype const *proto = m_items[i]->GetProto();
- if(!proto)
- continue;
-
- uint32 attacktype = Player::GetAttackBySlot(i);
- if(attacktype < MAX_ATTACK)
- _ApplyWeaponDependentAuraMods(m_items[i],WeaponAttackType(attacktype),true);
-
- _ApplyItemBonuses(proto,i, true);
-
- if( i == EQUIPMENT_SLOT_RANGED )
- _ApplyAmmoBonuses();
- }
- }
-
- for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
- {
- if(m_items[i])
- {
- ItemPrototype const *proto = m_items[i]->GetProto();
- if(!proto)
- continue;
-
- // item set bonuses not dependent from item broken state
- if(proto->ItemSet)
- AddItemsSetItem(this,m_items[i]);
-
- if(m_items[i]->IsBroken())
- continue;
-
- ApplyItemEquipSpell(m_items[i],true);
- ApplyEnchantment(m_items[i], true);
- }
- }
-
- sLog.outDebug("_ApplyAllItemMods complete.");
-}
-
-void Player::_ApplyAmmoBonuses()
-{
- // check ammo
- uint32 ammo_id = GetUInt32Value(PLAYER_AMMO_ID);
- if(!ammo_id)
- return;
-
- float currentAmmoDPS;
-
- ItemPrototype const *ammo_proto = objmgr.GetItemPrototype( ammo_id );
- if( !ammo_proto || ammo_proto->Class!=ITEM_CLASS_PROJECTILE || !CheckAmmoCompatibility(ammo_proto))
- currentAmmoDPS = 0.0f;
- else
- currentAmmoDPS = ammo_proto->Damage[0].DamageMin;
-
- if(currentAmmoDPS == GetAmmoDPS())
- return;
-
- m_ammoDPS = currentAmmoDPS;
-
- if(CanModifyStats())
- UpdateDamagePhysical(RANGED_ATTACK);
-}
-
-bool Player::CheckAmmoCompatibility(const ItemPrototype *ammo_proto) const
-{
- if(!ammo_proto)
- return false;
-
- // check ranged weapon
- Item *weapon = GetWeaponForAttack( RANGED_ATTACK );
- if(!weapon || weapon->IsBroken() )
- return false;
-
- ItemPrototype const* weapon_proto = weapon->GetProto();
- if(!weapon_proto || weapon_proto->Class!=ITEM_CLASS_WEAPON )
- return false;
-
- // check ammo ws. weapon compatibility
- switch(weapon_proto->SubClass)
- {
- case ITEM_SUBCLASS_WEAPON_BOW:
- case ITEM_SUBCLASS_WEAPON_CROSSBOW:
- if(ammo_proto->SubClass!=ITEM_SUBCLASS_ARROW)
- return false;
- break;
- case ITEM_SUBCLASS_WEAPON_GUN:
- if(ammo_proto->SubClass!=ITEM_SUBCLASS_BULLET)
- return false;
- break;
- default:
- return false;
- }
-
- return true;
-}
-
-/* If in a battleground a player dies, and an enemy removes the insignia, the player's bones is lootable
- Called by remove insignia spell effect */
-void Player::RemovedInsignia(Player* looterPlr)
-{
- if (!GetBattleGroundId())
- return;
-
- // If not released spirit, do it !
- if(m_deathTimer > 0)
- {
- m_deathTimer = 0;
- BuildPlayerRepop();
- RepopAtGraveyard();
- }
-
- Corpse *corpse = GetCorpse();
- if (!corpse)
- return;
-
- // We have to convert player corpse to bones, not to be able to resurrect there
- // SpawnCorpseBones isn't handy, 'cos it saves player while he in BG
- Corpse *bones = ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID());
- if (!bones)
- return;
-
- // Now we must make bones lootable, and send player loot
- bones->SetFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);
-
- // We store the level of our player in the gold field
- // We retrieve this information at Player::SendLoot()
- bones->loot.gold = getLevel();
- bones->lootRecipient = looterPlr;
- looterPlr->SendLoot(bones->GetGUID(), LOOT_INSIGNIA);
-}
-
-/*Loot type MUST be
-1-corpse, go
-2-skinning
-3-Fishing
-*/
-
-void Player::SendLootRelease( uint64 guid )
-{
- WorldPacket data( SMSG_LOOT_RELEASE_RESPONSE, (8+1) );
- data << uint64(guid) << uint8(1);
- SendDirectMessage( &data );
-}
-
-void Player::SendLoot(uint64 guid, LootType loot_type)
-{
- Loot *loot = 0;
- PermissionTypes permission = ALL_PERMISSION;
-
- sLog.outDebug("Player::SendLoot");
- if (IS_GAMEOBJECT_GUID(guid))
- {
- sLog.outDebug(" IS_GAMEOBJECT_GUID(guid)");
- GameObject *go =
- ObjectAccessor::GetGameObject(*this, guid);
-
- // not check distance for GO in case owned GO (fishing bobber case, for example)
- // And permit out of range GO with no owner in case fishing hole
- if (!go || (loot_type != LOOT_FISHINGHOLE && (loot_type != LOOT_FISHING || go->GetOwnerGUID() != GetGUID()) && !go->IsWithinDistInMap(this,INTERACTION_DISTANCE)))
- {
- SendLootRelease(guid);
- return;
- }
-
- loot = &go->loot;
-
- if(go->getLootState() == GO_READY)
- {
- uint32 lootid = go->GetLootId();
-
- if(lootid)
- {
- sLog.outDebug(" if(lootid)");
- loot->clear();
- loot->FillLoot(lootid, LootTemplates_Gameobject, this);
- }
-
- if(loot_type == LOOT_FISHING)
- go->getFishLoot(loot);
-
- go->SetLootState(GO_ACTIVATED);
- }
- }
- else if (IS_ITEM_GUID(guid))
- {
- Item *item = GetItemByGuid( guid );
-
- if (!item)
- {
- SendLootRelease(guid);
- return;
- }
-
- if(loot_type == LOOT_DISENCHANTING)
- {
- loot = &item->loot;
-
- if(!item->m_lootGenerated)
- {
- item->m_lootGenerated = true;
- loot->clear();
- loot->FillLoot(item->GetProto()->DisenchantID, LootTemplates_Disenchant, this);
- }
- }
- else if(loot_type == LOOT_PROSPECTING)
- {
- loot = &item->loot;
-
- if(!item->m_lootGenerated)
- {
- item->m_lootGenerated = true;
- loot->clear();
- loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this);
- }
- }
- else
- {
- loot = &item->loot;
-
- if(!item->m_lootGenerated)
- {
- item->m_lootGenerated = true;
- loot->clear();
- loot->FillLoot(item->GetEntry(), LootTemplates_Item, this);
-
- loot->generateMoneyLoot(item->GetProto()->MinMoneyLoot,item->GetProto()->MaxMoneyLoot);
- }
- }
- }
- else if (IS_CORPSE_GUID(guid)) // remove insignia
- {
- Corpse *bones = ObjectAccessor::GetCorpse(*this, guid);
-
- if (!bones || !((loot_type == LOOT_CORPSE) || (loot_type == LOOT_INSIGNIA)) || (bones->GetType() != CORPSE_BONES) )
- {
- SendLootRelease(guid);
- return;
- }
-
- loot = &bones->loot;
-
- if (!bones->lootForBody)
- {
- bones->lootForBody = true;
- uint32 pLevel = bones->loot.gold;
- bones->loot.clear();
- // It may need a better formula
- // Now it works like this: lvl10: ~6copper, lvl70: ~9silver
- bones->loot.gold = (uint32)( urand(50, 150) * 0.016f * pow( ((float)pLevel)/5.76f, 2.5f) * sWorld.getRate(RATE_DROP_MONEY) );
- }
-
- if (bones->lootRecipient != this)
- permission = NONE_PERMISSION;
- }
- else
- {
- Creature *creature = ObjectAccessor::GetCreature(*this, guid);
-
- // must be in range and creature must be alive for pickpocket and must be dead for another loot
- if (!creature || creature->isAlive()!=(loot_type == LOOT_PICKPOCKETING) || !creature->IsWithinDistInMap(this,INTERACTION_DISTANCE))
- {
- SendLootRelease(guid);
- return;
- }
-
- if(loot_type == LOOT_PICKPOCKETING && IsFriendlyTo(creature))
- {
- SendLootRelease(guid);
- return;
- }
-
- loot = &creature->loot;
-
- if(loot_type == LOOT_PICKPOCKETING)
- {
- if ( !creature->lootForPickPocketed )
- {
- creature->lootForPickPocketed = true;
- loot->clear();
-
- if (uint32 lootid = creature->GetCreatureInfo()->pickpocketLootId)
- loot->FillLoot(lootid, LootTemplates_Pickpocketing, this);
-
- // Generate extra money for pick pocket loot
- const uint32 a = urand(0, creature->getLevel()/2);
- const uint32 b = urand(0, getLevel()/2);
- loot->gold = uint32(10 * (a + b) * sWorld.getRate(RATE_DROP_MONEY));
- }
- }
- else
- {
- // the player whose group may loot the corpse
- Player *recipient = creature->GetLootRecipient();
- if (!recipient)
- {
- creature->SetLootRecipient(this);
- recipient = this;
- }
-
- if (creature->lootForPickPocketed)
- {
- creature->lootForPickPocketed = false;
- loot->clear();
- }
-
- if(!creature->lootForBody)
- {
- creature->lootForBody = true;
- loot->clear();
-
- if (uint32 lootid = creature->GetCreatureInfo()->lootid)
- loot->FillLoot(lootid, LootTemplates_Creature, recipient);
-
- loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold);
-
- if(Group* group = recipient->GetGroup())
- {
- group->UpdateLooterGuid(creature,true);
-
- switch (group->GetLootMethod())
- {
- case GROUP_LOOT:
- // GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with quality<threshold, round robin
- group->GroupLoot(recipient->GetGUID(), loot, creature);
- break;
- case NEED_BEFORE_GREED:
- group->NeedBeforeGreed(recipient->GetGUID(), loot, creature);
- break;
- case MASTER_LOOT:
- group->MasterLoot(recipient->GetGUID(), loot, creature);
- break;
- default:
- break;
- }
- }
- }
-
- // possible only if creature->lootForBody && loot->empty() at spell cast check
- if (loot_type == LOOT_SKINNING)
- {
- loot->clear();
- loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this);
- }
- // set group rights only for loot_type != LOOT_SKINNING
- else
- {
- if(Group* group = GetGroup())
- {
- if( group == recipient->GetGroup() )
- {
- if(group->GetLootMethod() == FREE_FOR_ALL)
- permission = ALL_PERMISSION;
- else if(group->GetLooterGuid() == GetGUID())
- {
- if(group->GetLootMethod() == MASTER_LOOT)
- permission = MASTER_PERMISSION;
- else
- permission = ALL_PERMISSION;
- }
- else
- permission = GROUP_PERMISSION;
- }
- else
- permission = NONE_PERMISSION;
- }
- else if(recipient == this)
- permission = ALL_PERMISSION;
- else
- permission = NONE_PERMISSION;
- }
- }
- }
-
- SetLootGUID(guid);
-
- QuestItemList *q_list = 0;
- if (permission != NONE_PERMISSION)
- {
- QuestItemMap const& lootPlayerQuestItems = loot->GetPlayerQuestItems();
- QuestItemMap::const_iterator itr = lootPlayerQuestItems.find(GetGUIDLow());
- if (itr == lootPlayerQuestItems.end())
- q_list = loot->FillQuestLoot(this);
- else
- q_list = itr->second;
- }
-
- QuestItemList *ffa_list = 0;
- if (permission != NONE_PERMISSION)
- {
- QuestItemMap const& lootPlayerFFAItems = loot->GetPlayerFFAItems();
- QuestItemMap::const_iterator itr = lootPlayerFFAItems.find(GetGUIDLow());
- if (itr == lootPlayerFFAItems.end())
- ffa_list = loot->FillFFALoot(this);
- else
- ffa_list = itr->second;
- }
-
- QuestItemList *conditional_list = 0;
- if (permission != NONE_PERMISSION)
- {
- QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = loot->GetPlayerNonQuestNonFFAConditionalItems();
- QuestItemMap::const_iterator itr = lootPlayerNonQuestNonFFAConditionalItems.find(GetGUIDLow());
- if (itr == lootPlayerNonQuestNonFFAConditionalItems.end())
- conditional_list = loot->FillNonQuestNonFFAConditionalLoot(this);
- else
- conditional_list = itr->second;
- }
-
- // LOOT_PICKPOCKETING, LOOT_PROSPECTING, LOOT_DISENCHANTING and LOOT_INSIGNIA unsupported by client, sending LOOT_SKINNING instead
- if(loot_type == LOOT_PICKPOCKETING || loot_type == LOOT_DISENCHANTING || loot_type == LOOT_PROSPECTING || loot_type == LOOT_INSIGNIA)
- loot_type = LOOT_SKINNING;
-
- if(loot_type == LOOT_FISHINGHOLE)
- loot_type = LOOT_FISHING;
-
- WorldPacket data(SMSG_LOOT_RESPONSE, (9+50)); // we guess size
-
- data << uint64(guid);
- data << uint8(loot_type);
- data << LootView(*loot, q_list, ffa_list, conditional_list, this, permission);
-
- SendDirectMessage(&data);
-
- // add 'this' player as one of the players that are looting 'loot'
- if (permission != NONE_PERMISSION)
- loot->AddLooter(GetGUID());
-
- if ( loot_type == LOOT_CORPSE && !IS_ITEM_GUID(guid) )
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
-}
-
-void Player::SendNotifyLootMoneyRemoved()
-{
- WorldPacket data(SMSG_LOOT_CLEAR_MONEY, 0);
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendNotifyLootItemRemoved(uint8 lootSlot)
-{
- WorldPacket data(SMSG_LOOT_REMOVED, 1);
- data << uint8(lootSlot);
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendUpdateWorldState(uint32 Field, uint32 Value)
-{
- WorldPacket data(SMSG_UPDATE_WORLD_STATE, 8);
- data << Field;
- data << Value;
- GetSession()->SendPacket(&data);
-}
-
-void Player::SendInitWorldStates()
-{
- // data depends on zoneid/mapid...
- BattleGround* bg = GetBattleGround();
- uint16 NumberOfFields = 0;
- uint32 mapid = GetMapId();
- uint32 zoneid = GetZoneId();
- uint32 areaid = GetAreaId();
- sLog.outDebug("Sending SMSG_INIT_WORLD_STATES to Map:%u, Zone: %u", mapid, zoneid);
- // may be exist better way to do this...
- switch(zoneid)
- {
- case 0:
- case 1:
- case 4:
- case 8:
- case 10:
- case 11:
- case 12:
- case 36:
- case 38:
- case 40:
- case 41:
- case 51:
- case 267:
- case 1519:
- case 1537:
- case 2257:
- case 2918:
- NumberOfFields = 6;
- break;
- case 2597:
- NumberOfFields = 81;
- break;
- case 3277:
- NumberOfFields = 14;
- break;
- case 3358:
- case 3820:
- NumberOfFields = 38;
- break;
- case 3483:
- NumberOfFields = 22;
- break;
- case 3519:
- NumberOfFields = 36;
- break;
- case 3521:
- NumberOfFields = 35;
- break;
- case 3698:
- case 3702:
- case 3968:
- NumberOfFields = 9;
- break;
- case 3703:
- NumberOfFields = 9;
- break;
- default:
- NumberOfFields = 10;
- break;
- }
-
- WorldPacket data(SMSG_INIT_WORLD_STATES, (4+4+4+2+(NumberOfFields*8)));
- data << uint32(mapid); // mapid
- data << uint32(zoneid); // zone id
- data << uint32(areaid); // area id, new 2.1.0
- data << uint16(NumberOfFields); // count of uint64 blocks
- data << uint32(0x8d8) << uint32(0x0); // 1
- data << uint32(0x8d7) << uint32(0x0); // 2
- data << uint32(0x8d6) << uint32(0x0); // 3
- data << uint32(0x8d5) << uint32(0x0); // 4
- data << uint32(0x8d4) << uint32(0x0); // 5
- data << uint32(0x8d3) << uint32(0x0); // 6
- if(mapid == 530) // Outland
- {
- data << uint32(0x9bf) << uint32(0x0); // 7
- data << uint32(0x9bd) << uint32(0xF); // 8
- data << uint32(0x9bb) << uint32(0xF); // 9
- }
- switch(zoneid)
- {
- case 1:
- case 11:
- case 12:
- case 38:
- case 40:
- case 51:
- case 1519:
- case 1537:
- case 2257:
- break;
- case 2597: // AV
- data << uint32(0x7ae) << uint32(0x1); // 7
- data << uint32(0x532) << uint32(0x1); // 8
- data << uint32(0x531) << uint32(0x0); // 9
- data << uint32(0x52e) << uint32(0x0); // 10
- data << uint32(0x571) << uint32(0x0); // 11
- data << uint32(0x570) << uint32(0x0); // 12
- data << uint32(0x567) << uint32(0x1); // 13
- data << uint32(0x566) << uint32(0x1); // 14
- data << uint32(0x550) << uint32(0x1); // 15
- data << uint32(0x544) << uint32(0x0); // 16
- data << uint32(0x536) << uint32(0x0); // 17
- data << uint32(0x535) << uint32(0x1); // 18
- data << uint32(0x518) << uint32(0x0); // 19
- data << uint32(0x517) << uint32(0x0); // 20
- data << uint32(0x574) << uint32(0x0); // 21
- data << uint32(0x573) << uint32(0x0); // 22
- data << uint32(0x572) << uint32(0x0); // 23
- data << uint32(0x56f) << uint32(0x0); // 24
- data << uint32(0x56e) << uint32(0x0); // 25
- data << uint32(0x56d) << uint32(0x0); // 26
- data << uint32(0x56c) << uint32(0x0); // 27
- data << uint32(0x56b) << uint32(0x0); // 28
- data << uint32(0x56a) << uint32(0x1); // 29
- data << uint32(0x569) << uint32(0x1); // 30
- data << uint32(0x568) << uint32(0x1); // 13
- data << uint32(0x565) << uint32(0x0); // 32
- data << uint32(0x564) << uint32(0x0); // 33
- data << uint32(0x563) << uint32(0x0); // 34
- data << uint32(0x562) << uint32(0x0); // 35
- data << uint32(0x561) << uint32(0x0); // 36
- data << uint32(0x560) << uint32(0x0); // 37
- data << uint32(0x55f) << uint32(0x0); // 38
- data << uint32(0x55e) << uint32(0x0); // 39
- data << uint32(0x55d) << uint32(0x0); // 40
- data << uint32(0x3c6) << uint32(0x4); // 41
- data << uint32(0x3c4) << uint32(0x6); // 42
- data << uint32(0x3c2) << uint32(0x4); // 43
- data << uint32(0x516) << uint32(0x1); // 44
- data << uint32(0x515) << uint32(0x0); // 45
- data << uint32(0x3b6) << uint32(0x6); // 46
- data << uint32(0x55c) << uint32(0x0); // 47
- data << uint32(0x55b) << uint32(0x0); // 48
- data << uint32(0x55a) << uint32(0x0); // 49
- data << uint32(0x559) << uint32(0x0); // 50
- data << uint32(0x558) << uint32(0x0); // 51
- data << uint32(0x557) << uint32(0x0); // 52
- data << uint32(0x556) << uint32(0x0); // 53
- data << uint32(0x555) << uint32(0x0); // 54
- data << uint32(0x554) << uint32(0x1); // 55
- data << uint32(0x553) << uint32(0x1); // 56
- data << uint32(0x552) << uint32(0x1); // 57
- data << uint32(0x551) << uint32(0x1); // 58
- data << uint32(0x54f) << uint32(0x0); // 59
- data << uint32(0x54e) << uint32(0x0); // 60
- data << uint32(0x54d) << uint32(0x1); // 61
- data << uint32(0x54c) << uint32(0x0); // 62
- data << uint32(0x54b) << uint32(0x0); // 63
- data << uint32(0x545) << uint32(0x0); // 64
- data << uint32(0x543) << uint32(0x1); // 65
- data << uint32(0x542) << uint32(0x0); // 66
- data << uint32(0x540) << uint32(0x0); // 67
- data << uint32(0x53f) << uint32(0x0); // 68
- data << uint32(0x53e) << uint32(0x0); // 69
- data << uint32(0x53d) << uint32(0x0); // 70
- data << uint32(0x53c) << uint32(0x0); // 71
- data << uint32(0x53b) << uint32(0x0); // 72
- data << uint32(0x53a) << uint32(0x1); // 73
- data << uint32(0x539) << uint32(0x0); // 74
- data << uint32(0x538) << uint32(0x0); // 75
- data << uint32(0x537) << uint32(0x0); // 76
- data << uint32(0x534) << uint32(0x0); // 77
- data << uint32(0x533) << uint32(0x0); // 78
- data << uint32(0x530) << uint32(0x0); // 79
- data << uint32(0x52f) << uint32(0x0); // 80
- data << uint32(0x52d) << uint32(0x1); // 81
- break;
- case 3277: // WS
- if (bg && bg->GetTypeID() == BATTLEGROUND_WS)
- bg->FillInitialWorldStates(data);
- else
- {
- data << uint32(0x62d) << uint32(0x0); // 7 1581 alliance flag captures
- data << uint32(0x62e) << uint32(0x0); // 8 1582 horde flag captures
- data << uint32(0x609) << uint32(0x0); // 9 1545 unk, set to 1 on alliance flag pickup...
- data << uint32(0x60a) << uint32(0x0); // 10 1546 unk, set to 1 on horde flag pickup, after drop it's -1
- data << uint32(0x60b) << uint32(0x2); // 11 1547 unk
- data << uint32(0x641) << uint32(0x3); // 12 1601 unk (max flag captures?)
- data << uint32(0x922) << uint32(0x1); // 13 2338 horde (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
- data << uint32(0x923) << uint32(0x1); // 14 2339 alliance (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
- }
- break;
- case 3358: // AB
- if (bg && bg->GetTypeID() == BATTLEGROUND_AB)
- bg->FillInitialWorldStates(data);
- else
- {
- data << uint32(0x6e7) << uint32(0x0); // 7 1767 stables alliance
- data << uint32(0x6e8) << uint32(0x0); // 8 1768 stables horde
- data << uint32(0x6e9) << uint32(0x0); // 9 1769 unk, ST?
- data << uint32(0x6ea) << uint32(0x0); // 10 1770 stables (show/hide)
- data << uint32(0x6ec) << uint32(0x0); // 11 1772 farm (0 - horde controlled, 1 - alliance controlled)
- data << uint32(0x6ed) << uint32(0x0); // 12 1773 farm (show/hide)
- data << uint32(0x6ee) << uint32(0x0); // 13 1774 farm color
- data << uint32(0x6ef) << uint32(0x0); // 14 1775 gold mine color, may be FM?
- data << uint32(0x6f0) << uint32(0x0); // 15 1776 alliance resources
- data << uint32(0x6f1) << uint32(0x0); // 16 1777 horde resources
- data << uint32(0x6f2) << uint32(0x0); // 17 1778 horde bases
- data << uint32(0x6f3) << uint32(0x0); // 18 1779 alliance bases
- data << uint32(0x6f4) << uint32(0x7d0); // 19 1780 max resources (2000)
- data << uint32(0x6f6) << uint32(0x0); // 20 1782 blacksmith color
- data << uint32(0x6f7) << uint32(0x0); // 21 1783 blacksmith (show/hide)
- data << uint32(0x6f8) << uint32(0x0); // 22 1784 unk, bs?
- data << uint32(0x6f9) << uint32(0x0); // 23 1785 unk, bs?
- data << uint32(0x6fb) << uint32(0x0); // 24 1787 gold mine (0 - horde contr, 1 - alliance contr)
- data << uint32(0x6fc) << uint32(0x0); // 25 1788 gold mine (0 - conflict, 1 - horde)
- data << uint32(0x6fd) << uint32(0x0); // 26 1789 gold mine (1 - show/0 - hide)
- data << uint32(0x6fe) << uint32(0x0); // 27 1790 gold mine color
- data << uint32(0x700) << uint32(0x0); // 28 1792 gold mine color, wtf?, may be LM?
- data << uint32(0x701) << uint32(0x0); // 29 1793 lumber mill color (0 - conflict, 1 - horde contr)
- data << uint32(0x702) << uint32(0x0); // 30 1794 lumber mill (show/hide)
- data << uint32(0x703) << uint32(0x0); // 31 1795 lumber mill color color
- data << uint32(0x732) << uint32(0x1); // 32 1842 stables (1 - uncontrolled)
- data << uint32(0x733) << uint32(0x1); // 33 1843 gold mine (1 - uncontrolled)
- data << uint32(0x734) << uint32(0x1); // 34 1844 lumber mill (1 - uncontrolled)
- data << uint32(0x735) << uint32(0x1); // 35 1845 farm (1 - uncontrolled)
- data << uint32(0x736) << uint32(0x1); // 36 1846 blacksmith (1 - uncontrolled)
- data << uint32(0x745) << uint32(0x2); // 37 1861 unk
- data << uint32(0x7a3) << uint32(0x708); // 38 1955 warning limit (1800)
- }
- break;
- case 3820: // EY
- if (bg && bg->GetTypeID() == BATTLEGROUND_EY)
- bg->FillInitialWorldStates(data);
- else
- {
- data << uint32(0xac1) << uint32(0x0); // 7 2753 Horde Bases
- data << uint32(0xac0) << uint32(0x0); // 8 2752 Alliance Bases
- data << uint32(0xab6) << uint32(0x0); // 9 2742 Mage Tower - Horde conflict
- data << uint32(0xab5) << uint32(0x0); // 10 2741 Mage Tower - Alliance conflict
- data << uint32(0xab4) << uint32(0x0); // 11 2740 Fel Reaver - Horde conflict
- data << uint32(0xab3) << uint32(0x0); // 12 2739 Fel Reaver - Alliance conflict
- data << uint32(0xab2) << uint32(0x0); // 13 2738 Draenei - Alliance conflict
- data << uint32(0xab1) << uint32(0x0); // 14 2737 Draenei - Horde conflict
- data << uint32(0xab0) << uint32(0x0); // 15 2736 unk // 0 at start
- data << uint32(0xaaf) << uint32(0x0); // 16 2735 unk // 0 at start
- data << uint32(0xaad) << uint32(0x0); // 17 2733 Draenei - Horde control
- data << uint32(0xaac) << uint32(0x0); // 18 2732 Draenei - Alliance control
- data << uint32(0xaab) << uint32(0x1); // 19 2731 Draenei uncontrolled (1 - yes, 0 - no)
- data << uint32(0xaaa) << uint32(0x0); // 20 2730 Mage Tower - Alliance control
- data << uint32(0xaa9) << uint32(0x0); // 21 2729 Mage Tower - Horde control
- data << uint32(0xaa8) << uint32(0x1); // 22 2728 Mage Tower uncontrolled (1 - yes, 0 - no)
- data << uint32(0xaa7) << uint32(0x0); // 23 2727 Fel Reaver - Horde control
- data << uint32(0xaa6) << uint32(0x0); // 24 2726 Fel Reaver - Alliance control
- data << uint32(0xaa5) << uint32(0x1); // 25 2725 Fel Reaver uncontroled (1 - yes, 0 - no)
- data << uint32(0xaa4) << uint32(0x0); // 26 2724 Boold Elf - Horde control
- data << uint32(0xaa3) << uint32(0x0); // 27 2723 Boold Elf - Alliance control
- data << uint32(0xaa2) << uint32(0x1); // 28 2722 Boold Elf uncontrolled (1 - yes, 0 - no)
- data << uint32(0xac5) << uint32(0x1); // 29 2757 Flag (1 - show, 0 - hide) - doesn't work exactly this way!
- data << uint32(0xad2) << uint32(0x1); // 30 2770 Horde top-stats (1 - show, 0 - hide) // 02 -> horde picked up the flag
- data << uint32(0xad1) << uint32(0x1); // 31 2769 Alliance top-stats (1 - show, 0 - hide) // 02 -> alliance picked up the flag
- data << uint32(0xabe) << uint32(0x0); // 32 2750 Horde resources
- data << uint32(0xabd) << uint32(0x0); // 33 2749 Alliance resources
- data << uint32(0xa05) << uint32(0x8e); // 34 2565 unk, constant?
- data << uint32(0xaa0) << uint32(0x0); // 35 2720 Capturing progress-bar (100 -> empty (only grey), 0 -> blue|red (no grey), default 0)
- data << uint32(0xa9f) << uint32(0x0); // 36 2719 Capturing progress-bar (0 - left, 100 - right)
- data << uint32(0xa9e) << uint32(0x0); // 37 2718 Capturing progress-bar (1 - show, 0 - hide)
- data << uint32(0xc0d) << uint32(0x17b); // 38 3085 unk
- // and some more ... unknown
- }
- break;
- case 3483: // Hellfire Peninsula
- data << uint32(0x9ba) << uint32(0x1); // 10
- data << uint32(0x9b9) << uint32(0x1); // 11
- data << uint32(0x9b5) << uint32(0x0); // 12
- data << uint32(0x9b4) << uint32(0x1); // 13
- data << uint32(0x9b3) << uint32(0x0); // 14
- data << uint32(0x9b2) << uint32(0x0); // 15
- data << uint32(0x9b1) << uint32(0x1); // 16
- data << uint32(0x9b0) << uint32(0x0); // 17
- data << uint32(0x9ae) << uint32(0x0); // 18 horde pvp objectives captured
- data << uint32(0x9ac) << uint32(0x0); // 19
- data << uint32(0x9a8) << uint32(0x0); // 20
- data << uint32(0x9a7) << uint32(0x0); // 21
- data << uint32(0x9a6) << uint32(0x1); // 22
- break;
- case 3519: // Terokkar Forest
- data << uint32(0xa41) << uint32(0x0); // 10
- data << uint32(0xa40) << uint32(0x14); // 11
- data << uint32(0xa3f) << uint32(0x0); // 12
- data << uint32(0xa3e) << uint32(0x0); // 13
- data << uint32(0xa3d) << uint32(0x5); // 14
- data << uint32(0xa3c) << uint32(0x0); // 15
- data << uint32(0xa87) << uint32(0x0); // 16
- data << uint32(0xa86) << uint32(0x0); // 17
- data << uint32(0xa85) << uint32(0x0); // 18
- data << uint32(0xa84) << uint32(0x0); // 19
- data << uint32(0xa83) << uint32(0x0); // 20
- data << uint32(0xa82) << uint32(0x0); // 21
- data << uint32(0xa81) << uint32(0x0); // 22
- data << uint32(0xa80) << uint32(0x0); // 23
- data << uint32(0xa7e) << uint32(0x0); // 24
- data << uint32(0xa7d) << uint32(0x0); // 25
- data << uint32(0xa7c) << uint32(0x0); // 26
- data << uint32(0xa7b) << uint32(0x0); // 27
- data << uint32(0xa7a) << uint32(0x0); // 28
- data << uint32(0xa79) << uint32(0x0); // 29
- data << uint32(0x9d0) << uint32(0x5); // 30
- data << uint32(0x9ce) << uint32(0x0); // 31
- data << uint32(0x9cd) << uint32(0x0); // 32
- data << uint32(0x9cc) << uint32(0x0); // 33
- data << uint32(0xa88) << uint32(0x0); // 34
- data << uint32(0xad0) << uint32(0x0); // 35
- data << uint32(0xacf) << uint32(0x1); // 36
- break;
- case 3521: // Zangarmarsh
- data << uint32(0x9e1) << uint32(0x0); // 10
- data << uint32(0x9e0) << uint32(0x0); // 11
- data << uint32(0x9df) << uint32(0x0); // 12
- data << uint32(0xa5d) << uint32(0x1); // 13
- data << uint32(0xa5c) << uint32(0x0); // 14
- data << uint32(0xa5b) << uint32(0x1); // 15
- data << uint32(0xa5a) << uint32(0x0); // 16
- data << uint32(0xa59) << uint32(0x1); // 17
- data << uint32(0xa58) << uint32(0x0); // 18
- data << uint32(0xa57) << uint32(0x0); // 19
- data << uint32(0xa56) << uint32(0x0); // 20
- data << uint32(0xa55) << uint32(0x1); // 21
- data << uint32(0xa54) << uint32(0x0); // 22
- data << uint32(0x9e7) << uint32(0x0); // 23
- data << uint32(0x9e6) << uint32(0x0); // 24
- data << uint32(0x9e5) << uint32(0x0); // 25
- data << uint32(0xa00) << uint32(0x0); // 26
- data << uint32(0x9ff) << uint32(0x1); // 27
- data << uint32(0x9fe) << uint32(0x0); // 28
- data << uint32(0x9fd) << uint32(0x0); // 29
- data << uint32(0x9fc) << uint32(0x1); // 30
- data << uint32(0x9fb) << uint32(0x0); // 31
- data << uint32(0xa62) << uint32(0x0); // 32
- data << uint32(0xa61) << uint32(0x1); // 33
- data << uint32(0xa60) << uint32(0x1); // 34
- data << uint32(0xa5f) << uint32(0x0); // 35
- break;
- case 3698: // Nagrand Arena
- if (bg && bg->GetTypeID() == BATTLEGROUND_NA)
- bg->FillInitialWorldStates(data);
- else
- {
- data << uint32(0xa0f) << uint32(0x0); // 7
- data << uint32(0xa10) << uint32(0x0); // 8
- data << uint32(0xa11) << uint32(0x0); // 9 show
- }
- break;
- case 3702: // Blade's Edge Arena
- if (bg && bg->GetTypeID() == BATTLEGROUND_BE)
- bg->FillInitialWorldStates(data);
- else
- {
- data << uint32(0x9f0) << uint32(0x0); // 7 gold
- data << uint32(0x9f1) << uint32(0x0); // 8 green
- data << uint32(0x9f3) << uint32(0x0); // 9 show
- }
- break;
- case 3968: // Ruins of Lordaeron
- if (bg && bg->GetTypeID() == BATTLEGROUND_RL)
- bg->FillInitialWorldStates(data);
- else
- {
- data << uint32(0xbb8) << uint32(0x0); // 7 gold
- data << uint32(0xbb9) << uint32(0x0); // 8 green
- data << uint32(0xbba) << uint32(0x0); // 9 show
- }
- break;
- case 3703: // Shattrath City
- break;
- default:
- data << uint32(0x914) << uint32(0x0); // 7
- data << uint32(0x913) << uint32(0x0); // 8
- data << uint32(0x912) << uint32(0x0); // 9
- data << uint32(0x915) << uint32(0x0); // 10
- break;
- }
- GetSession()->SendPacket(&data);
-}
-
-uint32 Player::GetXPRestBonus(uint32 xp)
-{
- uint32 rested_bonus = (uint32)GetRestBonus(); // xp for each rested bonus
-
- if(rested_bonus > xp) // max rested_bonus == xp or (r+x) = 200% xp
- rested_bonus = xp;
-
- SetRestBonus( GetRestBonus() - rested_bonus);
-
- sLog.outDetail("Player gain %u xp (+ %u Rested Bonus). Rested points=%f",xp+rested_bonus,rested_bonus,GetRestBonus());
- return rested_bonus;
-}
-
-void Player::SetBindPoint(uint64 guid)
-{
- WorldPacket data(SMSG_BINDER_CONFIRM, 8);
- data << uint64(guid);
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendTalentWipeConfirm(uint64 guid)
-{
- WorldPacket data(MSG_TALENT_WIPE_CONFIRM, (8+4));
- data << uint64(guid);
- data << uint32(resetTalentsCost());
- if(sWorld.getConfig(CONFIG_NO_RESET_TALENT_COST))
- data << uint32(0);
- else
- data << uint32(resetTalentsCost());
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendPetSkillWipeConfirm()
-{
- Pet* pet = GetPet();
- if(!pet)
- return;
- WorldPacket data(SMSG_PET_UNLEARN_CONFIRM, (8+4));
- data << pet->GetGUID();
- data << uint32(pet->resetTalentsCost());
- GetSession()->SendPacket( &data );
-}
-
-/*********************************************************/
-/*** STORAGE SYSTEM ***/
-/*********************************************************/
-
-void Player::SetVirtualItemSlot( uint8 i, Item* item)
-{
- assert(i < 3);
- if(i < 2 && item)
- {
- if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
- return;
- uint32 charges = item->GetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT);
- if(charges == 0)
- return;
- if(charges > 1)
- item->SetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT,charges-1);
- else if(charges <= 1)
- {
- ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false);
- item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
- }
- }
-}
-
-void Player::SetSheath( uint32 sheathed )
-{
- switch (sheathed)
- {
- case SHEATH_STATE_UNARMED: // no prepared weapon
- SetVirtualItemSlot(0,NULL);
- SetVirtualItemSlot(1,NULL);
- SetVirtualItemSlot(2,NULL);
- break;
- case SHEATH_STATE_MELEE: // prepared melee weapon
- {
- SetVirtualItemSlot(0,GetWeaponForAttack(BASE_ATTACK,true));
- SetVirtualItemSlot(1,GetWeaponForAttack(OFF_ATTACK,true));
- SetVirtualItemSlot(2,NULL);
- }; break;
- case SHEATH_STATE_RANGED: // prepared ranged weapon
- SetVirtualItemSlot(0,NULL);
- SetVirtualItemSlot(1,NULL);
- SetVirtualItemSlot(2,GetWeaponForAttack(RANGED_ATTACK,true));
- break;
- default:
- SetVirtualItemSlot(0,NULL);
- SetVirtualItemSlot(1,NULL);
- SetVirtualItemSlot(2,NULL);
- break;
- }
- SetByteValue(UNIT_FIELD_BYTES_2, 0, sheathed); // this must visualize Sheath changing for other players...
-}
-
-uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap ) const
-{
- uint8 pClass = getClass();
-
- uint8 slots[4];
- slots[0] = NULL_SLOT;
- slots[1] = NULL_SLOT;
- slots[2] = NULL_SLOT;
- slots[3] = NULL_SLOT;
- switch( proto->InventoryType )
- {
- case INVTYPE_HEAD:
- slots[0] = EQUIPMENT_SLOT_HEAD;
- break;
- case INVTYPE_NECK:
- slots[0] = EQUIPMENT_SLOT_NECK;
- break;
- case INVTYPE_SHOULDERS:
- slots[0] = EQUIPMENT_SLOT_SHOULDERS;
- break;
- case INVTYPE_BODY:
- slots[0] = EQUIPMENT_SLOT_BODY;
- break;
- case INVTYPE_CHEST:
- slots[0] = EQUIPMENT_SLOT_CHEST;
- break;
- case INVTYPE_ROBE:
- slots[0] = EQUIPMENT_SLOT_CHEST;
- break;
- case INVTYPE_WAIST:
- slots[0] = EQUIPMENT_SLOT_WAIST;
- break;
- case INVTYPE_LEGS:
- slots[0] = EQUIPMENT_SLOT_LEGS;
- break;
- case INVTYPE_FEET:
- slots[0] = EQUIPMENT_SLOT_FEET;
- break;
- case INVTYPE_WRISTS:
- slots[0] = EQUIPMENT_SLOT_WRISTS;
- break;
- case INVTYPE_HANDS:
- slots[0] = EQUIPMENT_SLOT_HANDS;
- break;
- case INVTYPE_FINGER:
- slots[0] = EQUIPMENT_SLOT_FINGER1;
- slots[1] = EQUIPMENT_SLOT_FINGER2;
- break;
- case INVTYPE_TRINKET:
- slots[0] = EQUIPMENT_SLOT_TRINKET1;
- slots[1] = EQUIPMENT_SLOT_TRINKET2;
- break;
- case INVTYPE_CLOAK:
- slots[0] = EQUIPMENT_SLOT_BACK;
- break;
- case INVTYPE_WEAPON:
- {
- slots[0] = EQUIPMENT_SLOT_MAINHAND;
-
- // suggest offhand slot only if know dual wielding
- // (this will be replace mainhand weapon at auto equip instead unwonted "you don't known dual wielding" ...
- if(CanDualWield())
- slots[1] = EQUIPMENT_SLOT_OFFHAND;
- };break;
- case INVTYPE_SHIELD:
- slots[0] = EQUIPMENT_SLOT_OFFHAND;
- break;
- case INVTYPE_RANGED:
- slots[0] = EQUIPMENT_SLOT_RANGED;
- break;
- case INVTYPE_2HWEAPON:
- slots[0] = EQUIPMENT_SLOT_MAINHAND;
- break;
- case INVTYPE_TABARD:
- slots[0] = EQUIPMENT_SLOT_TABARD;
- break;
- case INVTYPE_WEAPONMAINHAND:
- slots[0] = EQUIPMENT_SLOT_MAINHAND;
- break;
- case INVTYPE_WEAPONOFFHAND:
- slots[0] = EQUIPMENT_SLOT_OFFHAND;
- break;
- case INVTYPE_HOLDABLE:
- slots[0] = EQUIPMENT_SLOT_OFFHAND;
- break;
- case INVTYPE_THROWN:
- slots[0] = EQUIPMENT_SLOT_RANGED;
- break;
- case INVTYPE_RANGEDRIGHT:
- slots[0] = EQUIPMENT_SLOT_RANGED;
- break;
- case INVTYPE_BAG:
- slots[0] = INVENTORY_SLOT_BAG_1;
- slots[1] = INVENTORY_SLOT_BAG_2;
- slots[2] = INVENTORY_SLOT_BAG_3;
- slots[3] = INVENTORY_SLOT_BAG_4;
- break;
- case INVTYPE_RELIC:
- {
- switch(proto->SubClass)
- {
- case ITEM_SUBCLASS_ARMOR_LIBRAM:
- if (pClass == CLASS_PALADIN)
- slots[0] = EQUIPMENT_SLOT_RANGED;
- break;
- case ITEM_SUBCLASS_ARMOR_IDOL:
- if (pClass == CLASS_DRUID)
- slots[0] = EQUIPMENT_SLOT_RANGED;
- break;
- case ITEM_SUBCLASS_ARMOR_TOTEM:
- if (pClass == CLASS_SHAMAN)
- slots[0] = EQUIPMENT_SLOT_RANGED;
- break;
- case ITEM_SUBCLASS_ARMOR_MISC:
- if (pClass == CLASS_WARLOCK)
- slots[0] = EQUIPMENT_SLOT_RANGED;
- break;
- }
- break;
- }
- default :
- return NULL_SLOT;
- }
-
- if( slot != NULL_SLOT )
- {
- if( swap || !GetItemByPos( INVENTORY_SLOT_BAG_0, slot ) )
- {
- for (int i = 0; i < 4; i++)
- {
- if ( slots[i] == slot )
- return slot;
- }
- }
- }
- else
- {
- // search free slot at first
- for (int i = 0; i < 4; i++)
- {
- if ( slots[i] != NULL_SLOT && !GetItemByPos( INVENTORY_SLOT_BAG_0, slots[i] ) )
- {
- // in case 2hand equipped weapon offhand slot empty but not free
- if(slots[i]==EQUIPMENT_SLOT_OFFHAND)
- {
- Item* mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND );
- if(!mainItem || mainItem->GetProto()->InventoryType != INVTYPE_2HWEAPON)
- return slots[i];
- }
- else
- return slots[i];
- }
- }
-
- // if not found free and can swap return first appropriate from used
- for (int i = 0; i < 4; i++)
- {
- if ( slots[i] != NULL_SLOT && swap )
- return slots[i];
- }
- }
-
- // no free position
- return NULL_SLOT;
-}
-
-uint8 Player::CanUnequipItems( uint32 item, uint32 count ) const
-{
- Item *pItem;
- uint32 tempcount = 0;
-
- uint8 res = EQUIP_ERR_OK;
-
- for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEntry() == item )
- {
- uint8 ires = CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false);
- if(ires==EQUIP_ERR_OK)
- {
- tempcount += pItem->GetCount();
- if( tempcount >= count )
- return EQUIP_ERR_OK;
- }
- else
- res = ires;
- }
- }
- for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEntry() == item )
- {
- tempcount += pItem->GetCount();
- if( tempcount >= count )
- return EQUIP_ERR_OK;
- }
- }
- for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
- {
- pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEntry() == item )
- {
- tempcount += pItem->GetCount();
- if( tempcount >= count )
- return EQUIP_ERR_OK;
- }
- }
- Bag *pBag;
- ItemPrototype const *pBagProto;
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- {
- pBagProto = pBag->GetProto();
- if( pBagProto )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- pItem = GetItemByPos( i, j );
- if( pItem && pItem->GetEntry() == item )
- {
- tempcount += pItem->GetCount();
- if( tempcount >= count )
- return EQUIP_ERR_OK;
- }
- }
- }
- }
- }
-
- // not found req. item count and have unequippable items
- return res;
-}
-
-uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) const
-{
- uint32 count = 0;
- for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem != skipItem && pItem->GetEntry() == item )
- count += pItem->GetCount();
- }
- for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
- {
- Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem != skipItem && pItem->GetEntry() == item )
- count += pItem->GetCount();
- }
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- count += pBag->GetItemCount(item,skipItem);
- }
-
- if(skipItem && skipItem->GetProto()->GemProperties)
- {
- for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem != skipItem && pItem->GetProto()->Socket[0].Color )
- count += pItem->GetGemCountWithID(item);
- }
- }
-
- if(inBankAlso)
- {
- for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
- {
- Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem != skipItem && pItem->GetEntry() == item )
- count += pItem->GetCount();
- }
- for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
- {
- Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- count += pBag->GetItemCount(item,skipItem);
- }
-
- if(skipItem && skipItem->GetProto()->GemProperties)
- {
- for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
- {
- Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem != skipItem && pItem->GetProto()->Socket[0].Color )
- count += pItem->GetGemCountWithID(item);
- }
- }
- }
-
- return count;
-}
-
-Item* Player::GetItemByGuid( uint64 guid ) const
-{
- for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetGUID() == guid )
- return pItem;
- }
- for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
- {
- Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetGUID() == guid )
- return pItem;
- }
-
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- {
- ItemPrototype const *pBagProto = pBag->GetProto();
- if( pBagProto )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- Item* pItem = pBag->GetItemByPos( j );
- if( pItem && pItem->GetGUID() == guid )
- return pItem;
- }
- }
- }
- }
- for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
- {
- Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- {
- ItemPrototype const *pBagProto = pBag->GetProto();
- if( pBagProto )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- Item* pItem = pBag->GetItemByPos( j );
- if( pItem && pItem->GetGUID() == guid )
- return pItem;
- }
- }
- }
- }
-
- return NULL;
-}
-
-Item* Player::GetItemByPos( uint16 pos ) const
-{
- uint8 bag = pos >> 8;
- uint8 slot = pos & 255;
- return GetItemByPos( bag, slot );
-}
-
-Item* Player::GetItemByPos( uint8 bag, uint8 slot ) const
-{
- if( bag == INVENTORY_SLOT_BAG_0 && ( slot < BANK_SLOT_BAG_END || slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END ) )
- return m_items[slot];
- else if(bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END
- || bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END )
- {
- Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
- if ( pBag )
- return pBag->GetItemByPos(slot);
- }
- return NULL;
-}
-
-Item* Player::GetWeaponForAttack(WeaponAttackType attackType, bool useable) const
-{
- uint16 slot;
- switch (attackType)
- {
- case BASE_ATTACK: slot = EQUIPMENT_SLOT_MAINHAND; break;
- case OFF_ATTACK: slot = EQUIPMENT_SLOT_OFFHAND; break;
- case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break;
- default: return NULL;
- }
-
- Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
- if (!item || item->GetProto()->Class != ITEM_CLASS_WEAPON)
- return NULL;
-
- if(!useable)
- return item;
-
- if( item->IsBroken() || !IsUseEquipedWeapon(attackType==BASE_ATTACK) )
- return NULL;
-
- return item;
-}
-
-Item* Player::GetShield(bool useable) const
-{
- Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
- if (!item || item->GetProto()->Class != ITEM_CLASS_ARMOR)
- return NULL;
-
- if(!useable)
- return item;
-
- if( item->IsBroken())
- return NULL;
-
- return item;
-}
-
-uint32 Player::GetAttackBySlot( uint8 slot )
-{
- switch(slot)
- {
- case EQUIPMENT_SLOT_MAINHAND: return BASE_ATTACK;
- case EQUIPMENT_SLOT_OFFHAND: return OFF_ATTACK;
- case EQUIPMENT_SLOT_RANGED: return RANGED_ATTACK;
- default: return MAX_ATTACK;
- }
-}
-
-bool Player::HasBankBagSlot( uint8 slot ) const
-{
- uint32 maxslot = GetByteValue(PLAYER_BYTES_2, 2) + BANK_SLOT_BAG_START;
- if( slot < maxslot )
- return true;
- return false;
-}
-
-bool Player::IsInventoryPos( uint8 bag, uint8 slot )
-{
- if( bag == INVENTORY_SLOT_BAG_0 && slot == NULL_SLOT )
- return true;
- if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END ) )
- return true;
- if( bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END )
- return true;
- if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END ) )
- return true;
- return false;
-}
-
-bool Player::IsEquipmentPos( uint8 bag, uint8 slot )
-{
- if( bag == INVENTORY_SLOT_BAG_0 && ( slot < EQUIPMENT_SLOT_END ) )
- return true;
- if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END ) )
- return true;
- return false;
-}
-
-bool Player::IsBankPos( uint8 bag, uint8 slot )
-{
- if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END ) )
- return true;
- if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) )
- return true;
- if( bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END )
- return true;
- return false;
-}
-
-bool Player::IsBagPos( uint16 pos )
-{
- uint8 bag = pos >> 8;
- uint8 slot = pos & 255;
- if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END ) )
- return true;
- if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) )
- return true;
- return false;
-}
-
-bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const
-{
- uint32 tempcount = 0;
- for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEntry() == item )
- {
- tempcount += pItem->GetCount();
- if( tempcount >= count )
- return true;
- }
- }
- for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
- {
- Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEntry() == item )
- {
- tempcount += pItem->GetCount();
- if( tempcount >= count )
- return true;
- }
- }
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- {
- if(ItemPrototype const *pBagProto = pBag->GetProto())
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- Item* pItem = GetItemByPos( i, j );
- if( pItem && pItem->GetEntry() == item )
- {
- tempcount += pItem->GetCount();
- if( tempcount >= count )
- return true;
- }
- }
- }
- }
- }
-
- if(inBankAlso)
- {
- for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
- {
- Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEntry() == item )
- {
- tempcount += pItem->GetCount();
- if( tempcount >= count )
- return true;
- }
- }
- for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
- {
- if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- {
- if(ItemPrototype const *pBagProto = pBag->GetProto())
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- Item* pItem = GetItemByPos( i, j );
- if( pItem && pItem->GetEntry() == item )
- {
- tempcount += pItem->GetCount();
- if( tempcount >= count )
- return true;
- }
- }
- }
- }
- }
- }
-
- return false;
-}
-
-Item* Player::GetItemOrItemWithGemEquipped( uint32 item ) const
-{
- Item *pItem;
- for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
- {
- pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEntry() == item )
- return pItem;
- }
-
- ItemPrototype const *pProto = objmgr.GetItemPrototype(item);
- if (pProto && pProto->GemProperties)
- {
- for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
- {
- pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetProto()->Socket[0].Color )
- {
- if (pItem->GetGemCountWithID(item) > 0 )
- return pItem;
- }
- }
- }
-
- return NULL;
-}
-
-uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count ) const
-{
- ItemPrototype const *pProto = objmgr.GetItemPrototype(entry);
- if( !pProto )
- {
- if(no_space_count)
- *no_space_count = count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
-
- // no maximum
- if(pProto->MaxCount == 0)
- return EQUIP_ERR_OK;
-
- uint32 curcount = GetItemCount(pProto->ItemId,true,pItem);
-
- if( curcount + count > pProto->MaxCount )
- {
- if(no_space_count)
- *no_space_count = count +curcount - pProto->MaxCount;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
-
- return EQUIP_ERR_OK;
-}
-
-bool Player::HasItemTotemCategory( uint32 TotemCategory ) const
-{
- Item *pItem;
- for(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
- {
- pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
- return true;
- }
- for(uint8 i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
- {
- pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
- return true;
- }
- Bag *pBag;
- ItemPrototype const *pBagProto;
- for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
- {
- pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- {
- pBagProto = pBag->GetProto();
- if( pBagProto )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; ++j)
- {
- pItem = GetItemByPos( i, j );
- if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
- return true;
- }
- }
- }
- }
- return false;
-}
-
-uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool swap, Item* pSrcItem ) const
-{
- Item* pItem2 = GetItemByPos( bag, slot );
-
- // ignore move item (this slot will be empty at move)
- if(pItem2==pSrcItem)
- pItem2 = NULL;
-
- uint32 need_space;
-
- // empty specific slot - check item fit to slot
- if( !pItem2 || swap )
- {
- if( bag == INVENTORY_SLOT_BAG_0 )
- {
- // keyring case
- if(slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_START+GetMaxKeyringSize() && !(pProto->BagFamily & BAG_FAMILY_MASK_KEYS))
- return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
-
- // prevent cheating
- if(slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END || slot >= PLAYER_SLOT_END)
- return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
- }
- else
- {
- Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
- if( !pBag )
- return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
-
- ItemPrototype const* pBagProto = pBag->GetProto();
- if( !pBagProto )
- return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
-
- if( !ItemCanGoIntoBag(pProto,pBagProto) )
- return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
- }
-
- // non empty stack with space
- need_space = pProto->Stackable;
- }
- // non empty slot, check item type
- else
- {
- // check item type
- if(pItem2->GetEntry() != pProto->ItemId)
- return EQUIP_ERR_ITEM_CANT_STACK;
-
- // check free space
- if(pItem2->GetCount() >= pProto->Stackable)
- return EQUIP_ERR_ITEM_CANT_STACK;
-
- need_space = pProto->Stackable - pItem2->GetCount();
- }
-
- if(need_space > count)
- need_space = count;
-
- ItemPosCount newPosition = ItemPosCount((bag << 8) | slot, need_space);
- if(!newPosition.isContainedIn(dest))
- {
- dest.push_back(newPosition);
- count -= need_space;
- }
- return EQUIP_ERR_OK;
-}
-
-uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const
-{
- // skip specific bag already processed in first called _CanStoreItem_InBag
- if(bag==skip_bag)
- return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
-
- Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
- if( !pBag )
- return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
-
- ItemPrototype const* pBagProto = pBag->GetProto();
- if( !pBagProto )
- return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
-
- // specialized bag mode or non-specilized
- if( non_specialized != (pBagProto->Class == ITEM_CLASS_CONTAINER && pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER) )
- return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
-
- if( !ItemCanGoIntoBag(pProto,pBagProto) )
- return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
-
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
- if(j==skip_slot)
- continue;
-
- Item* pItem2 = GetItemByPos( bag, j );
-
- // ignore move item (this slot will be empty at move)
- if(pItem2==pSrcItem)
- pItem2 = NULL;
-
- // if merge skip empty, if !merge skip non-empty
- if((pItem2!=NULL)!=merge)
- continue;
-
- if( pItem2 )
- {
- if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->Stackable )
- {
- uint32 need_space = pProto->Stackable - pItem2->GetCount();
- if(need_space > count)
- need_space = count;
-
- ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space);
- if(!newPosition.isContainedIn(dest))
- {
- dest.push_back(newPosition);
- count -= need_space;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
- }
- else
- {
- uint32 need_space = pProto->Stackable;
- if(need_space > count)
- need_space = count;
-
- ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space);
- if(!newPosition.isContainedIn(dest))
- {
- dest.push_back(newPosition);
- count -= need_space;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
- }
- return EQUIP_ERR_OK;
-}
-
-uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const
-{
- for(uint32 j = slot_begin; j < slot_end; j++)
- {
- // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
- if(INVENTORY_SLOT_BAG_0==skip_bag && j==skip_slot)
- continue;
-
- Item* pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, j );
-
- // ignore move item (this slot will be empty at move)
- if(pItem2==pSrcItem)
- pItem2 = NULL;
-
- // if merge skip empty, if !merge skip non-empty
- if((pItem2!=NULL)!=merge)
- continue;
-
- if( pItem2 )
- {
- if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->Stackable )
- {
- uint32 need_space = pProto->Stackable - pItem2->GetCount();
- if(need_space > count)
- need_space = count;
- ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space);
- if(!newPosition.isContainedIn(dest))
- {
- dest.push_back(newPosition);
- count -= need_space;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
- }
- else
- {
- uint32 need_space = pProto->Stackable;
- if(need_space > count)
- need_space = count;
-
- ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space);
- if(!newPosition.isContainedIn(dest))
- {
- dest.push_back(newPosition);
- count -= need_space;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
- }
- return EQUIP_ERR_OK;
-}
-
-uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 entry, uint32 count, Item *pItem, bool swap, uint32* no_space_count ) const
-{
- sLog.outDebug( "STORAGE: CanStoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, entry, count);
-
- ItemPrototype const *pProto = objmgr.GetItemPrototype(entry);
- if( !pProto )
- {
- if(no_space_count)
- *no_space_count = count;
- return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND;
- }
-
- if(pItem && pItem->IsBindedNotWith(GetGUID()))
- {
- if(no_space_count)
- *no_space_count = count;
- return EQUIP_ERR_DONT_OWN_THAT_ITEM;
- }
-
- // check count of items (skip for auto move for same player from bank)
- uint32 no_similar_count = 0; // can't store this amount similar items
- uint8 res = _CanTakeMoreSimilarItems(entry,count,pItem,&no_similar_count);
- if(res!=EQUIP_ERR_OK)
- {
- if(count==no_similar_count)
- {
- if(no_space_count)
- *no_space_count = no_similar_count;
- return res;
- }
- count -= no_similar_count;
- }
-
- // in specific slot
- if( bag != NULL_BAG && slot != NULL_SLOT )
- {
- res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem);
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
-
- // not specific slot or have spece for partly store only in specific slot
-
- // in specific bag
- if( bag != NULL_BAG )
- {
- // search stack in bag for merge to
- if( pProto->Stackable > 1 )
- {
- if( bag == INVENTORY_SLOT_BAG_0 ) // inventory
- {
- res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_END,dest,pProto,count,true,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
-
- res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
- else // equipped bag
- {
- // we need check 2 time (specilized/non_specialized), use NULL_BAG to prevent skipping bag
- res = _CanStoreItem_InBag(bag,dest,pProto,count,true,false,pItem,NULL_BAG,slot);
- if(res!=EQUIP_ERR_OK)
- res = _CanStoreItem_InBag(bag,dest,pProto,count,true,true,pItem,NULL_BAG,slot);
-
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
- }
-
- // search free slot in bag for place to
- if( bag == INVENTORY_SLOT_BAG_0 ) // inventory
- {
- // search free slot - keyring case
- if(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)
- {
- uint32 keyringSize = GetMaxKeyringSize();
- res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_START+keyringSize,dest,pProto,count,false,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
-
- res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
- else // equipped bag
- {
- res = _CanStoreItem_InBag(bag,dest,pProto,count,false,false,pItem,NULL_BAG,slot);
- if(res!=EQUIP_ERR_OK)
- res = _CanStoreItem_InBag(bag,dest,pProto,count,false,true,pItem,NULL_BAG,slot);
-
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
- }
-
- // not specific bag or have space for partly store only in specific bag
-
- // search stack for merge to
- if( pProto->Stackable > 1 )
- {
- res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_END,dest,pProto,count,true,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
-
- res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
-
- if( pProto->BagFamily )
- {
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- res = _CanStoreItem_InBag(i,dest,pProto,count,true,false,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- continue;
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
- }
-
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- res = _CanStoreItem_InBag(i,dest,pProto,count,true,true,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- continue;
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
- }
-
- // search free slot - special bag case
- if( pProto->BagFamily )
- {
- if(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)
- {
- uint32 keyringSize = GetMaxKeyringSize();
- res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_START+keyringSize,dest,pProto,count,false,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
-
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- res = _CanStoreItem_InBag(i,dest,pProto,count,false,false,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- continue;
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
- }
-
- // search free slot
- res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- {
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return res;
- }
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
-
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- res = _CanStoreItem_InBag(i,dest,pProto,count,false,true,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- continue;
-
- if(count==0)
- {
- if(no_similar_count==0)
- return EQUIP_ERR_OK;
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
- return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
- }
- }
-
- if(no_space_count)
- *no_space_count = count + no_similar_count;
-
- return EQUIP_ERR_INVENTORY_FULL;
-}
-
-//////////////////////////////////////////////////////////////////////////
-uint8 Player::CanStoreItems( Item **pItems,int count) const
-{
- Item *pItem2;
-
- // fill space table
- int inv_slot_items[INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START];
- int inv_bags[INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE];
- int inv_keys[KEYRING_SLOT_END-KEYRING_SLOT_START];
-
- memset(inv_slot_items,0,sizeof(int)*(INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START));
- memset(inv_bags,0,sizeof(int)*(INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START)*MAX_BAG_SIZE);
- memset(inv_keys,0,sizeof(int)*(KEYRING_SLOT_END-KEYRING_SLOT_START));
-
- for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
-
- if (pItem2 && !pItem2->IsInTrade())
- {
- inv_slot_items[i-INVENTORY_SLOT_ITEM_START] = pItem2->GetCount();
- }
- }
-
- for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
- {
- pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
-
- if (pItem2 && !pItem2->IsInTrade())
- {
- inv_keys[i-KEYRING_SLOT_START] = pItem2->GetCount();
- }
- }
-
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- Bag *pBag;
- ItemPrototype const *pBagProto;
-
- pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- {
- pBagProto = pBag->GetProto();
-
- if( pBagProto )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- pItem2 = GetItemByPos( i, j );
- if (pItem2 && !pItem2->IsInTrade())
- {
- inv_bags[i-INVENTORY_SLOT_BAG_START][j] = pItem2->GetCount();
- }
- }
- }
- }
- }
-
- // check free space for all items
- for (int k=0;k<count;k++)
- {
- Item *pItem = pItems[k];
-
- // no item
- if (!pItem) continue;
-
- sLog.outDebug( "STORAGE: CanStoreItems %i. item = %u, count = %u", k+1, pItem->GetEntry(), pItem->GetCount());
- ItemPrototype const *pProto = pItem->GetProto();
-
- // strange item
- if( !pProto )
- return EQUIP_ERR_ITEM_NOT_FOUND;
-
- // item it 'bind'
- if(pItem->IsBindedNotWith(GetGUID()))
- return EQUIP_ERR_DONT_OWN_THAT_ITEM;
-
- Bag *pBag;
- ItemPrototype const *pBagProto;
-
- // item is 'one item only'
- uint8 res = CanTakeMoreSimilarItems(pItem);
- if(res != EQUIP_ERR_OK)
- return res;
-
- // search stack for merge to
- if( pProto->Stackable > 1 )
- {
- bool b_found = false;
-
- for(int t = KEYRING_SLOT_START; t < KEYRING_SLOT_END; t++)
- {
- pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
- if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_keys[t-KEYRING_SLOT_START] + pItem->GetCount() <= pProto->Stackable )
- {
- inv_keys[t-KEYRING_SLOT_START] += pItem->GetCount();
- b_found = true;
- break;
- }
- }
- if (b_found) continue;
-
- for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; t++)
- {
- pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
- if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_slot_items[t-INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->Stackable )
- {
- inv_slot_items[t-INVENTORY_SLOT_ITEM_START] += pItem->GetCount();
- b_found = true;
- break;
- }
- }
- if (b_found) continue;
-
- for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++)
- {
- pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
- if( pBag )
- {
- pBagProto = pBag->GetProto();
- if( pBagProto )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- pItem2 = GetItemByPos( t, j );
- if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_bags[t-INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->Stackable )
- {
- inv_bags[t-INVENTORY_SLOT_BAG_START][j] += pItem->GetCount();
- b_found = true;
- break;
- }
- }
- }
- }
- }
- if (b_found) continue;
- }
-
- // special bag case
- if( pProto->BagFamily )
- {
- bool b_found = false;
- if(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)
- {
- uint32 keyringSize = GetMaxKeyringSize();
- for(uint32 t = KEYRING_SLOT_START; t < KEYRING_SLOT_START+keyringSize; ++t)
- {
- if( inv_keys[t-KEYRING_SLOT_START] == 0 )
- {
- inv_keys[t-KEYRING_SLOT_START] = 1;
- b_found = true;
- break;
- }
- }
- }
-
- if (b_found) continue;
-
- for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++)
- {
- pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
- if( pBag )
- {
- pBagProto = pBag->GetProto();
-
- // not plain container check
- if( pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER) &&
- ItemCanGoIntoBag(pProto,pBagProto) )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 )
- {
- inv_bags[t-INVENTORY_SLOT_BAG_START][j] = 1;
- b_found = true;
- break;
- }
- }
- }
- }
- }
- if (b_found) continue;
- }
-
- // search free slot
- bool b_found = false;
- for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; t++)
- {
- if( inv_slot_items[t-INVENTORY_SLOT_ITEM_START] == 0 )
- {
- inv_slot_items[t-INVENTORY_SLOT_ITEM_START] = 1;
- b_found = true;
- break;
- }
- }
- if (b_found) continue;
-
- // search free slot in bags
- for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++)
- {
- pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
- if( pBag )
- {
- pBagProto = pBag->GetProto();
- if( pBagProto && ItemCanGoIntoBag(pProto,pBagProto))
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 )
- {
- inv_bags[t-INVENTORY_SLOT_BAG_START][j] = 1;
- b_found = true;
- break;
- }
- }
- }
- }
- }
-
- // no free slot found?
- if (!b_found)
- return EQUIP_ERR_INVENTORY_FULL;
- }
-
- return EQUIP_ERR_OK;
-}
-
-//////////////////////////////////////////////////////////////////////////
-uint8 Player::CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, uint32 count, bool swap ) const
-{
- dest = 0;
- Item *pItem = Item::CreateItem( item, count, this );
- if( pItem )
- {
- uint8 result = CanEquipItem(slot, dest, pItem, swap );
- delete pItem;
- return result;
- }
-
- return EQUIP_ERR_ITEM_NOT_FOUND;
-}
-
-uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading ) const
-{
- dest = 0;
- if( pItem )
- {
- sLog.outDebug( "STORAGE: CanEquipItem slot = %u, item = %u, count = %u", slot, pItem->GetEntry(), pItem->GetCount());
- 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;
-
- // check count of items (skip for auto move for same player from bank)
- uint8 res = CanTakeMoreSimilarItems(pItem);
- if(res != EQUIP_ERR_OK)
- return res;
-
- // do not allow equipping gear except weapons, offhands, projectiles, relics in
- // - combat
- // - in-progress arenas
- if( !pProto->CanChangeEquipStateInCombat() )
- {
- if( isInCombat() )
- return EQUIP_ERR_NOT_IN_COMBAT;
-
- if(BattleGround* bg = GetBattleGround())
- if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS )
- return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
- }
-
- 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;
-
- uint8 msg = CanUseItem( pItem , not_loading );
- if( msg != EQUIP_ERR_OK )
- return msg;
- if( !swap && GetItemByPos( INVENTORY_SLOT_BAG_0, eslot ) )
- return EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE;
-
- // check unique-equipped on item
- if (pProto->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED)
- {
- // there is an equip limit on this item
- Item* tItem = GetItemOrItemWithGemEquipped(pProto->ItemId);
- if (tItem && (!swap || tItem->GetSlot() != eslot ) )
- return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
- }
-
- // check unique-equipped on gems
- for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
- {
- uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
- if(!enchant_id)
- continue;
- SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!enchantEntry)
- continue;
-
- ItemPrototype const* pGem = objmgr.GetItemPrototype(enchantEntry->GemID);
- if(pGem && (pGem->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED))
- {
- Item* tItem = GetItemOrItemWithGemEquipped(enchantEntry->GemID);
- if(tItem && (!swap || tItem->GetSlot() != eslot ))
- return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
- }
- }
-
- // check unique-equipped special item classes
- if (pProto->Class == ITEM_CLASS_QUIVER)
- {
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
- {
- if( Item* pBag = GetItemByPos( INVENTORY_SLOT_BAG_0, i ) )
- {
- if( ItemPrototype const* pBagProto = pBag->GetProto() )
- {
- if( pBagProto->Class==pProto->Class && pBagProto->SubClass==pProto->SubClass &&
- (!swap || pBag->GetSlot() != eslot ) )
- {
- if(pBagProto->SubClass == ITEM_SUBCLASS_AMMO_POUCH)
- return EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH;
- else
- return EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER;
- }
- }
- }
- }
- }
-
- uint32 type = pProto->InventoryType;
-
- if(eslot == EQUIPMENT_SLOT_OFFHAND)
- {
- if( type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND )
- {
- if(!CanDualWield())
- return EQUIP_ERR_CANT_DUAL_WIELD;
- }
-
- Item *mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND );
- if(mainItem)
- {
- if(mainItem->GetProto()->InventoryType == INVTYPE_2HWEAPON)
- return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED;
- }
- }
-
- // equip two-hand weapon case (with possible unequip 2 items)
- if( type == INVTYPE_2HWEAPON )
- {
- if(eslot != EQUIPMENT_SLOT_MAINHAND)
- return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
-
- // offhand item must can be stored in inventitory for offhand item and it also must be unequipped
- Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND );
- ItemPosCountVec off_dest;
- if( offItem && (!not_loading ||
- CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0) << 8 | EQUIPMENT_SLOT_OFFHAND,false) != EQUIP_ERR_OK ||
- CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false ) != EQUIP_ERR_OK ) )
- return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_INVENTORY_FULL;
- }
- dest = ((INVENTORY_SLOT_BAG_0 << 8) | eslot);
- return EQUIP_ERR_OK;
- }
- }
- if( !swap )
- return EQUIP_ERR_ITEM_NOT_FOUND;
- else
- return EQUIP_ERR_ITEMS_CANT_BE_SWAPPED;
-}
-
-uint8 Player::CanUnequipItem( uint16 pos, bool swap ) const
-{
- // Applied only to equipped items and bank bags
- if(!IsEquipmentPos(pos) && !IsBagPos(pos))
- return EQUIP_ERR_OK;
-
- Item* pItem = GetItemByPos(pos);
-
- // Applied only to existed equipped item
- if( !pItem )
- return EQUIP_ERR_OK;
-
- sLog.outDebug( "STORAGE: CanUnequipItem slot = %u, item = %u, count = %u", pos, pItem->GetEntry(), pItem->GetCount());
-
- ItemPrototype const *pProto = pItem->GetProto();
- if( !pProto )
- return EQUIP_ERR_ITEM_NOT_FOUND;
-
- // do not allow unequipping gear except weapons, offhands, projectiles, relics in
- // - combat
- // - in-progress arenas
- if( !pProto->CanChangeEquipStateInCombat() )
- {
- if( isInCombat() )
- return EQUIP_ERR_NOT_IN_COMBAT;
-
- if(BattleGround* bg = GetBattleGround())
- if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS )
- return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
- }
-
- if(!swap && pItem->IsBag() && !((Bag*)pItem)->IsEmpty())
- return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS;
-
- return EQUIP_ERR_OK;
-}
-
-uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pItem, bool swap, bool not_loading ) const
-{
- if( !pItem )
- return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
-
- uint32 count = pItem->GetCount();
-
- sLog.outDebug( "STORAGE: CanBankItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), pItem->GetCount());
- ItemPrototype const *pProto = pItem->GetProto();
- if( !pProto )
- return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
-
- if( pItem->IsBindedNotWith(GetGUID()) )
- return EQUIP_ERR_DONT_OWN_THAT_ITEM;
-
- // check count of items (skip for auto move for same player from bank)
- uint8 res = CanTakeMoreSimilarItems(pItem);
- if(res != EQUIP_ERR_OK)
- return res;
-
- // in specific slot
- if( bag != NULL_BAG && slot != NULL_SLOT )
- {
- if( pProto->InventoryType == INVTYPE_BAG )
- {
- Bag *pBag = (Bag*)pItem;
- if( pBag )
- {
- if( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END )
- {
- if( !HasBankBagSlot( slot ) )
- return EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT;
- if( uint8 cantuse = CanUseItem( pItem, not_loading ) != EQUIP_ERR_OK )
- return cantuse;
- }
- else
- {
- if( !pBag->IsEmpty() )
- return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG;
- }
- }
- }
- else
- {
- if( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END )
- return EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT;
- }
-
- res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem);
- if(res!=EQUIP_ERR_OK)
- return res;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
-
- // not specific slot or have spece for partly store only in specific slot
-
- // in specific bag
- if( bag != NULL_BAG )
- {
- if( pProto->InventoryType == INVTYPE_BAG )
- {
- Bag *pBag = (Bag*)pItem;
- if( pBag && !pBag->IsEmpty() )
- return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG;
- }
-
- // search stack in bag for merge to
- if( pProto->Stackable > 1 )
- {
- if( bag == INVENTORY_SLOT_BAG_0 )
- {
- res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- return res;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- else
- {
- res = _CanStoreItem_InBag(bag,dest,pProto,count,true,false,pItem,NULL_BAG,slot);
- if(res!=EQUIP_ERR_OK)
- res = _CanStoreItem_InBag(bag,dest,pProto,count,true,true,pItem,NULL_BAG,slot);
-
- if(res!=EQUIP_ERR_OK)
- return res;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
-
- // search free slot in bag
- if( bag == INVENTORY_SLOT_BAG_0 )
- {
- res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- return res;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- else
- {
- res = _CanStoreItem_InBag(bag,dest,pProto,count,false,false,pItem,NULL_BAG,slot);
- if(res!=EQUIP_ERR_OK)
- res = _CanStoreItem_InBag(bag,dest,pProto,count,false,true,pItem,NULL_BAG,slot);
-
- if(res!=EQUIP_ERR_OK)
- return res;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
-
- // not specific bag or have spece for partly store only in specific bag
-
- // search stack for merge to
- if( pProto->Stackable > 1 )
- {
- // in slots
- res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- return res;
-
- if(count==0)
- return EQUIP_ERR_OK;
-
- // in special bags
- if( pProto->BagFamily )
- {
- for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
- {
- res = _CanStoreItem_InBag(i,dest,pProto,count,true,false,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- continue;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
-
- for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
- {
- res = _CanStoreItem_InBag(i,dest,pProto,count,true,true,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- continue;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
-
- // search free place in special bag
- if( pProto->BagFamily )
- {
- for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
- {
- res = _CanStoreItem_InBag(i,dest,pProto,count,false,false,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- continue;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- }
-
- // search free space
- res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- return res;
-
- if(count==0)
- return EQUIP_ERR_OK;
-
- for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
- {
- res = _CanStoreItem_InBag(i,dest,pProto,count,false,true,pItem,bag,slot);
- if(res!=EQUIP_ERR_OK)
- continue;
-
- if(count==0)
- return EQUIP_ERR_OK;
- }
- return EQUIP_ERR_BANK_FULL;
-}
-
-uint8 Player::CanUseItem( Item *pItem, bool not_loading ) const
-{
- if( pItem )
- {
- sLog.outDebug( "STORAGE: CanUseItem item = %u", pItem->GetEntry());
- if( !isAlive() && not_loading )
- return EQUIP_ERR_YOU_ARE_DEAD;
- //if( isStunned() )
- // return EQUIP_ERR_YOU_ARE_STUNNED;
- ItemPrototype const *pProto = pItem->GetProto();
- if( pProto )
- {
- if( pItem->IsBindedNotWith(GetGUID()) )
- return EQUIP_ERR_DONT_OWN_THAT_ITEM;
- if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
- return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
- if( pItem->GetSkill() != 0 )
- {
- if( GetSkillValue( pItem->GetSkill() ) == 0 )
- return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
- }
- if( pProto->RequiredSkill != 0 )
- {
- if( GetSkillValue( pProto->RequiredSkill ) == 0 )
- return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
- else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
- return EQUIP_ERR_ERR_CANT_EQUIP_SKILL;
- }
- if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
- return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
- if( pProto->RequiredReputationFaction && uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank )
- return EQUIP_ERR_CANT_EQUIP_REPUTATION;
- if( getLevel() < pProto->RequiredLevel )
- return EQUIP_ERR_CANT_EQUIP_LEVEL_I;
- return EQUIP_ERR_OK;
- }
- }
- return EQUIP_ERR_ITEM_NOT_FOUND;
-}
-
-bool Player::CanUseItem( ItemPrototype const *pProto )
-{
- // Used by group, function NeedBeforeGreed, to know if a prototype can be used by a player
-
- if( pProto )
- {
- if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
- return false;
- if( pProto->RequiredSkill != 0 )
- {
- if( GetSkillValue( pProto->RequiredSkill ) == 0 )
- return false;
- else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
- return false;
- }
- if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
- return false;
- if( getLevel() < pProto->RequiredLevel )
- return false;
- return true;
- }
- return false;
-}
-
-uint8 Player::CanUseAmmo( uint32 item ) const
-{
- sLog.outDebug( "STORAGE: CanUseAmmo item = %u", item);
- if( !isAlive() )
- return EQUIP_ERR_YOU_ARE_DEAD;
- //if( isStunned() )
- // return EQUIP_ERR_YOU_ARE_STUNNED;
- ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
- if( pProto )
- {
- if( pProto->InventoryType!= INVTYPE_AMMO )
- return EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE;
- if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
- return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
- if( pProto->RequiredSkill != 0 )
- {
- if( GetSkillValue( pProto->RequiredSkill ) == 0 )
- return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
- else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
- return EQUIP_ERR_ERR_CANT_EQUIP_SKILL;
- }
- if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
- return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
- /*if( GetReputation() < pProto->RequiredReputation )
- return EQUIP_ERR_CANT_EQUIP_REPUTATION;
- */
- if( getLevel() < pProto->RequiredLevel )
- return EQUIP_ERR_CANT_EQUIP_LEVEL_I;
-
- // Requires No Ammo
- if(GetDummyAura(46699))
- return EQUIP_ERR_BAG_FULL6;
-
- return EQUIP_ERR_OK;
- }
- return EQUIP_ERR_ITEM_NOT_FOUND;
-}
-
-void Player::SetAmmo( uint32 item )
-{
- if(!item)
- return;
-
- // already set
- if( GetUInt32Value(PLAYER_AMMO_ID) == item )
- return;
-
- // check ammo
- if(item)
- {
- uint8 msg = CanUseAmmo( item );
- if( msg != EQUIP_ERR_OK )
- {
- SendEquipError( msg, NULL, NULL );
- return;
- }
- }
-
- SetUInt32Value(PLAYER_AMMO_ID, item);
-
- _ApplyAmmoBonuses();
-}
-
-void Player::RemoveAmmo()
-{
- SetUInt32Value(PLAYER_AMMO_ID, 0);
-
- m_ammoDPS = 0.0f;
-
- if(CanModifyStats())
- UpdateDamagePhysical(RANGED_ATTACK);
-}
-
-// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
-Item* Player::StoreNewItem( ItemPosCountVec const& dest, uint32 item, bool update,int32 randomPropertyId )
-{
- uint32 count = 0;
- for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ++itr)
- count += itr->count;
-
- Item *pItem = Item::CreateItem( item, count, this );
- if( pItem )
- {
- ItemAddedQuestCheck( item, count );
- if(randomPropertyId)
- pItem->SetItemRandomProperties(randomPropertyId);
- pItem = StoreItem( dest, pItem, update );
- }
- return pItem;
-}
-
-Item* Player::StoreItem( ItemPosCountVec const& dest, Item* pItem, bool update )
-{
- if( !pItem )
- return NULL;
-
- Item* lastItem = pItem;
-
- for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); )
- {
- uint16 pos = itr->pos;
- uint32 count = itr->count;
-
- ++itr;
-
- if(itr == dest.end())
- {
- lastItem = _StoreItem(pos,pItem,count,false,update);
- break;
- }
-
- lastItem = _StoreItem(pos,pItem,count,true,update);
- }
-
- return lastItem;
-}
-
-// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
-Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, bool update )
-{
- if( !pItem )
- return NULL;
-
- uint8 bag = pos >> 8;
- uint8 slot = pos & 255;
-
- sLog.outDebug( "STORAGE: StoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), count);
-
- Item *pItem2 = GetItemByPos( bag, slot );
-
- if( !pItem2 )
- {
- if(clone)
- pItem = pItem->CloneItem(count,this);
- else
- pItem->SetCount(count);
-
- if(!pItem)
- return NULL;
-
- if( pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP ||
- pItem->GetProto()->Bonding == BIND_QUEST_ITEM ||
- pItem->GetProto()->Bonding == BIND_WHEN_EQUIPED && IsBagPos(pos) )
- pItem->SetBinding( true );
-
- if( bag == INVENTORY_SLOT_BAG_0 )
- {
- m_items[slot] = pItem;
- SetUInt64Value( (uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2) ), pItem->GetGUID() );
- pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, GetGUID() );
- pItem->SetUInt64Value( ITEM_FIELD_OWNER, GetGUID() );
-
- pItem->SetSlot( slot );
- pItem->SetContainer( NULL );
-
- if( IsInWorld() && update )
- {
- pItem->AddToWorld();
- pItem->SendUpdateToPlayer( this );
- }
-
- pItem->SetState(ITEM_CHANGED, this);
- }
- else
- {
- Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
- if( pBag )
- {
- pBag->StoreItem( slot, pItem, update );
- if( IsInWorld() && update )
- {
- pItem->AddToWorld();
- pItem->SendUpdateToPlayer( this );
- }
- pItem->SetState(ITEM_CHANGED, this);
- pBag->SetState(ITEM_CHANGED, this);
- }
- }
-
- AddEnchantmentDurations(pItem);
- AddItemDurations(pItem);
-
- return pItem;
- }
- else
- {
- if( pItem2->GetProto()->Bonding == BIND_WHEN_PICKED_UP ||
- pItem2->GetProto()->Bonding == BIND_QUEST_ITEM ||
- pItem2->GetProto()->Bonding == BIND_WHEN_EQUIPED && IsBagPos(pos) )
- pItem2->SetBinding( true );
-
- pItem2->SetCount( pItem2->GetCount() + count );
- if( IsInWorld() && update )
- pItem2->SendUpdateToPlayer( this );
-
- if(!clone)
- {
- // delete item (it not in any slot currently)
- if( IsInWorld() && update )
- {
- pItem->RemoveFromWorld();
- pItem->DestroyForPlayer( this );
- }
-
- RemoveEnchantmentDurations(pItem);
- RemoveItemDurations(pItem);
-
- pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
- pItem->SetState(ITEM_REMOVED, this);
- }
- // AddItemDurations(pItem2); - pItem2 already have duration listed for player
- AddEnchantmentDurations(pItem2);
-
- pItem2->SetState(ITEM_CHANGED, this);
-
- return pItem2;
- }
-}
-
-Item* Player::EquipNewItem( uint16 pos, uint32 item, uint32 count, bool update )
-{
- Item *pItem = Item::CreateItem( item, count, this );
- if( pItem )
- {
- ItemAddedQuestCheck( item, count );
- Item * retItem = EquipItem( pos, pItem, update );
-
- return retItem;
- }
- return NULL;
-}
-
-Item* Player::EquipItem( uint16 pos, Item *pItem, bool update )
-{
- if( pItem )
- {
- AddEnchantmentDurations(pItem);
- AddItemDurations(pItem);
-
- uint8 bag = pos >> 8;
- uint8 slot = pos & 255;
-
- Item *pItem2 = GetItemByPos( bag, slot );
-
- if( !pItem2 )
- {
- VisualizeItem( slot, pItem);
-
- if(isAlive())
- {
- ItemPrototype const *pProto = pItem->GetProto();
-
- // item set bonuses applied only at equip and removed at unequip, and still active for broken items
- if(pProto && pProto->ItemSet)
- AddItemsSetItem(this,pItem);
-
- _ApplyItemMods(pItem, slot, true);
-
- if(pProto && isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer == 0)
- {
- m_weaponChangeTimer = DEFAULT_SWITCH_WEAPON;
- if (getClass() == CLASS_ROGUE)
- m_weaponChangeTimer = ROGUE_SWITCH_WEAPON;
- }
- }
-
- if( IsInWorld() && update )
- {
- pItem->AddToWorld();
- pItem->SendUpdateToPlayer( this );
- }
-
- ApplyEquipCooldown(pItem);
-
- if( slot == EQUIPMENT_SLOT_MAINHAND )
- UpdateExpertise(BASE_ATTACK);
- else if( slot == EQUIPMENT_SLOT_OFFHAND )
- UpdateExpertise(OFF_ATTACK);
- }
- else
- {
- pItem2->SetCount( pItem2->GetCount() + pItem->GetCount() );
- if( IsInWorld() && update )
- pItem2->SendUpdateToPlayer( this );
-
- // delete item (it not in any slot currently)
- //pItem->DeleteFromDB();
- if( IsInWorld() && update )
- {
- pItem->RemoveFromWorld();
- pItem->DestroyForPlayer( this );
- }
-
- RemoveEnchantmentDurations(pItem);
- RemoveItemDurations(pItem);
-
- pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
- pItem->SetState(ITEM_REMOVED, this);
- pItem2->SetState(ITEM_CHANGED, this);
-
- ApplyEquipCooldown(pItem2);
-
- return pItem2;
- }
- }
-
- return pItem;
-}
-
-void Player::QuickEquipItem( uint16 pos, Item *pItem)
-{
- if( pItem )
- {
- AddEnchantmentDurations(pItem);
- AddItemDurations(pItem);
-
- uint8 slot = pos & 255;
- VisualizeItem( slot, pItem);
-
- if( IsInWorld() )
- {
- pItem->AddToWorld();
- pItem->SendUpdateToPlayer( this );
- }
- }
-}
-
-void Player::SetVisibleItemSlot(uint8 slot, Item *pItem)
-{
- // PLAYER_VISIBLE_ITEM_i_CREATOR // Size: 2
- // PLAYER_VISIBLE_ITEM_i_0 // Size: 12
- // entry // Size: 1
- // inspected enchantments // Size: 6
- // ? // Size: 5
- // PLAYER_VISIBLE_ITEM_i_PROPERTIES // Size: 1 (property,suffix factor)
- // PLAYER_VISIBLE_ITEM_i_PAD // Size: 1
- // // = 16
-
- if(pItem)
- {
- SetUInt64Value(PLAYER_VISIBLE_ITEM_1_CREATOR + (slot * MAX_VISIBLE_ITEM_OFFSET), pItem->GetUInt64Value(ITEM_FIELD_CREATOR));
-
- int VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET);
- SetUInt32Value(VisibleBase + 0, pItem->GetEntry());
-
- for(int i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i)
- SetUInt32Value(VisibleBase + 1 + i, pItem->GetEnchantmentId(EnchantmentSlot(i)));
-
- // Use SetInt16Value to prevent set high part to FFFF for negative value
- SetInt16Value( PLAYER_VISIBLE_ITEM_1_PROPERTIES + (slot * MAX_VISIBLE_ITEM_OFFSET), 0, pItem->GetItemRandomPropertyId());
- SetUInt32Value(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (slot * MAX_VISIBLE_ITEM_OFFSET), pItem->GetItemSuffixFactor());
- }
- else
- {
- SetUInt64Value(PLAYER_VISIBLE_ITEM_1_CREATOR + (slot * MAX_VISIBLE_ITEM_OFFSET), 0);
-
- int VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET);
- SetUInt32Value(VisibleBase + 0, 0);
-
- for(int i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i)
- SetUInt32Value(VisibleBase + 1 + i, 0);
-
- SetUInt32Value(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 0 + (slot * MAX_VISIBLE_ITEM_OFFSET), 0);
- SetUInt32Value(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (slot * MAX_VISIBLE_ITEM_OFFSET), 0);
- }
-}
-
-void Player::VisualizeItem( uint8 slot, Item *pItem)
-{
- if(!pItem)
- return;
-
- // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
- if( pItem->GetProto()->Bonding == BIND_WHEN_EQUIPED || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
- pItem->SetBinding( true );
-
- sLog.outDebug( "STORAGE: EquipItem slot = %u, item = %u", slot, pItem->GetEntry());
-
- m_items[slot] = pItem;
- SetUInt64Value( (uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2) ), pItem->GetGUID() );
- pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, GetGUID() );
- pItem->SetUInt64Value( ITEM_FIELD_OWNER, GetGUID() );
- pItem->SetSlot( slot );
- pItem->SetContainer( NULL );
-
- if( slot < EQUIPMENT_SLOT_END )
- SetVisibleItemSlot(slot,pItem);
-
- pItem->SetState(ITEM_CHANGED, this);
-}
-
-void Player::RemoveItem( uint8 bag, uint8 slot, bool update )
-{
- // note: removeitem does not actually change the item
- // it only takes the item out of storage temporarily
- // note2: if removeitem is to be used for delinking
- // the item must be removed from the player's updatequeue
-
- Item *pItem = GetItemByPos( bag, slot );
- if( pItem )
- {
- sLog.outDebug( "STORAGE: RemoveItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
-
- RemoveEnchantmentDurations(pItem);
- RemoveItemDurations(pItem);
-
- if( bag == INVENTORY_SLOT_BAG_0 )
- {
- if ( slot < INVENTORY_SLOT_BAG_END )
- {
- ItemPrototype const *pProto = pItem->GetProto();
- // item set bonuses applied only at equip and removed at unequip, and still active for broken items
-
- if(pProto && pProto->ItemSet)
- RemoveItemsSetItem(this,pProto);
-
- _ApplyItemMods(pItem, slot, false);
-
- // remove item dependent auras and casts (only weapon and armor slots)
- if(slot < EQUIPMENT_SLOT_END)
- RemoveItemDependentAurasAndCasts(pItem);
-
- // remove held enchantments
- if ( slot == EQUIPMENT_SLOT_MAINHAND )
- {
- if (pItem->GetItemSuffixFactor())
- {
- pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_3);
- pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_4);
- }
- else
- {
- pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0);
- pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1);
- }
- }
- }
-
- m_items[slot] = NULL;
- SetUInt64Value((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot*2)), 0);
-
- if ( slot < EQUIPMENT_SLOT_END )
- SetVisibleItemSlot(slot,NULL);
- }
- else
- {
- Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
- if( pBag )
- pBag->RemoveItem(slot, update);
- }
- pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, 0 );
- // pItem->SetUInt64Value( ITEM_FIELD_OWNER, 0 ); not clear owner at remove (it will be set at store). This used in mail and auction code
- pItem->SetSlot( NULL_SLOT );
- if( IsInWorld() && update )
- pItem->SendUpdateToPlayer( this );
-
- if( slot == EQUIPMENT_SLOT_MAINHAND )
- UpdateExpertise(BASE_ATTACK);
- else if( slot == EQUIPMENT_SLOT_OFFHAND )
- UpdateExpertise(OFF_ATTACK);
- }
-}
-
-// Common operation need to remove item from inventory without delete in trade, auction, guild bank, mail....
-void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
-{
- if(Item* it = GetItemByPos(bag,slot))
- {
- ItemRemovedQuestCheck(it->GetEntry(),it->GetCount());
- RemoveItem( bag,slot,update);
- it->RemoveFromUpdateQueueOf(this);
- if(it->IsInWorld())
- {
- it->RemoveFromWorld();
- it->DestroyForPlayer( this );
- }
- }
-}
-
-// Common operation need to add item from inventory without delete in trade, guild bank, mail....
-void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB)
-{
- // update quest counters
- ItemAddedQuestCheck(pItem->GetEntry(),pItem->GetCount());
-
- // store item
- Item* pLastItem = StoreItem( dest, pItem, update);
-
- // only set if not merged to existed stack (pItem can be deleted already but we can compare pointers any way)
- if(pLastItem==pItem)
- {
- // update owner for last item (this can be original item with wrong owner
- if(pLastItem->GetOwnerGUID() != GetGUID())
- pLastItem->SetOwnerGUID(GetGUID());
-
- // if this original item then it need create record in inventory
- // in case trade we laready have item in other player inventory
- pLastItem->SetState(in_characterInventoryDB ? ITEM_CHANGED : ITEM_NEW, this);
- }
-}
-
-void Player::DestroyItem( uint8 bag, uint8 slot, bool update )
-{
- Item *pItem = GetItemByPos( bag, slot );
- if( pItem )
- {
- sLog.outDebug( "STORAGE: DestroyItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
-
- // start from destroy contained items (only equipped bag can have its)
- if (pItem->IsBag() && pItem->IsEquipped()) // this also prevent infinity loop if empty bag stored in bag==slot
- {
- for (int i = 0; i < MAX_BAG_SIZE; i++)
- DestroyItem(slot,i,update);
- }
-
- if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
- CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
-
- ItemPrototype const *pProto = pItem->GetProto();
-
- RemoveEnchantmentDurations(pItem);
- RemoveItemDurations(pItem);
-
- ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount() );
-
- if( bag == INVENTORY_SLOT_BAG_0 )
- {
-
- SetUInt64Value((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot*2)), 0);
-
- // equipment and equipped bags can have applied bonuses
- if ( slot < INVENTORY_SLOT_BAG_END )
- {
- ItemPrototype const *pProto = pItem->GetProto();
-
- // item set bonuses applied only at equip and removed at unequip, and still active for broken items
- if(pProto && pProto->ItemSet)
- RemoveItemsSetItem(this,pProto);
-
- _ApplyItemMods(pItem, slot, false);
- }
-
- if ( slot < EQUIPMENT_SLOT_END )
- {
- // remove item dependent auras and casts (only weapon and armor slots)
- RemoveItemDependentAurasAndCasts(pItem);
-
- // equipment visual show
- SetVisibleItemSlot(slot,NULL);
- }
-
- m_items[slot] = NULL;
- }
- else if(Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag ))
- pBag->RemoveItem(slot, update);
-
- if( IsInWorld() && update )
- {
- pItem->RemoveFromWorld();
- pItem->DestroyForPlayer(this);
- }
-
- //pItem->SetOwnerGUID(0);
- pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, 0 );
- pItem->SetSlot( NULL_SLOT );
- pItem->SetState(ITEM_REMOVED, this);
- }
-}
-
-void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool unequip_check)
-{
- sLog.outDebug( "STORAGE: DestroyItemCount item = %u, count = %u", item, count);
- Item *pItem;
- ItemPrototype const *pProto;
- uint32 remcount = 0;
-
- // in inventory
- for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEntry() == item )
- {
- if( pItem->GetCount() + remcount <= count )
- {
- // all items in inventory can unequipped
- remcount += pItem->GetCount();
- DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
-
- if(remcount >=count)
- return;
- }
- else
- {
- pProto = pItem->GetProto();
- ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
- pItem->SetCount( pItem->GetCount() - count + remcount );
- if( IsInWorld() & update )
- pItem->SendUpdateToPlayer( this );
- pItem->SetState(ITEM_CHANGED, this);
- return;
- }
- }
- }
- for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
- {
- pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEntry() == item )
- {
- if( pItem->GetCount() + remcount <= count )
- {
- // all keys can be unequipped
- remcount += pItem->GetCount();
- DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
-
- if(remcount >=count)
- return;
- }
- else
- {
- pProto = pItem->GetProto();
- ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
- pItem->SetCount( pItem->GetCount() - count + remcount );
- if( IsInWorld() & update )
- pItem->SendUpdateToPlayer( this );
- pItem->SetState(ITEM_CHANGED, this);
- return;
- }
- }
- }
-
- // in inventory bags
- Bag *pBag;
- ItemPrototype const *pBagProto;
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- {
- pBagProto = pBag->GetProto();
- if( pBagProto )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- pItem = pBag->GetItemByPos(j);
- if( pItem && pItem->GetEntry() == item )
- {
- // all items in bags can be unequipped
- if( pItem->GetCount() + remcount <= count )
- {
- remcount += pItem->GetCount();
- DestroyItem( i, j, update );
-
- if(remcount >=count)
- return;
- }
- else
- {
- pProto = pItem->GetProto();
- ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
- pItem->SetCount( pItem->GetCount() - count + remcount );
- if( IsInWorld() && update )
- pItem->SendUpdateToPlayer( this );
- pItem->SetState(ITEM_CHANGED, this);
- return;
- }
- }
- }
- }
- }
- }
-
- // in equipment and bag list
- for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEntry() == item )
- {
- if( pItem->GetCount() + remcount <= count )
- {
- if(!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i,false) == EQUIP_ERR_OK )
- {
- remcount += pItem->GetCount();
- DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
-
- if(remcount >=count)
- return;
- }
- }
- else
- {
- pProto = pItem->GetProto();
- ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
- pItem->SetCount( pItem->GetCount() - count + remcount );
- if( IsInWorld() & update )
- pItem->SendUpdateToPlayer( this );
- pItem->SetState(ITEM_CHANGED, this);
- return;
- }
- }
- }
-}
-
-void Player::DestroyZoneLimitedItem( bool update, uint32 new_zone )
-{
- sLog.outDebug( "STORAGE: DestroyZoneLimitedItem in map %u and area %u", GetMapId(), new_zone );
-
- // in inventory
- for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
- DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
- }
- for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
- {
- Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
- DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
- }
-
- // in inventory bags
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- {
- ItemPrototype const *pBagProto = pBag->GetProto();
- if( pBagProto )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- Item* pItem = pBag->GetItemByPos(j);
- if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
- DestroyItem( i, j, update);
- }
- }
- }
- }
-
- // in equipment and bag list
- for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
- DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
- }
-}
-
-void Player::DestroyConjuredItems( bool update )
-{
- // used when entering arena
- // distroys all conjured items
- sLog.outDebug( "STORAGE: DestroyConjuredItems" );
-
- // in inventory
- for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetProto() &&
- (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) &&
- (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) )
- DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
- }
-
- // in inventory bags
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- {
- ItemPrototype const *pBagProto = pBag->GetProto();
- if( pBagProto )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- Item* pItem = pBag->GetItemByPos(j);
- if( pItem && pItem->GetProto() &&
- (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) &&
- (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) )
- DestroyItem( i, j, update);
- }
- }
- }
- }
-
- // in equipment and bag list
- for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetProto() &&
- (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) &&
- (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) )
- DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
- }
-}
-
-void Player::DestroyItemCount( Item* pItem, uint32 &count, bool update )
-{
- if(!pItem)
- return;
-
- sLog.outDebug( "STORAGE: DestroyItemCount item (GUID: %u, Entry: %u) count = %u", pItem->GetGUIDLow(),pItem->GetEntry(), count);
-
- if( pItem->GetCount() <= count )
- {
- count-= pItem->GetCount();
-
- DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), update);
- }
- else
- {
- ItemRemovedQuestCheck( pItem->GetEntry(), count);
- pItem->SetCount( pItem->GetCount() - count );
- count = 0;
- if( IsInWorld() & update )
- pItem->SendUpdateToPlayer( this );
- pItem->SetState(ITEM_CHANGED, this);
- }
-}
-
-void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
-{
- uint8 srcbag = src >> 8;
- uint8 srcslot = src & 255;
-
- uint8 dstbag = dst >> 8;
- uint8 dstslot = dst & 255;
-
- Item *pSrcItem = GetItemByPos( srcbag, srcslot );
- if( !pSrcItem )
- {
- SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL );
- return;
- }
-
- // not let split all items (can be only at cheating)
- if(pSrcItem->GetCount() == count)
- {
- SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL );
- return;
- }
-
- // not let split more existed items (can be only at cheating)
- if(pSrcItem->GetCount() < count)
- {
- SendEquipError( EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT, pSrcItem, NULL );
- return;
- }
-
- if(pSrcItem->m_lootGenerated) // prevent split looting item (item
- {
- //best error message found for attempting to split while looting
- SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL );
- return;
- }
-
- sLog.outDebug( "STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count);
- Item *pNewItem = pSrcItem->CloneItem( count, this );
- if( !pNewItem )
- {
- SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL );
- return;
- }
-
- if( IsInventoryPos( dst ) )
- {
- // change item amount before check (for unique max count check)
- pSrcItem->SetCount( pSrcItem->GetCount() - count );
-
- ItemPosCountVec dest;
- uint8 msg = CanStoreItem( dstbag, dstslot, dest, pNewItem, false );
- if( msg != EQUIP_ERR_OK )
- {
- delete pNewItem;
- pSrcItem->SetCount( pSrcItem->GetCount() + count );
- SendEquipError( msg, pSrcItem, NULL );
- return;
- }
-
- if( IsInWorld() )
- pSrcItem->SendUpdateToPlayer( this );
- pSrcItem->SetState(ITEM_CHANGED, this);
- StoreItem( dest, pNewItem, true);
- }
- else if( IsBankPos ( dst ) )
- {
- // change item amount before check (for unique max count check)
- pSrcItem->SetCount( pSrcItem->GetCount() - count );
-
- ItemPosCountVec dest;
- uint8 msg = CanBankItem( dstbag, dstslot, dest, pNewItem, false );
- if( msg != EQUIP_ERR_OK )
- {
- delete pNewItem;
- pSrcItem->SetCount( pSrcItem->GetCount() + count );
- SendEquipError( msg, pSrcItem, NULL );
- return;
- }
-
- if( IsInWorld() )
- pSrcItem->SendUpdateToPlayer( this );
- pSrcItem->SetState(ITEM_CHANGED, this);
- BankItem( dest, pNewItem, true);
- }
- else if( IsEquipmentPos ( dst ) )
- {
- // change item amount before check (for unique max count check), provide space for splitted items
- pSrcItem->SetCount( pSrcItem->GetCount() - count );
-
- uint16 dest;
- uint8 msg = CanEquipItem( dstslot, dest, pNewItem, false );
- if( msg != EQUIP_ERR_OK )
- {
- delete pNewItem;
- pSrcItem->SetCount( pSrcItem->GetCount() + count );
- SendEquipError( msg, pSrcItem, NULL );
- return;
- }
-
- if( IsInWorld() )
- pSrcItem->SendUpdateToPlayer( this );
- pSrcItem->SetState(ITEM_CHANGED, this);
- EquipItem( dest, pNewItem, true);
- AutoUnequipOffhandIfNeed();
- }
-}
-
-void Player::SwapItem( uint16 src, uint16 dst )
-{
- uint8 srcbag = src >> 8;
- uint8 srcslot = src & 255;
-
- uint8 dstbag = dst >> 8;
- uint8 dstslot = dst & 255;
-
- Item *pSrcItem = GetItemByPos( srcbag, srcslot );
- Item *pDstItem = GetItemByPos( dstbag, dstslot );
-
- if( !pSrcItem )
- return;
-
- sLog.outDebug( "STORAGE: SwapItem bag = %u, slot = %u, item = %u", dstbag, dstslot, pSrcItem->GetEntry());
-
- if(!isAlive() )
- {
- SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, pSrcItem, pDstItem );
- return;
- }
-
- if(pSrcItem->m_lootGenerated) // prevent swap looting item
- {
- //best error message found for attempting to swap while looting
- SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL );
- return;
- }
-
- // check unequip potability for equipped items and bank bags
- if(IsEquipmentPos ( src ) || IsBagPos ( src ))
- {
- // bags can be swapped with empty bag slots
- uint8 msg = CanUnequipItem( src, !IsBagPos ( src ) || IsBagPos ( dst ));
- if(msg != EQUIP_ERR_OK)
- {
- SendEquipError( msg, pSrcItem, pDstItem );
- return;
- }
- }
-
- // prevent put equipped/bank bag in self
- if( IsBagPos ( src ) && srcslot == dstbag)
- {
- SendEquipError( EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem );
- return;
- }
-
- if( !pDstItem )
- {
- if( IsInventoryPos( dst ) )
- {
- ItemPosCountVec dest;
- uint8 msg = CanStoreItem( dstbag, dstslot, dest, pSrcItem, false );
- if( msg != EQUIP_ERR_OK )
- {
- SendEquipError( msg, pSrcItem, NULL );
- return;
- }
-
- RemoveItem(srcbag, srcslot, true);
- StoreItem( dest, pSrcItem, true);
- }
- else if( IsBankPos ( dst ) )
- {
- ItemPosCountVec dest;
- uint8 msg = CanBankItem( dstbag, dstslot, dest, pSrcItem, false);
- if( msg != EQUIP_ERR_OK )
- {
- SendEquipError( msg, pSrcItem, NULL );
- return;
- }
-
- RemoveItem(srcbag, srcslot, true);
- BankItem( dest, pSrcItem, true);
- }
- else if( IsEquipmentPos ( dst ) )
- {
- uint16 dest;
- uint8 msg = CanEquipItem( dstslot, dest, pSrcItem, false );
- if( msg != EQUIP_ERR_OK )
- {
- SendEquipError( msg, pSrcItem, NULL );
- return;
- }
-
- RemoveItem(srcbag, srcslot, true);
- EquipItem( dest, pSrcItem, true);
- AutoUnequipOffhandIfNeed();
- }
- }
- else // if (!pDstItem)
- {
- if(pDstItem->m_lootGenerated) // prevent swap looting item
- {
- //best error message found for attempting to swap while looting
- SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pDstItem, NULL );
- return;
- }
-
- // check unequip potability for equipped items and bank bags
- if(IsEquipmentPos ( dst ) || IsBagPos ( dst ))
- {
- // bags can be swapped with empty bag slots
- uint8 msg = CanUnequipItem( dst, !IsBagPos ( dst ) || IsBagPos ( src ) );
- if(msg != EQUIP_ERR_OK)
- {
- SendEquipError( msg, pSrcItem, pDstItem );
- return;
- }
- }
-
- // attempt merge to / fill target item
- {
- uint8 msg;
- ItemPosCountVec sDest;
- uint16 eDest;
- if( IsInventoryPos( dst ) )
- msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, false );
- else if( IsBankPos ( dst ) )
- msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, false );
- else if( IsEquipmentPos ( dst ) )
- msg = CanEquipItem( dstslot, eDest, pSrcItem, false );
- else
- return;
-
- // can be merge/fill
- if(msg == EQUIP_ERR_OK)
- {
- if( pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetProto()->Stackable )
- {
- RemoveItem(srcbag, srcslot, true);
-
- if( IsInventoryPos( dst ) )
- StoreItem( sDest, pSrcItem, true);
- else if( IsBankPos ( dst ) )
- BankItem( sDest, pSrcItem, true);
- else if( IsEquipmentPos ( dst ) )
- {
- EquipItem( eDest, pSrcItem, true);
- AutoUnequipOffhandIfNeed();
- }
- }
- else
- {
- pSrcItem->SetCount( pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetProto()->Stackable );
- pDstItem->SetCount( pSrcItem->GetProto()->Stackable );
- pSrcItem->SetState(ITEM_CHANGED, this);
- pDstItem->SetState(ITEM_CHANGED, this);
- if( IsInWorld() )
- {
- pSrcItem->SendUpdateToPlayer( this );
- pDstItem->SendUpdateToPlayer( this );
- }
- }
- return;
- }
- }
-
- // impossible merge/fill, do real swap
- uint8 msg;
-
- // check src->dest move possibility
- ItemPosCountVec sDest;
- uint16 eDest;
- if( IsInventoryPos( dst ) )
- msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, true );
- else if( IsBankPos( dst ) )
- msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, true );
- else if( IsEquipmentPos( dst ) )
- {
- msg = CanEquipItem( dstslot, eDest, pSrcItem, true );
- if( msg == EQUIP_ERR_OK )
- msg = CanUnequipItem( eDest, true );
- }
-
- if( msg != EQUIP_ERR_OK )
- {
- SendEquipError( msg, pSrcItem, pDstItem );
- return;
- }
-
- // check dest->src move possibility
- ItemPosCountVec sDest2;
- uint16 eDest2;
- if( IsInventoryPos( src ) )
- msg = CanStoreItem( srcbag, srcslot, sDest2, pDstItem, true );
- else if( IsBankPos( src ) )
- msg = CanBankItem( srcbag, srcslot, sDest2, pDstItem, true );
- else if( IsEquipmentPos( src ) )
- {
- msg = CanEquipItem( srcslot, eDest2, pDstItem, true);
- if( msg == EQUIP_ERR_OK )
- msg = CanUnequipItem( eDest2, true);
- }
-
- if( msg != EQUIP_ERR_OK )
- {
- SendEquipError( msg, pDstItem, pSrcItem );
- return;
- }
-
- // now do moves, remove...
- RemoveItem(dstbag, dstslot, false);
- RemoveItem(srcbag, srcslot, false);
-
- // add to dest
- if( IsInventoryPos( dst ) )
- StoreItem(sDest, pSrcItem, true);
- else if( IsBankPos( dst ) )
- BankItem(sDest, pSrcItem, true);
- else if( IsEquipmentPos( dst ) )
- EquipItem(eDest, pSrcItem, true);
-
- // add to src
- if( IsInventoryPos( src ) )
- StoreItem(sDest2, pDstItem, true);
- else if( IsBankPos( src ) )
- BankItem(sDest2, pDstItem, true);
- else if( IsEquipmentPos( src ) )
- EquipItem(eDest2, pDstItem, true);
-
- AutoUnequipOffhandIfNeed();
- }
-}
-
-void Player::AddItemToBuyBackSlot( Item *pItem )
-{
- if( pItem )
- {
- uint32 slot = m_currentBuybackSlot;
- // if current back slot non-empty search oldest or free
- if(m_items[slot])
- {
- uint32 oldest_time = GetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 );
- uint32 oldest_slot = BUYBACK_SLOT_START;
-
- for(uint32 i = BUYBACK_SLOT_START+1; i < BUYBACK_SLOT_END; ++i )
- {
- // found empty
- if(!m_items[i])
- {
- slot = i;
- break;
- }
-
- uint32 i_time = GetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + i - BUYBACK_SLOT_START);
-
- if(oldest_time > i_time)
- {
- oldest_time = i_time;
- oldest_slot = i;
- }
- }
-
- // find oldest
- slot = oldest_slot;
- }
-
- RemoveItemFromBuyBackSlot( slot, true );
- sLog.outDebug( "STORAGE: AddItemToBuyBackSlot item = %u, slot = %u", pItem->GetEntry(), slot);
-
- m_items[slot] = pItem;
- time_t base = time(NULL);
- uint32 etime = uint32(base - m_logintime + (30 * 3600));
- uint32 eslot = slot - BUYBACK_SLOT_START;
-
- SetUInt64Value( PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + eslot * 2, pItem->GetGUID() );
- ItemPrototype const *pProto = pItem->GetProto();
- if( pProto )
- SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, pProto->SellPrice * pItem->GetCount() );
- else
- SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0 );
- SetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, (uint32)etime );
-
- // move to next (for non filled list is move most optimized choice)
- if(m_currentBuybackSlot < BUYBACK_SLOT_END-1)
- ++m_currentBuybackSlot;
- }
-}
-
-Item* Player::GetItemFromBuyBackSlot( uint32 slot )
-{
- sLog.outDebug( "STORAGE: GetItemFromBuyBackSlot slot = %u", slot);
- if( slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END )
- return m_items[slot];
- return NULL;
-}
-
-void Player::RemoveItemFromBuyBackSlot( uint32 slot, bool del )
-{
- sLog.outDebug( "STORAGE: RemoveItemFromBuyBackSlot slot = %u", slot);
- if( slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END )
- {
- Item *pItem = m_items[slot];
- if( pItem )
- {
- pItem->RemoveFromWorld();
- if(del) pItem->SetState(ITEM_REMOVED, this);
- }
-
- m_items[slot] = NULL;
-
- uint32 eslot = slot - BUYBACK_SLOT_START;
- SetUInt64Value( PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + eslot * 2, 0 );
- SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0 );
- SetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0 );
-
- // if current backslot is filled set to now free slot
- if(m_items[m_currentBuybackSlot])
- m_currentBuybackSlot = slot;
- }
-}
-
-void Player::SendEquipError( uint8 msg, Item* pItem, Item *pItem2 )
-{
- sLog.outDebug( "WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)",msg);
- WorldPacket data( SMSG_INVENTORY_CHANGE_FAILURE, (msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I ? 22 : 18) );
- data << uint8(msg);
-
- if(msg)
- {
- data << uint64(pItem ? pItem->GetGUID() : 0);
- data << uint64(pItem2 ? pItem2->GetGUID() : 0);
- data << uint8(0); // not 0 there...
-
- if(msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I)
- {
- uint32 level = 0;
-
- if(pItem)
- if(ItemPrototype const* proto = pItem->GetProto())
- level = proto->RequiredLevel;
-
- data << uint32(level); // new 2.4.0
- }
- }
- GetSession()->SendPacket(&data);
-}
-
-void Player::SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 param )
-{
- sLog.outDebug( "WORLD: Sent SMSG_BUY_FAILED" );
- WorldPacket data( SMSG_BUY_FAILED, (8+4+4+1) );
- data << uint64(pCreature ? pCreature->GetGUID() : 0);
- data << uint32(item);
- if( param > 0 )
- data << uint32(param);
- data << uint8(msg);
- GetSession()->SendPacket(&data);
-}
-
-void Player::SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param )
-{
- sLog.outDebug( "WORLD: Sent SMSG_SELL_ITEM" );
- WorldPacket data( SMSG_SELL_ITEM,(8+8+(param?4:0)+1)); // last check 2.0.10
- data << uint64(pCreature ? pCreature->GetGUID() : 0);
- data << uint64(guid);
- if( param > 0 )
- data << uint32(param);
- data << uint8(msg);
- GetSession()->SendPacket(&data);
-}
-
-void Player::ClearTrade()
-{
- tradeGold = 0;
- acceptTrade = false;
- for(int i = 0; i < TRADE_SLOT_COUNT; i++)
- tradeItems[i] = NULL_SLOT;
-}
-
-void Player::TradeCancel(bool sendback)
-{
- if(pTrader)
- {
- // send yellow "Trade cancelled" message to both traders
- WorldSession* ws;
- ws = GetSession();
- if(sendback)
- ws->SendCancelTrade();
- ws = pTrader->GetSession();
- if(!ws->PlayerLogout())
- ws->SendCancelTrade();
-
- // cleanup
- ClearTrade();
- pTrader->ClearTrade();
- // prevent loss of reference
- pTrader->pTrader = NULL;
- pTrader = NULL;
- }
-}
-
-void Player::UpdateItemDuration(uint32 time, bool realtimeonly)
-{
- if(m_itemDuration.empty())
- return;
-
- sLog.outDebug("Player::UpdateItemDuration(%u,%u)", time,realtimeonly);
-
- for(ItemDurationList::iterator itr = m_itemDuration.begin();itr != m_itemDuration.end(); )
- {
- Item* item = *itr;
- ++itr; // current element can be erased in UpdateDuration
-
- if (realtimeonly && item->GetProto()->Duration < 0 || !realtimeonly)
- item->UpdateDuration(this,time);
- }
-}
-
-void Player::UpdateEnchantTime(uint32 time)
-{
- for(EnchantDurationList::iterator itr = m_enchantDuration.begin(),next;itr != m_enchantDuration.end();itr=next)
- {
- assert(itr->item);
- next=itr;
- if(!itr->item->GetEnchantmentId(itr->slot))
- {
- next = m_enchantDuration.erase(itr);
- }
- else if(itr->leftduration <= time)
- {
- ApplyEnchantment(itr->item,itr->slot,false,false);
- itr->item->ClearEnchantment(itr->slot);
- next = m_enchantDuration.erase(itr);
- }
- else if(itr->leftduration > time)
- {
- itr->leftduration -= time;
- ++next;
- }
- }
-}
-
-void Player::AddEnchantmentDurations(Item *item)
-{
- for(int x=0;x<MAX_ENCHANTMENT_SLOT;++x)
- {
- if(!item->GetEnchantmentId(EnchantmentSlot(x)))
- continue;
-
- uint32 duration = item->GetEnchantmentDuration(EnchantmentSlot(x));
- if( duration > 0 )
- AddEnchantmentDuration(item,EnchantmentSlot(x),duration);
- }
-}
-
-void Player::RemoveEnchantmentDurations(Item *item)
-{
- for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();)
- {
- if(itr->item == item)
- {
- // save duration in item
- item->SetEnchantmentDuration(EnchantmentSlot(itr->slot),itr->leftduration);
- itr = m_enchantDuration.erase(itr);
- }
- else
- ++itr;
- }
-}
-
-
-void Player::RemoveAllEnchantments(EnchantmentSlot slot)
-{
- // remove enchantments from equipped items first to clean up the m_enchantDuration list
- for(EnchantDurationList::iterator itr = m_enchantDuration.begin(),next;itr != m_enchantDuration.end();itr=next)
- {
- next = itr;
- if(itr->slot==slot)
- {
- if(itr->item && itr->item->GetEnchantmentId(slot))
- {
- // remove from stats
- ApplyEnchantment(itr->item,slot,false,false);
- // remove visual
- itr->item->ClearEnchantment(slot);
- }
- // remove from update list
- next = m_enchantDuration.erase(itr);
- }
- else
- ++next;
- }
-
- // remove enchants from inventory items
- // NOTE: no need to remove these from stats, since these aren't equipped
- // in inventory
- for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
- {
- Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pItem && pItem->GetEnchantmentId(slot) )
- pItem->ClearEnchantment(slot);
- }
-
- // in inventory bags
- for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
- {
- Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if( pBag )
- {
- ItemPrototype const *pBagProto = pBag->GetProto();
- if( pBagProto )
- {
- for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
- {
- Item* pItem = pBag->GetItemByPos(j);
- if( pItem && pItem->GetEnchantmentId(slot) )
- pItem->ClearEnchantment(slot);
- }
- }
- }
- }
-}
-
-// duration == 0 will remove item enchant
-void Player::AddEnchantmentDuration(Item *item,EnchantmentSlot slot,uint32 duration)
-{
- if(!item)
- return;
-
- if(slot >= MAX_ENCHANTMENT_SLOT)
- return;
-
- for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();++itr)
- {
- if(itr->item == item && itr->slot == slot)
- {
- itr->item->SetEnchantmentDuration(itr->slot,itr->leftduration);
- m_enchantDuration.erase(itr);
- break;
- }
- }
- if(item && duration > 0 )
- {
- GetSession()->SendItemEnchantTimeUpdate(GetGUID(), item->GetGUID(),slot,uint32(duration/1000));
- m_enchantDuration.push_back(EnchantDuration(item,slot,duration));
- }
-}
-
-void Player::ApplyEnchantment(Item *item,bool apply)
-{
- for(uint32 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot)
- ApplyEnchantment(item, EnchantmentSlot(slot), apply);
-}
-
-void Player::ApplyEnchantment(Item *item,EnchantmentSlot slot,bool apply, bool apply_dur, bool ignore_condition)
-{
- if(!item)
- return;
-
- if(!item->IsEquipped())
- return;
-
- if(slot >= MAX_ENCHANTMENT_SLOT)
- return;
-
- uint32 enchant_id = item->GetEnchantmentId(slot);
- if(!enchant_id)
- return;
-
- SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!pEnchant)
- return;
-
- if(!ignore_condition && pEnchant->EnchantmentCondition && !((Player*)this)->EnchantmentFitsRequirements(pEnchant->EnchantmentCondition, -1))
- return;
-
- for (int s=0; s<3; s++)
- {
- uint32 enchant_display_type = pEnchant->type[s];
- uint32 enchant_amount = pEnchant->amount[s];
- uint32 enchant_spell_id = pEnchant->spellid[s];
-
- switch(enchant_display_type)
- {
- case ITEM_ENCHANTMENT_TYPE_NONE:
- break;
- case ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL:
- // processed in Player::CastItemCombatSpell
- break;
- case ITEM_ENCHANTMENT_TYPE_DAMAGE:
- if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
- HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(enchant_amount), apply);
- else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND)
- HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(enchant_amount), apply);
- else if (item->GetSlot() == EQUIPMENT_SLOT_RANGED)
- HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
- break;
- case ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL:
- if(enchant_spell_id)
- {
- if(apply)
- {
- int32 basepoints = int32(enchant_amount);
- // Random Property Exist - try found basepoints for spell (basepoints depencs from item suffix factor)
- if (item->GetItemRandomPropertyId() !=0 && !enchant_amount)
- {
- ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
- if (item_rand)
- {
- // Search enchant_amount
- for (int k=0; k<3; k++)
- {
- if(item_rand->enchant_id[k] == enchant_id)
- {
- basepoints = int32((item_rand->prefix[k]*item->GetItemSuffixFactor()) / 10000 );
- break;
- }
- }
- }
- }
- // Cast custom spell vs all equal basepoints getted from enchant_amount
- if (basepoints)
- CastCustomSpell(this,enchant_spell_id,&basepoints,&basepoints,&basepoints,true,item);
- else
- CastSpell(this,enchant_spell_id,true,item);
- }
- else
- RemoveAurasDueToItemSpell(item,enchant_spell_id);
- }
- break;
- case ITEM_ENCHANTMENT_TYPE_RESISTANCE:
- if (!enchant_amount)
- {
- ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
- if(item_rand)
- {
- for (int k=0; k<3; k++)
- {
- if(item_rand->enchant_id[k] == enchant_id)
- {
- enchant_amount = uint32((item_rand->prefix[k]*item->GetItemSuffixFactor()) / 10000 );
- break;
- }
- }
- }
- }
-
- HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply);
- break;
- case ITEM_ENCHANTMENT_TYPE_STAT:
- {
- if (!enchant_amount)
- {
- ItemRandomSuffixEntry const *item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
- if(item_rand_suffix)
- {
- for (int k=0; k<3; k++)
- {
- if(item_rand_suffix->enchant_id[k] == enchant_id)
- {
- enchant_amount = uint32((item_rand_suffix->prefix[k]*item->GetItemSuffixFactor()) / 10000 );
- break;
- }
- }
- }
- }
-
- sLog.outDebug("Adding %u to stat nb %u",enchant_amount,enchant_spell_id);
- switch (enchant_spell_id)
- {
- case ITEM_MOD_AGILITY:
- sLog.outDebug("+ %u AGILITY",enchant_amount);
- HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply);
- ApplyStatBuffMod(STAT_AGILITY, enchant_amount, apply);
- break;
- case ITEM_MOD_STRENGTH:
- sLog.outDebug("+ %u STRENGTH",enchant_amount);
- HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply);
- ApplyStatBuffMod(STAT_STRENGTH, enchant_amount, apply);
- break;
- case ITEM_MOD_INTELLECT:
- sLog.outDebug("+ %u INTELLECT",enchant_amount);
- HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply);
- ApplyStatBuffMod(STAT_INTELLECT, enchant_amount, apply);
- break;
- case ITEM_MOD_SPIRIT:
- sLog.outDebug("+ %u SPIRIT",enchant_amount);
- HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply);
- ApplyStatBuffMod(STAT_SPIRIT, enchant_amount, apply);
- break;
- case ITEM_MOD_STAMINA:
- sLog.outDebug("+ %u STAMINA",enchant_amount);
- HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply);
- ApplyStatBuffMod(STAT_STAMINA, enchant_amount, apply);
- break;
- case ITEM_MOD_DEFENSE_SKILL_RATING:
- ((Player*)this)->ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply);
- sLog.outDebug("+ %u DEFENCE", enchant_amount);
- break;
- case ITEM_MOD_DODGE_RATING:
- ((Player*)this)->ApplyRatingMod(CR_DODGE, enchant_amount, apply);
- sLog.outDebug("+ %u DODGE", enchant_amount);
- break;
- case ITEM_MOD_PARRY_RATING:
- ((Player*)this)->ApplyRatingMod(CR_PARRY, enchant_amount, apply);
- sLog.outDebug("+ %u PARRY", enchant_amount);
- break;
- case ITEM_MOD_BLOCK_RATING:
- ((Player*)this)->ApplyRatingMod(CR_BLOCK, enchant_amount, apply);
- sLog.outDebug("+ %u SHIELD_BLOCK", enchant_amount);
- break;
- case ITEM_MOD_HIT_MELEE_RATING:
- ((Player*)this)->ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
- sLog.outDebug("+ %u MELEE_HIT", enchant_amount);
- break;
- case ITEM_MOD_HIT_RANGED_RATING:
- ((Player*)this)->ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
- sLog.outDebug("+ %u RANGED_HIT", enchant_amount);
- break;
- case ITEM_MOD_HIT_SPELL_RATING:
- ((Player*)this)->ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
- sLog.outDebug("+ %u SPELL_HIT", enchant_amount);
- break;
- case ITEM_MOD_CRIT_MELEE_RATING:
- ((Player*)this)->ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
- sLog.outDebug("+ %u MELEE_CRIT", enchant_amount);
- break;
- case ITEM_MOD_CRIT_RANGED_RATING:
- ((Player*)this)->ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
- sLog.outDebug("+ %u RANGED_CRIT", enchant_amount);
- break;
- case ITEM_MOD_CRIT_SPELL_RATING:
- ((Player*)this)->ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
- sLog.outDebug("+ %u SPELL_CRIT", enchant_amount);
- break;
-// Values from ITEM_STAT_MELEE_HA_RATING to ITEM_MOD_HASTE_RANGED_RATING are never used
-// in Enchantments
-// case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
-// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
-// break;
-// case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
-// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
-// break;
-// case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
-// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
-// break;
-// case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
-// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
-// break;
-// case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
-// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
-// break;
-// case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
-// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
-// break;
-// case ITEM_MOD_HASTE_MELEE_RATING:
-// ((Player*)this)->ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
-// break;
-// case ITEM_MOD_HASTE_RANGED_RATING:
-// ((Player*)this)->ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
-// break;
- case ITEM_MOD_HASTE_SPELL_RATING:
- ((Player*)this)->ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
- break;
- case ITEM_MOD_HIT_RATING:
- ((Player*)this)->ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
- ((Player*)this)->ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
- ((Player*)this)->ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
- sLog.outDebug("+ %u HIT", enchant_amount);
- break;
- case ITEM_MOD_CRIT_RATING:
- ((Player*)this)->ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
- ((Player*)this)->ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
- ((Player*)this)->ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
- sLog.outDebug("+ %u CRITICAL", enchant_amount);
- break;
-// Values ITEM_MOD_HIT_TAKEN_RATING and ITEM_MOD_CRIT_TAKEN_RATING are never used in Enchantment
-// case ITEM_MOD_HIT_TAKEN_RATING:
-// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
-// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
-// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
-// break;
-// case ITEM_MOD_CRIT_TAKEN_RATING:
-// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
-// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
-// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
-// break;
- case ITEM_MOD_RESILIENCE_RATING:
- ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
- ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
- ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
- sLog.outDebug("+ %u RESILIENCE", enchant_amount);
- break;
- case ITEM_MOD_HASTE_RATING:
- ((Player*)this)->ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
- ((Player*)this)->ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
- ((Player*)this)->ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
- sLog.outDebug("+ %u HASTE", enchant_amount);
- break;
- case ITEM_MOD_EXPERTISE_RATING:
- ((Player*)this)->ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply);
- sLog.outDebug("+ %u EXPERTISE", enchant_amount);
- break;
- default:
- break;
- }
- break;
- }
- case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon
- {
- if(getClass() == CLASS_SHAMAN)
- {
- float addValue = 0.0f;
- if(item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
- {
- addValue = float(enchant_amount * item->GetProto()->Delay/1000.0f);
- HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, addValue, apply);
- }
- else if(item->GetSlot() == EQUIPMENT_SLOT_OFFHAND )
- {
- addValue = float(enchant_amount * item->GetProto()->Delay/1000.0f);
- HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, addValue, apply);
- }
- }
- break;
- }
- default:
- sLog.outError("Unknown item enchantment display type: %d",enchant_display_type);
- break;
- } /*switch(enchant_display_type)*/
- } /*for*/
-
- // visualize enchantment at player and equipped items
- if(slot < MAX_INSPECTED_ENCHANTMENT_SLOT)
- {
- int VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + (item->GetSlot() * MAX_VISIBLE_ITEM_OFFSET);
- SetUInt32Value(VisibleBase + 1 + slot, apply? item->GetEnchantmentId(slot) : 0);
- }
-
- if(apply_dur)
- {
- if(apply)
- {
- // set duration
- uint32 duration = item->GetEnchantmentDuration(slot);
- if(duration > 0)
- AddEnchantmentDuration(item,slot,duration);
- }
- else
- {
- // duration == 0 will remove EnchantDuration
- AddEnchantmentDuration(item,slot,0);
- }
- }
-}
-
-void Player::SendEnchantmentDurations()
-{
- for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();++itr)
- {
- GetSession()->SendItemEnchantTimeUpdate(GetGUID(), itr->item->GetGUID(),itr->slot,uint32(itr->leftduration)/1000);
- }
-}
-
-void Player::SendItemDurations()
-{
- for(ItemDurationList::iterator itr = m_itemDuration.begin();itr != m_itemDuration.end();++itr)
- {
- (*itr)->SendTimeUpdate(this);
- }
-}
-
-void Player::SendNewItem(Item *item, uint32 count, bool received, bool created, bool broadcast)
-{
- if(!item) // prevent crash
- return;
-
- // last check 2.0.10
- WorldPacket data( SMSG_ITEM_PUSH_RESULT, (8+4+4+4+1+4+4+4+4+4) );
- data << GetGUID(); // player GUID
- data << uint32(received); // 0=looted, 1=from npc
- data << uint32(created); // 0=received, 1=created
- data << uint32(1); // always 0x01 (probably meant to be count of listed items)
- data << (uint8)item->GetBagSlot(); // bagslot
- // item slot, but when added to stack: 0xFFFFFFFF
- data << (uint32) ((item->GetCount()==count) ? item->GetSlot() : -1);
- data << uint32(item->GetEntry()); // item id
- data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
- data << uint32(item->GetItemRandomPropertyId()); // random item property id
- data << uint32(count); // count of items
- data << GetItemCount(item->GetEntry()); // count of items in inventory
-
- if (broadcast && GetGroup())
- GetGroup()->BroadcastPacket(&data);
- else
- GetSession()->SendPacket(&data);
-}
-
-/*********************************************************/
-/*** QUEST SYSTEM ***/
-/*********************************************************/
-
-void Player::PrepareQuestMenu( uint64 guid )
-{
- Object *pObject;
- QuestRelations* pObjectQR;
- QuestRelations* pObjectQIR;
- Creature *pCreature = ObjectAccessor::GetCreature(*this, guid);
- if( pCreature )
- {
- pObject = (Object*)pCreature;
- pObjectQR = &objmgr.mCreatureQuestRelations;
- pObjectQIR = &objmgr.mCreatureQuestInvolvedRelations;
- }
- else
- {
- GameObject *pGameObject = ObjectAccessor::GetGameObject(*this, guid);
- if( pGameObject )
- {
- pObject = (Object*)pGameObject;
- pObjectQR = &objmgr.mGOQuestRelations;
- pObjectQIR = &objmgr.mGOQuestInvolvedRelations;
- }
- else
- return;
- }
-
- QuestMenu &qm = PlayerTalkClass->GetQuestMenu();
- qm.ClearMenu();
-
- for(QuestRelations::const_iterator i = pObjectQIR->lower_bound(pObject->GetEntry()); i != pObjectQIR->upper_bound(pObject->GetEntry()); ++i)
- {
- uint32 quest_id = i->second;
- QuestStatus status = GetQuestStatus( quest_id );
- if ( status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus( quest_id ) )
- qm.AddMenuItem(quest_id, DIALOG_STATUS_REWARD_REP);
- else if ( status == QUEST_STATUS_INCOMPLETE )
- qm.AddMenuItem(quest_id, DIALOG_STATUS_INCOMPLETE);
- else if (status == QUEST_STATUS_AVAILABLE )
- qm.AddMenuItem(quest_id, DIALOG_STATUS_CHAT);
- }
-
- for(QuestRelations::const_iterator i = pObjectQR->lower_bound(pObject->GetEntry()); i != pObjectQR->upper_bound(pObject->GetEntry()); ++i)
- {
- uint32 quest_id = i->second;
- Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
- if(!pQuest) continue;
-
- QuestStatus status = GetQuestStatus( quest_id );
-
- if (pQuest->IsAutoComplete() && CanTakeQuest(pQuest, false))
- qm.AddMenuItem(quest_id, DIALOG_STATUS_REWARD_REP);
- else if ( status == QUEST_STATUS_NONE && CanTakeQuest( pQuest, false ) )
- qm.AddMenuItem(quest_id, DIALOG_STATUS_AVAILABLE);
- }
-}
-
-void Player::SendPreparedQuest( uint64 guid )
-{
- QuestMenu& questMenu = PlayerTalkClass->GetQuestMenu();
- if( questMenu.Empty() )
- return;
-
- QuestMenuItem const& qmi0 = questMenu.GetItem( 0 );
-
- uint32 status = qmi0.m_qIcon;
-
- // single element case
- if ( questMenu.MenuItemCount() == 1 )
- {
- // Auto open -- maybe also should verify there is no greeting
- uint32 quest_id = qmi0.m_qId;
- Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
- if ( pQuest )
- {
- if( status == DIALOG_STATUS_REWARD_REP && !GetQuestRewardStatus( quest_id ) )
- PlayerTalkClass->SendQuestGiverRequestItems( pQuest, guid, CanRewardQuest(pQuest,false), true );
- else if( status == DIALOG_STATUS_INCOMPLETE )
- PlayerTalkClass->SendQuestGiverRequestItems( pQuest, guid, false, true );
- // Send completable on repeatable quest if player don't have quest
- else if( pQuest->IsRepeatable() )
- PlayerTalkClass->SendQuestGiverRequestItems( pQuest, guid, CanCompleteRepeatableQuest(pQuest), true );
- else
- PlayerTalkClass->SendQuestGiverQuestDetails( pQuest, guid, true );
- }
- }
- // multiply entries
- else
- {
- QEmote qe;
- qe._Delay = 0;
- qe._Emote = 0;
- std::string title = "";
- Creature *pCreature = ObjectAccessor::GetCreature(*this, guid);
- if( pCreature )
- {
- uint32 textid = pCreature->GetNpcTextId();
- GossipText * gossiptext = objmgr.GetGossipText(textid);
- if( !gossiptext )
- {
- qe._Delay = 0; //TEXTEMOTE_MESSAGE; //zyg: player emote
- qe._Emote = 0; //TEXTEMOTE_HELLO; //zyg: NPC emote
- title = "";
- }
- else
- {
- qe = gossiptext->Options[0].Emotes[0];
-
- if(!gossiptext->Options[0].Text_0.empty())
- {
- title = gossiptext->Options[0].Text_0;
-
- int loc_idx = GetSession()->GetSessionDbLocaleIndex();
- if (loc_idx >= 0)
- {
- NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textid);
- if (nl)
- {
- if (nl->Text_0[0].size() > loc_idx && !nl->Text_0[0][loc_idx].empty())
- title = nl->Text_0[0][loc_idx];
- }
- }
- }
- else
- {
- title = gossiptext->Options[0].Text_1;
-
- int loc_idx = GetSession()->GetSessionDbLocaleIndex();
- if (loc_idx >= 0)
- {
- NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textid);
- if (nl)
- {
- if (nl->Text_1[0].size() > loc_idx && !nl->Text_1[0][loc_idx].empty())
- title = nl->Text_1[0][loc_idx];
- }
- }
- }
- }
- }
- PlayerTalkClass->SendQuestGiverQuestList( qe, title, guid );
- }
-}
-
-bool Player::IsActiveQuest( uint32 quest_id ) const
-{
- QuestStatusMap::const_iterator itr = mQuestStatus.find(quest_id);
-
- return itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE;
-}
-
-Quest const * Player::GetNextQuest( uint64 guid, Quest const *pQuest )
-{
- Object *pObject;
- QuestRelations* pObjectQR;
- QuestRelations* pObjectQIR;
-
- Creature *pCreature = ObjectAccessor::GetCreature(*this, guid);
- if( pCreature )
- {
- pObject = (Object*)pCreature;
- pObjectQR = &objmgr.mCreatureQuestRelations;
- pObjectQIR = &objmgr.mCreatureQuestInvolvedRelations;
- }
- else
- {
- GameObject *pGameObject = ObjectAccessor::GetGameObject(*this, guid);
- if( pGameObject )
- {
- pObject = (Object*)pGameObject;
- pObjectQR = &objmgr.mGOQuestRelations;
- pObjectQIR = &objmgr.mGOQuestInvolvedRelations;
- }
- else
- return NULL;
- }
-
- uint32 nextQuestID = pQuest->GetNextQuestInChain();
- for(QuestRelations::const_iterator itr = pObjectQR->lower_bound(pObject->GetEntry()); itr != pObjectQR->upper_bound(pObject->GetEntry()); ++itr)
- {
- if (itr->second == nextQuestID)
- return objmgr.GetQuestTemplate(nextQuestID);
- }
-
- return NULL;
-}
-
-bool Player::CanSeeStartQuest( Quest const *pQuest )
-{
- if( SatisfyQuestRace( pQuest, false ) && SatisfyQuestSkillOrClass( pQuest, false ) &&
- SatisfyQuestExclusiveGroup( pQuest, false ) && SatisfyQuestReputation( pQuest, false ) &&
- SatisfyQuestPreviousQuest( pQuest, false ) && SatisfyQuestNextChain( pQuest, false ) &&
- SatisfyQuestPrevChain( pQuest, false ) && SatisfyQuestDay( pQuest, false ) )
- {
- return getLevel() + sWorld.getConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF) >= pQuest->GetMinLevel();
- }
-
- return false;
-}
-
-bool Player::CanTakeQuest( Quest const *pQuest, bool msg )
-{
- return SatisfyQuestStatus( pQuest, msg ) && SatisfyQuestExclusiveGroup( pQuest, msg )
- && SatisfyQuestRace( pQuest, msg ) && SatisfyQuestLevel( pQuest, msg )
- && SatisfyQuestSkillOrClass( pQuest, msg ) && SatisfyQuestReputation( pQuest, msg )
- && SatisfyQuestPreviousQuest( pQuest, msg ) && SatisfyQuestTimed( pQuest, msg )
- && SatisfyQuestNextChain( pQuest, msg ) && SatisfyQuestPrevChain( pQuest, msg )
- && SatisfyQuestDay( pQuest, msg );
-}
-
-bool Player::CanAddQuest( Quest const *pQuest, bool msg )
-{
- if( !SatisfyQuestLog( msg ) )
- return false;
-
- uint32 srcitem = pQuest->GetSrcItemId();
- if( srcitem > 0 )
- {
- uint32 count = pQuest->GetSrcItemCount();
- ItemPosCountVec dest;
- uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, srcitem, count );
-
- // player already have max number (in most case 1) source item, no additional item needed and quest can be added.
- if( msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
- return true;
- else if( msg != EQUIP_ERR_OK )
- {
- SendEquipError( msg, NULL, NULL );
- return false;
- }
- }
- return true;
-}
-
-bool Player::CanCompleteQuest( uint32 quest_id )
-{
- if( quest_id )
- {
- QuestStatusData& q_status = mQuestStatus[quest_id];
- if( q_status.m_status == QUEST_STATUS_COMPLETE )
- return false; // not allow re-complete quest
-
- Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
-
- if(!qInfo)
- return false;
-
- // auto complete quest
- if (qInfo->IsAutoComplete() && CanTakeQuest(qInfo, false))
- return true;
-
- if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
- {
-
- if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
- {
- for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
- {
- if( qInfo->ReqItemCount[i]!= 0 && q_status.m_itemcount[i] < qInfo->ReqItemCount[i] )
- return false;
- }
- }
-
- if ( qInfo->HasFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO) )
- {
- for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
- {
- if( qInfo->ReqCreatureOrGOId[i] == 0 )
- continue;
-
- if( qInfo->ReqCreatureOrGOCount[i] != 0 && q_status.m_creatureOrGOcount[i] < qInfo->ReqCreatureOrGOCount[i] )
- return false;
- }
- }
-
- if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT ) && !q_status.m_explored )
- return false;
-
- if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) && q_status.m_timer == 0 )
- return false;
-
- if ( qInfo->GetRewOrReqMoney() < 0 )
- {
- if ( GetMoney() < uint32(-qInfo->GetRewOrReqMoney()) )
- return false;
- }
-
- uint32 repFacId = qInfo->GetRepObjectiveFaction();
- if ( repFacId && GetReputation(repFacId) < qInfo->GetRepObjectiveValue() )
- return false;
-
- return true;
- }
- }
- return false;
-}
-
-bool Player::CanCompleteRepeatableQuest( Quest const *pQuest )
-{
- // Solve problem that player don't have the quest and try complete it.
- // if repeatable she must be able to complete event if player don't have it.
- // Seem that all repeatable quest are DELIVER Flag so, no need to add more.
- if( !CanTakeQuest(pQuest, false) )
- return false;
-
- if (pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER) )
- for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
- if( pQuest->ReqItemId[i] && pQuest->ReqItemCount[i] && !HasItemCount(pQuest->ReqItemId[i],pQuest->ReqItemCount[i]) )
- return false;
-
- if( !CanRewardQuest(pQuest, false) )
- return false;
-
- return true;
-}
-
-bool Player::CanRewardQuest( Quest const *pQuest, bool msg )
-{
- // not auto complete quest and not completed quest (only cheating case, then ignore without message)
- if(!pQuest->IsAutoComplete() && GetQuestStatus(pQuest->GetQuestId()) != QUEST_STATUS_COMPLETE)
- return false;
-
- // daily quest can't be rewarded (10 daily quest already completed)
- if(!SatisfyQuestDay(pQuest,true))
- return false;
-
- // rewarded and not repeatable quest (only cheating case, then ignore without message)
- if(GetQuestRewardStatus(pQuest->GetQuestId()))
- return false;
-
- // prevent receive reward with quest items in bank
- if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
- {
- for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
- {
- if( pQuest->ReqItemCount[i]!= 0 &&
- GetItemCount(pQuest->ReqItemId[i]) < pQuest->ReqItemCount[i] )
- {
- if(msg)
- SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
- return false;
- }
- }
- }
-
- // prevent receive reward with low money and GetRewOrReqMoney() < 0
- if(pQuest->GetRewOrReqMoney() < 0 && GetMoney() < uint32(-pQuest->GetRewOrReqMoney()) )
- return false;
-
- return true;
-}
-
-bool Player::CanRewardQuest( Quest const *pQuest, uint32 reward, bool msg )
-{
- // prevent receive reward with quest items in bank or for not completed quest
- if(!CanRewardQuest(pQuest,msg))
- return false;
-
- if ( pQuest->GetRewChoiceItemsCount() > 0 )
- {
- if( pQuest->RewChoiceItemId[reward] )
- {
- ItemPosCountVec dest;
- uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] );
- if( res != EQUIP_ERR_OK )
- {
- SendEquipError( res, NULL, NULL );
- return false;
- }
- }
- }
-
- if ( pQuest->GetRewItemsCount() > 0 )
- {
- for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i)
- {
- if( pQuest->RewItemId[i] )
- {
- ItemPosCountVec dest;
- uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] );
- if( res != EQUIP_ERR_OK )
- {
- SendEquipError( res, NULL, NULL );
- return false;
- }
- }
- }
- }
-
- return true;
-}
-
-void Player::AddQuest( Quest const *pQuest, Object *questGiver )
-{
- uint16 log_slot = FindQuestSlot( 0 );
- assert(log_slot < MAX_QUEST_LOG_SIZE);
-
- uint32 quest_id = pQuest->GetQuestId();
-
- // if not exist then created with set uState==NEW and rewarded=false
- QuestStatusData& questStatusData = mQuestStatus[quest_id];
- if (questStatusData.uState != QUEST_NEW)
- questStatusData.uState = QUEST_CHANGED;
-
- // check for repeatable quests status reset
- questStatusData.m_status = QUEST_STATUS_INCOMPLETE;
- questStatusData.m_explored = false;
-
- if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
- {
- for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
- questStatusData.m_itemcount[i] = 0;
- }
-
- if ( pQuest->HasFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO) )
- {
- for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
- questStatusData.m_creatureOrGOcount[i] = 0;
- }
-
- GiveQuestSourceItem( pQuest );
- AdjustQuestReqItemCount( pQuest );
-
- if( pQuest->GetRepObjectiveFaction() )
- SetFactionVisibleForFactionId(pQuest->GetRepObjectiveFaction());
-
- uint32 qtime = 0;
- if( pQuest->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) )
- {
- uint32 limittime = pQuest->GetLimitTime();
-
- // shared timed quest
- if(questGiver && questGiver->GetTypeId()==TYPEID_PLAYER)
- limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / 1000;
-
- AddTimedQuest( quest_id );
- questStatusData.m_timer = limittime * 1000;
- qtime = static_cast<uint32>(time(NULL)) + limittime;
- }
- else
- questStatusData.m_timer = 0;
-
- SetQuestSlot(log_slot, quest_id, qtime);
-
- //starting initial quest script
- if(questGiver && pQuest->GetQuestStartScript()!=0)
- sWorld.ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this);
-
- UpdateForQuestsGO();
-}
-
-void Player::CompleteQuest( uint32 quest_id )
-{
- if( quest_id )
- {
- SetQuestStatus( quest_id, QUEST_STATUS_COMPLETE );
-
- uint16 log_slot = FindQuestSlot( quest_id );
- if( log_slot < MAX_QUEST_LOG_SIZE)
- SetQuestSlotState(log_slot,QUEST_STATE_COMPLETE);
-
- if(Quest const* qInfo = objmgr.GetQuestTemplate(quest_id))
- {
- if( qInfo->HasFlag(QUEST_FLAGS_AUTO_REWARDED) )
- RewardQuest(qInfo,0,this,false);
- else
- SendQuestComplete( quest_id );
- }
- }
-}
-
-void Player::IncompleteQuest( uint32 quest_id )
-{
- if( quest_id )
- {
- SetQuestStatus( quest_id, QUEST_STATUS_INCOMPLETE );
-
- uint16 log_slot = FindQuestSlot( quest_id );
- if( log_slot < MAX_QUEST_LOG_SIZE)
- RemoveQuestSlotState(log_slot,QUEST_STATE_COMPLETE);
- }
-}
-
-void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce )
-{
- uint32 quest_id = pQuest->GetQuestId();
-
- for (int i = 0; i < QUEST_OBJECTIVES_COUNT; i++ )
- {
- if ( pQuest->ReqItemId[i] )
- DestroyItemCount( pQuest->ReqItemId[i], pQuest->ReqItemCount[i], true);
- }
-
- //if( qInfo->HasSpecialFlag( QUEST_FLAGS_TIMED ) )
- // SetTimedQuest( 0 );
- m_timedquests.erase(pQuest->GetQuestId());
-
- if ( pQuest->GetRewChoiceItemsCount() > 0 )
- {
- if( pQuest->RewChoiceItemId[reward] )
- {
- ItemPosCountVec dest;
- if( CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] ) == EQUIP_ERR_OK )
- {
- Item* item = StoreNewItem( dest, pQuest->RewChoiceItemId[reward], true);
- SendNewItem(item, pQuest->RewChoiceItemCount[reward], true, false);
- }
- }
- }
-
- if ( pQuest->GetRewItemsCount() > 0 )
- {
- for (uint32 i=0; i < pQuest->GetRewItemsCount(); ++i)
- {
- if( pQuest->RewItemId[i] )
- {
- ItemPosCountVec dest;
- if( CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] ) == EQUIP_ERR_OK )
- {
- Item* item = StoreNewItem( dest, pQuest->RewItemId[i], true);
- SendNewItem(item, pQuest->RewItemCount[i], true, false);
- }
- }
- }
- }
-
- RewardReputation( pQuest );
-
- if( pQuest->GetRewSpellCast() > 0 )
- CastSpell( this, pQuest->GetRewSpellCast(), true);
- else if( pQuest->GetRewSpell() > 0)
- CastSpell( this, pQuest->GetRewSpell(), true);
-
- uint16 log_slot = FindQuestSlot( quest_id );
- if( log_slot < MAX_QUEST_LOG_SIZE)
- SetQuestSlot(log_slot,0);
-
- QuestStatusData& q_status = mQuestStatus[quest_id];
-
- // Not give XP in case already completed once repeatable quest
- uint32 XP = q_status.m_rewarded ? 0 : uint32(pQuest->XPValue( this )*sWorld.getRate(RATE_XP_QUEST));
-
- if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
- GiveXP( XP , NULL );
- else
- ModifyMoney( int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)) );
-
- // Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
- ModifyMoney( pQuest->GetRewOrReqMoney() );
-
- // title reward
- if(pQuest->GetCharTitleId())
- {
- if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
- SetFlag64(PLAYER__FIELD_KNOWN_TITLES, (uint64(1) << titleEntry->bit_index));
- }
-
- // Send reward mail
- if(pQuest->GetRewMailTemplateId())
- {
- MailMessageType mailType;
- uint32 senderGuidOrEntry;
- switch(questGiver->GetTypeId())
- {
- case TYPEID_UNIT:
- mailType = MAIL_CREATURE;
- senderGuidOrEntry = questGiver->GetEntry();
- break;
- case TYPEID_GAMEOBJECT:
- mailType = MAIL_GAMEOBJECT;
- senderGuidOrEntry = questGiver->GetEntry();
- break;
- case TYPEID_ITEM:
- mailType = MAIL_ITEM;
- senderGuidOrEntry = questGiver->GetEntry();
- break;
- case TYPEID_PLAYER:
- mailType = MAIL_NORMAL;
- senderGuidOrEntry = questGiver->GetGUIDLow();
- break;
- default:
- mailType = MAIL_NORMAL;
- senderGuidOrEntry = GetGUIDLow();
- break;
- }
-
- Loot questMailLoot;
-
- questMailLoot.FillLoot(pQuest->GetQuestId(), LootTemplates_QuestMail, this);
-
- // fill mail
- MailItemsInfo mi; // item list preparing
-
- for(size_t i = 0; mi.size() < MAX_MAIL_ITEMS && i < questMailLoot.items.size(); ++i)
- {
- if(LootItem* lootitem = questMailLoot.LootItemInSlot(i,this))
- {
- if(Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,this))
- {
- item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
- mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
- }
- }
- }
-
- for(size_t i = 0; mi.size() < MAX_MAIL_ITEMS && i < questMailLoot.quest_items.size(); ++i)
- {
- if(LootItem* lootitem = questMailLoot.LootItemInSlot(i+questMailLoot.items.size(),this))
- {
- if(Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,this))
- {
- 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(this, mailType, MAIL_STATIONERY_NORMAL, senderGuidOrEntry, GetGUIDLow(), "", 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE,pQuest->GetRewMailDelaySecs(),pQuest->GetRewMailTemplateId());
- }
-
- if(pQuest->IsDaily())
- SetDailyQuestStatus(quest_id);
-
- if ( !pQuest->IsRepeatable() )
- SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE);
- else
- SetQuestStatus(quest_id, QUEST_STATUS_NONE);
-
- q_status.m_rewarded = true;
-
- if(announce)
- SendQuestReward( pQuest, XP, questGiver );
-
- if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
-}
-
-void Player::FailQuest( uint32 quest_id )
-{
- if( quest_id )
- {
- IncompleteQuest( quest_id );
-
- uint16 log_slot = FindQuestSlot( quest_id );
- if( log_slot < MAX_QUEST_LOG_SIZE)
- {
- SetQuestSlotTimer(log_slot, 1 );
- SetQuestSlotState(log_slot,QUEST_STATE_FAIL);
- }
- SendQuestFailed( quest_id );
- }
-}
-
-void Player::FailTimedQuest( uint32 quest_id )
-{
- if( quest_id )
- {
- QuestStatusData& q_status = mQuestStatus[quest_id];
-
- if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
- q_status.m_timer = 0;
-
- IncompleteQuest( quest_id );
-
- uint16 log_slot = FindQuestSlot( quest_id );
- if( log_slot < MAX_QUEST_LOG_SIZE)
- {
- SetQuestSlotTimer(log_slot, 1 );
- SetQuestSlotState(log_slot,QUEST_STATE_FAIL);
- }
- SendQuestTimerFailed( quest_id );
- }
-}
-
-bool Player::SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg )
-{
- int32 zoneOrSort = qInfo->GetZoneOrSort();
- int32 skillOrClass = qInfo->GetSkillOrClass();
-
- // skip zone zoneOrSort and 0 case skillOrClass
- if( zoneOrSort >= 0 && skillOrClass == 0 )
- return true;
-
- int32 questSort = -zoneOrSort;
- uint8 reqSortClass = ClassByQuestSort(questSort);
-
- // check class sort cases in zoneOrSort
- if( reqSortClass != 0 && getClass() != reqSortClass)
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
-
- // check class
- if( skillOrClass < 0 )
- {
- uint8 reqClass = -int32(skillOrClass);
- if(getClass() != reqClass)
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
- }
- // check skill
- else if( skillOrClass > 0 )
- {
- uint32 reqSkill = skillOrClass;
- if( GetSkillValue( reqSkill ) < qInfo->GetRequiredSkillValue() )
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
- }
-
- return true;
-}
-
-bool Player::SatisfyQuestLevel( Quest const* qInfo, bool msg )
-{
- if( getLevel() < qInfo->GetMinLevel() )
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
- return true;
-}
-
-bool Player::SatisfyQuestLog( bool msg )
-{
- // exist free slot
- if( FindQuestSlot(0) < MAX_QUEST_LOG_SIZE )
- return true;
-
- if( msg )
- {
- WorldPacket data( SMSG_QUESTLOG_FULL, 0 );
- GetSession()->SendPacket( &data );
- sLog.outDebug( "WORLD: Sent QUEST_LOG_FULL_MESSAGE" );
- }
- return false;
-}
-
-bool Player::SatisfyQuestPreviousQuest( Quest const* qInfo, bool msg )
-{
- // No previous quest (might be first quest in a series)
- if( qInfo->prevQuests.empty())
- return true;
-
- for(Quest::PrevQuests::const_iterator iter = qInfo->prevQuests.begin(); iter != qInfo->prevQuests.end(); ++iter )
- {
- uint32 prevId = abs(*iter);
-
- QuestStatusMap::iterator i_prevstatus = mQuestStatus.find( prevId );
- Quest const* qPrevInfo = objmgr.GetQuestTemplate(prevId);
-
- if( qPrevInfo && i_prevstatus != mQuestStatus.end() )
- {
- // If any of the positive previous quests completed, return true
- if( *iter > 0 && i_prevstatus->second.m_rewarded )
- {
- // skip one-from-all exclusive group
- if(qPrevInfo->GetExclusiveGroup() >= 0)
- return true;
-
- // each-from-all exclusive group ( < 0)
- // can be start if only all quests in prev quest exclusive group complited and rewarded
- ObjectMgr::ExclusiveQuestGroups::iterator iter = objmgr.mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
- ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
-
- assert(iter!=end); // always must be found if qPrevInfo->ExclusiveGroup != 0
-
- for(; iter != end; ++iter)
- {
- uint32 exclude_Id = iter->second;
-
- // skip checked quest id, only state of other quests in group is interesting
- if(exclude_Id == prevId)
- continue;
-
- QuestStatusMap::iterator i_exstatus = mQuestStatus.find( exclude_Id );
-
- // alternative quest from group also must be completed and rewarded(reported)
- if( i_exstatus == mQuestStatus.end() || !i_exstatus->second.m_rewarded )
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
- }
- return true;
- }
- // If any of the negative previous quests active, return true
- if( *iter < 0 && (i_prevstatus->second.m_status == QUEST_STATUS_INCOMPLETE
- || (i_prevstatus->second.m_status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(prevId))))
- {
- // skip one-from-all exclusive group
- if(qPrevInfo->GetExclusiveGroup() >= 0)
- return true;
-
- // each-from-all exclusive group ( < 0)
- // can be start if only all quests in prev quest exclusive group active
- ObjectMgr::ExclusiveQuestGroups::iterator iter = objmgr.mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
- ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
-
- assert(iter!=end); // always must be found if qPrevInfo->ExclusiveGroup != 0
-
- for(; iter != end; ++iter)
- {
- uint32 exclude_Id = iter->second;
-
- // skip checked quest id, only state of other quests in group is interesting
- if(exclude_Id == prevId)
- continue;
-
- QuestStatusMap::iterator i_exstatus = mQuestStatus.find( exclude_Id );
-
- // alternative quest from group also must be active
- if( i_exstatus == mQuestStatus.end() ||
- i_exstatus->second.m_status != QUEST_STATUS_INCOMPLETE &&
- (i_prevstatus->second.m_status != QUEST_STATUS_COMPLETE || GetQuestRewardStatus(prevId)) )
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
- }
- return true;
- }
- }
- }
-
- // Has only positive prev. quests in non-rewarded state
- // and negative prev. quests in non-active state
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
-
- return false;
-}
-
-bool Player::SatisfyQuestRace( Quest const* qInfo, bool msg )
-{
- uint32 reqraces = qInfo->GetRequiredRaces();
- if ( reqraces == 0 )
- return true;
- if( (reqraces & getRaceMask()) == 0 )
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_QUEST_FAILED_WRONG_RACE );
- return false;
- }
- return true;
-}
-
-bool Player::SatisfyQuestReputation( Quest const* qInfo, bool msg )
-{
- uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); //Min required rep
- if(fIdMin && GetReputation(fIdMin) < qInfo->GetRequiredMinRepValue())
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
-
- uint32 fIdMax = qInfo->GetRequiredMaxRepFaction(); //Max required rep
- if(fIdMax && GetReputation(fIdMax) >= qInfo->GetRequiredMaxRepValue())
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
-
- return true;
-}
-
-bool Player::SatisfyQuestStatus( Quest const* qInfo, bool msg )
-{
- QuestStatusMap::iterator itr = mQuestStatus.find( qInfo->GetQuestId() );
- if ( itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE )
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_QUEST_ALREADY_ON );
- return false;
- }
- return true;
-}
-
-bool Player::SatisfyQuestTimed( Quest const* qInfo, bool msg )
-{
- if ( (find(m_timedquests.begin(), m_timedquests.end(), qInfo->GetQuestId()) != m_timedquests.end()) && qInfo->HasFlag(QUEST_MANGOS_FLAGS_TIMED) )
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_QUEST_ONLY_ONE_TIMED );
- return false;
- }
- return true;
-}
-
-bool Player::SatisfyQuestExclusiveGroup( Quest const* qInfo, bool msg )
-{
- // non positive exclusive group, if > 0 then can be start if any other quest in exclusive group already started/completed
- if(qInfo->GetExclusiveGroup() <= 0)
- return true;
-
- ObjectMgr::ExclusiveQuestGroups::iterator iter = objmgr.mExclusiveQuestGroups.lower_bound(qInfo->GetExclusiveGroup());
- ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qInfo->GetExclusiveGroup());
-
- assert(iter!=end); // always must be found if qInfo->ExclusiveGroup != 0
-
- for(; iter != end; ++iter)
- {
- uint32 exclude_Id = iter->second;
-
- // skip checked quest id, only state of other quests in group is interesting
- if(exclude_Id == qInfo->GetQuestId())
- continue;
-
- QuestStatusMap::iterator i_exstatus = mQuestStatus.find( exclude_Id );
-
- // alternative quest already started or completed
- if( i_exstatus != mQuestStatus.end()
- && (i_exstatus->second.m_status == QUEST_STATUS_COMPLETE || i_exstatus->second.m_status == QUEST_STATUS_INCOMPLETE) )
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
- }
- return true;
-}
-
-bool Player::SatisfyQuestNextChain( Quest const* qInfo, bool msg )
-{
- if(!qInfo->GetNextQuestInChain())
- return true;
-
- // next quest in chain already started or completed
- QuestStatusMap::iterator itr = mQuestStatus.find( qInfo->GetNextQuestInChain() );
- if( itr != mQuestStatus.end()
- && (itr->second.m_status == QUEST_STATUS_COMPLETE || itr->second.m_status == QUEST_STATUS_INCOMPLETE) )
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
-
- // check for all quests further up the chain
- // only necessary if there are quest chains with more than one quest that can be skipped
- //return SatisfyQuestNextChain( qInfo->GetNextQuestInChain(), msg );
- return true;
-}
-
-bool Player::SatisfyQuestPrevChain( Quest const* qInfo, bool msg )
-{
- // No previous quest in chain
- if( qInfo->prevChainQuests.empty())
- return true;
-
- for(Quest::PrevChainQuests::const_iterator iter = qInfo->prevChainQuests.begin(); iter != qInfo->prevChainQuests.end(); ++iter )
- {
- uint32 prevId = *iter;
-
- QuestStatusMap::iterator i_prevstatus = mQuestStatus.find( prevId );
-
- if( i_prevstatus != mQuestStatus.end() )
- {
- // If any of the previous quests in chain active, return false
- if( i_prevstatus->second.m_status == QUEST_STATUS_INCOMPLETE
- || (i_prevstatus->second.m_status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(prevId)))
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
- return false;
- }
- }
-
- // check for all quests further down the chain
- // only necessary if there are quest chains with more than one quest that can be skipped
- //if( !SatisfyQuestPrevChain( prevId, msg ) )
- // return false;
- }
-
- // No previous quest in chain active
- return true;
-}
-
-bool Player::SatisfyQuestDay( Quest const* qInfo, bool msg )
-{
- if(!qInfo->IsDaily())
- return true;
-
- bool have_slot = false;
- for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
- {
- uint32 id = GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx);
- if(qInfo->GetQuestId()==id)
- return false;
-
- if(!id)
- have_slot = true;
- }
-
- if(!have_slot)
- {
- if( msg )
- SendCanTakeQuestResponse( INVALIDREASON_DAILY_QUESTS_REMAINING );
- return false;
- }
-
- return true;
-}
-
-bool Player::GiveQuestSourceItem( Quest const *pQuest )
-{
- uint32 srcitem = pQuest->GetSrcItemId();
- if( srcitem > 0 )
- {
- uint32 count = pQuest->GetSrcItemCount();
- if( count <= 0 )
- count = 1;
-
- ItemPosCountVec dest;
- uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, srcitem, count );
- if( msg == EQUIP_ERR_OK )
- {
- Item * item = StoreNewItem(dest, srcitem, true);
- SendNewItem(item, count, true, false);
- return true;
- }
- // player already have max amount required item, just report success
- else if( msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
- return true;
- else
- SendEquipError( msg, NULL, NULL );
- return false;
- }
-
- return true;
-}
-
-bool Player::TakeQuestSourceItem( uint32 quest_id, bool msg )
-{
- Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
- if( qInfo )
- {
- uint32 srcitem = qInfo->GetSrcItemId();
- if( srcitem > 0 )
- {
- uint32 count = qInfo->GetSrcItemCount();
- if( count <= 0 )
- count = 1;
-
- // exist one case when destroy source quest item not possible:
- // non un-equippable item (equipped non-empty bag, for example)
- uint8 res = CanUnequipItems(srcitem,count);
- if(res != EQUIP_ERR_OK)
- {
- if(msg)
- SendEquipError( res, NULL, NULL );
- return false;
- }
-
- DestroyItemCount(srcitem, count, true, true);
- }
- }
- return true;
-}
-
-bool Player::GetQuestRewardStatus( uint32 quest_id ) const
-{
- Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
- if( qInfo )
- {
- // for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
- QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
- if( itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE
- && !qInfo->IsRepeatable() )
- return itr->second.m_rewarded;
-
- return false;
- }
- return false;
-}
-
-QuestStatus Player::GetQuestStatus( uint32 quest_id ) const
-{
- if( quest_id )
- {
- QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
- if( itr != mQuestStatus.end() )
- return itr->second.m_status;
- }
- return QUEST_STATUS_NONE;
-}
-
-bool Player::CanShareQuest(uint32 quest_id) const
-{
- Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
- if( qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE) )
- {
- QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
- if( itr != mQuestStatus.end() )
- return itr->second.m_status == QUEST_STATUS_NONE || itr->second.m_status == QUEST_STATUS_INCOMPLETE;
- }
- return false;
-}
-
-void Player::SetQuestStatus( uint32 quest_id, QuestStatus status )
-{
- Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
- if( qInfo )
- {
- if( status == QUEST_STATUS_NONE || status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_COMPLETE )
- {
- if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) )
- m_timedquests.erase(qInfo->GetQuestId());
- }
-
- QuestStatusData& q_status = mQuestStatus[quest_id];
-
- q_status.m_status = status;
- if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
- }
-
- UpdateForQuestsGO();
-}
-
-// not used in MaNGOS, but used in scripting code
-uint32 Player::GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry)
-{
- Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
- if( !qInfo )
- return 0;
-
- for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
- if ( qInfo->ReqCreatureOrGOId[j] == entry )
- return mQuestStatus[quest_id].m_creatureOrGOcount[j];
-
- return 0;
-}
-
-void Player::AdjustQuestReqItemCount( Quest const* pQuest )
-{
- if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
- {
- for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
- {
- uint32 reqitemcount = pQuest->ReqItemCount[i];
- if( reqitemcount != 0 )
- {
- uint32 quest_id = pQuest->GetQuestId();
- uint32 curitemcount = GetItemCount(pQuest->ReqItemId[i],true);
-
- QuestStatusData& q_status = mQuestStatus[quest_id];
- q_status.m_itemcount[i] = std::min(curitemcount, reqitemcount);
- if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
- }
- }
- }
-}
-
-uint16 Player::FindQuestSlot( uint32 quest_id ) const
-{
- for ( uint16 i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
- if ( GetQuestSlotQuestId(i) == quest_id )
- return i;
-
- return MAX_QUEST_LOG_SIZE;
-}
-
-void Player::AreaExploredOrEventHappens( uint32 questId )
-{
- if( questId )
- {
- uint16 log_slot = FindQuestSlot( questId );
- if( log_slot < MAX_QUEST_LOG_SIZE)
- {
- QuestStatusData& q_status = mQuestStatus[questId];
-
- if(!q_status.m_explored)
- {
- q_status.m_explored = true;
- if (q_status.uState != QUEST_NEW)
- q_status.uState = QUEST_CHANGED;
- }
- }
- if( CanCompleteQuest( questId ) )
- CompleteQuest( questId );
- }
-}
-
-//not used in mangosd, function for external script library
-void Player::GroupEventHappens( uint32 questId, WorldObject const* pEventObject )
-{
- if( Group *pGroup = GetGroup() )
- {
- for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- Player *pGroupGuy = itr->getSource();
-
- // for any leave or dead (with not released body) group member at appropriate distance
- if( pGroupGuy && pGroupGuy->IsAtGroupRewardDistance(pEventObject) && !pGroupGuy->GetCorpse() )
- pGroupGuy->AreaExploredOrEventHappens(questId);
- }
- }
- else
- AreaExploredOrEventHappens(questId);
-}
-
-void Player::ItemAddedQuestCheck( uint32 entry, uint32 count )
-{
- for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if ( questid == 0 )
- continue;
-
- QuestStatusData& q_status = mQuestStatus[questid];
-
- if ( q_status.m_status != QUEST_STATUS_INCOMPLETE )
- continue;
-
- Quest const* qInfo = objmgr.GetQuestTemplate(questid);
- if( !qInfo || !qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
- continue;
-
- for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
- {
- uint32 reqitem = qInfo->ReqItemId[j];
- if ( reqitem == entry )
- {
- uint32 reqitemcount = qInfo->ReqItemCount[j];
- uint32 curitemcount = q_status.m_itemcount[j];
- if ( curitemcount < reqitemcount )
- {
- uint32 additemcount = ( curitemcount + count <= reqitemcount ? count : reqitemcount - curitemcount);
- q_status.m_itemcount[j] += additemcount;
- if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
-
- SendQuestUpdateAddItem( qInfo, j, additemcount );
- }
- if ( CanCompleteQuest( questid ) )
- CompleteQuest( questid );
- return;
- }
- }
- }
- UpdateForQuestsGO();
-}
-
-void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count )
-{
- for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if(!questid)
- continue;
- Quest const* qInfo = objmgr.GetQuestTemplate(questid);
- if ( !qInfo )
- continue;
- if( !qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
- continue;
-
- for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
- {
- uint32 reqitem = qInfo->ReqItemId[j];
- if ( reqitem == entry )
- {
- QuestStatusData& q_status = mQuestStatus[questid];
-
- uint32 reqitemcount = qInfo->ReqItemCount[j];
- uint32 curitemcount;
- if( q_status.m_status != QUEST_STATUS_COMPLETE )
- curitemcount = q_status.m_itemcount[j];
- else
- curitemcount = GetItemCount(entry,true);
- if ( curitemcount < reqitemcount + count )
- {
- uint32 remitemcount = ( curitemcount <= reqitemcount ? count : count + reqitemcount - curitemcount);
- q_status.m_itemcount[j] = curitemcount - remitemcount;
- if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
-
- IncompleteQuest( questid );
- }
- return;
- }
- }
- }
- UpdateForQuestsGO();
-}
-
-void Player::KilledMonster( uint32 entry, uint64 guid )
-{
- uint32 addkillcount = 1;
- for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if(!questid)
- continue;
-
- Quest const* qInfo = objmgr.GetQuestTemplate(questid);
- if( !qInfo )
- continue;
- // just if !ingroup || !noraidgroup || raidgroup
- QuestStatusData& q_status = mQuestStatus[questid];
- if( q_status.m_status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->GetType() == QUEST_TYPE_RAID))
- {
- if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST) )
- {
- for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
- {
- // skip GO activate objective or none
- if(qInfo->ReqCreatureOrGOId[j] <=0)
- continue;
-
- // skip Cast at creature objective
- if(qInfo->ReqSpell[j] !=0 )
- continue;
-
- uint32 reqkill = qInfo->ReqCreatureOrGOId[j];
-
- if ( reqkill == entry )
- {
- uint32 reqkillcount = qInfo->ReqCreatureOrGOCount[j];
- uint32 curkillcount = q_status.m_creatureOrGOcount[j];
- if ( curkillcount < reqkillcount )
- {
- q_status.m_creatureOrGOcount[j] = curkillcount + addkillcount;
- if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
-
- SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curkillcount, addkillcount);
- }
- if ( CanCompleteQuest( questid ) )
- CompleteQuest( questid );
-
- // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
- continue;
- }
- }
- }
- }
- }
-}
-
-void Player::CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id )
-{
- bool isCreature = IS_CREATURE_GUID(guid);
-
- uint32 addCastCount = 1;
- for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if(!questid)
- continue;
-
- Quest const* qInfo = objmgr.GetQuestTemplate(questid);
- if ( !qInfo )
- continue;
-
- QuestStatusData& q_status = mQuestStatus[questid];
-
- if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
- {
- if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST ) )
- {
- for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
- {
- // skip kill creature objective (0) or wrong spell casts
- if(qInfo->ReqSpell[j] != spell_id )
- continue;
-
- uint32 reqTarget = 0;
-
- if(isCreature)
- {
- // creature activate objectives
- if(qInfo->ReqCreatureOrGOId[j] > 0)
- // checked at quest_template loading
- reqTarget = qInfo->ReqCreatureOrGOId[j];
- }
- else
- {
- // GO activate objective
- if(qInfo->ReqCreatureOrGOId[j] < 0)
- // checked at quest_template loading
- reqTarget = - qInfo->ReqCreatureOrGOId[j];
- }
-
- // other not this creature/GO related objectives
- if( reqTarget != entry )
- continue;
-
- uint32 reqCastCount = qInfo->ReqCreatureOrGOCount[j];
- uint32 curCastCount = q_status.m_creatureOrGOcount[j];
- if ( curCastCount < reqCastCount )
- {
- q_status.m_creatureOrGOcount[j] = curCastCount + addCastCount;
- if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
-
- SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curCastCount, addCastCount);
- }
-
- if ( CanCompleteQuest( questid ) )
- CompleteQuest( questid );
-
- // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
- break;
- }
- }
- }
- }
-}
-
-void Player::TalkedToCreature( uint32 entry, uint64 guid )
-{
- uint32 addTalkCount = 1;
- for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if(!questid)
- continue;
-
- Quest const* qInfo = objmgr.GetQuestTemplate(questid);
- if ( !qInfo )
- continue;
-
- QuestStatusData& q_status = mQuestStatus[questid];
-
- if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
- {
- if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO ) )
- {
- for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
- {
- // skip spell casts and Gameobject objectives
- if(qInfo->ReqSpell[j] > 0 || qInfo->ReqCreatureOrGOId[j] < 0)
- continue;
-
- uint32 reqTarget = 0;
-
- if(qInfo->ReqCreatureOrGOId[j] > 0) // creature activate objectives
- // checked at quest_template loading
- reqTarget = qInfo->ReqCreatureOrGOId[j];
- else
- continue;
-
- if ( reqTarget == entry )
- {
- uint32 reqTalkCount = qInfo->ReqCreatureOrGOCount[j];
- uint32 curTalkCount = q_status.m_creatureOrGOcount[j];
- if ( curTalkCount < reqTalkCount )
- {
- q_status.m_creatureOrGOcount[j] = curTalkCount + addTalkCount;
- if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
-
- SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curTalkCount, addTalkCount);
- }
- if ( CanCompleteQuest( questid ) )
- CompleteQuest( questid );
-
- // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
- continue;
- }
- }
- }
- }
- }
-}
-
-void Player::MoneyChanged( uint32 count )
-{
- for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
- {
- uint32 questid = GetQuestSlotQuestId(i);
- if (!questid)
- continue;
-
- Quest const* qInfo = objmgr.GetQuestTemplate(questid);
- if( qInfo && qInfo->GetRewOrReqMoney() < 0 )
- {
- QuestStatusData& q_status = mQuestStatus[questid];
-
- if( q_status.m_status == QUEST_STATUS_INCOMPLETE )
- {
- if(int32(count) >= -qInfo->GetRewOrReqMoney())
- {
- if ( CanCompleteQuest( questid ) )
- CompleteQuest( questid );
- }
- }
- else if( q_status.m_status == QUEST_STATUS_COMPLETE )
- {
- if(int32(count) < -qInfo->GetRewOrReqMoney())
- IncompleteQuest( questid );
- }
- }
- }
-}
-
-bool Player::HasQuestForItem( uint32 itemid ) const
-{
- for( QuestStatusMap::const_iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
- {
- QuestStatusData const& q_status = i->second;
-
- if (q_status.m_status == QUEST_STATUS_INCOMPLETE)
- {
- Quest const* qinfo = objmgr.GetQuestTemplate(i->first);
- if(!qinfo)
- continue;
-
- // hide quest if player is in raid-group and quest is no raid quest
- if(GetGroup() && GetGroup()->isRaidGroup() && qinfo->GetType() != QUEST_TYPE_RAID)
- continue;
-
- // There should be no mixed ReqItem/ReqSource drop
- // This part for ReqItem drop
- for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
- {
- if(itemid == qinfo->ReqItemId[j] && q_status.m_itemcount[j] < qinfo->ReqItemCount[j] )
- return true;
- }
- // This part - for ReqSource
- for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; j++)
- {
- // examined item is a source item
- if (qinfo->ReqSourceId[j] == itemid && qinfo->ReqSourceRef[j] > 0 && qinfo->ReqSourceRef[j] <= QUEST_OBJECTIVES_COUNT)
- {
- uint32 idx = qinfo->ReqSourceRef[j]-1;
-
- // total count of created ReqItems and SourceItems is less than ReqItemCount
- if(qinfo->ReqItemId[idx] != 0 &&
- q_status.m_itemcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqItemCount[idx] * qinfo->ReqSourceCount[j])
- return true;
-
- // total count of casted ReqCreatureOrGOs and SourceItems is less than ReqCreatureOrGOCount
- if (qinfo->ReqCreatureOrGOId[idx] != 0)
- {
- if(q_status.m_creatureOrGOcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqCreatureOrGOCount[idx] * qinfo->ReqSourceCount[j])
- return true;
- }
- // spell with SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT (with script) case
- else if(qinfo->ReqSpell[idx] != 0)
- {
- // not casted and need more reagents/item for use.
- if(!q_status.m_explored && GetItemCount(itemid,true) < qinfo->ReqSourceCount[j])
- return true;
- }
- }
- }
- }
- }
- return false;
-}
-
-void Player::SendQuestComplete( uint32 quest_id )
-{
- if( quest_id )
- {
- WorldPacket data( SMSG_QUESTUPDATE_COMPLETE, 4 );
- data << quest_id;
- GetSession()->SendPacket( &data );
- sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = %u", quest_id );
- }
-}
-
-void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGiver )
-{
- uint32 questid = pQuest->GetQuestId();
- sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questid );
- WorldPacket data( SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4+4+pQuest->GetRewItemsCount()*8) );
- data << questid;
- data << uint32(0x03);
-
- if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
- {
- data << XP;
- data << uint32(pQuest->GetRewOrReqMoney());
- }
- else
- {
- data << uint32(0);
- data << uint32(pQuest->GetRewOrReqMoney() + int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)));
- }
- data << uint32(0); // new 2.3.0, HonorPoints?
- data << uint32( pQuest->GetRewItemsCount() ); // max is 5
-
- for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i)
- {
- if ( pQuest->RewItemId[i] > 0 )
- data << pQuest->RewItemId[i] << pQuest->RewItemCount[i];
- else
- data << uint32(0) << uint32(0);
- }
- GetSession()->SendPacket( &data );
-
- if (pQuest->GetQuestCompleteScript() != 0)
- sWorld.ScriptsStart(sQuestEndScripts, pQuest->GetQuestCompleteScript(), questGiver, this);
-}
-
-void Player::SendQuestFailed( uint32 quest_id )
-{
- if( quest_id )
- {
- WorldPacket data( SMSG_QUESTGIVER_QUEST_FAILED, 4 );
- data << quest_id;
- GetSession()->SendPacket( &data );
- sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED");
- }
-}
-
-void Player::SendQuestTimerFailed( uint32 quest_id )
-{
- if( quest_id )
- {
- WorldPacket data( SMSG_QUESTUPDATE_FAILEDTIMER, 4 );
- data << quest_id;
- GetSession()->SendPacket( &data );
- sLog.outDebug("WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
- }
-}
-
-void Player::SendCanTakeQuestResponse( uint32 msg )
-{
- WorldPacket data( SMSG_QUESTGIVER_QUEST_INVALID, 4 );
- data << uint32(msg);
- GetSession()->SendPacket( &data );
- sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID");
-}
-
-void Player::SendPushToPartyResponse( Player *pPlayer, uint32 msg )
-{
- if( pPlayer )
- {
- WorldPacket data( MSG_QUEST_PUSH_RESULT, (8+1) );
- data << uint64(pPlayer->GetGUID());
- data << uint8(msg); // valid values: 0-8
- GetSession()->SendPacket( &data );
- sLog.outDebug("WORLD: Sent MSG_QUEST_PUSH_RESULT");
- }
-}
-
-void Player::SendQuestUpdateAddItem( Quest const* pQuest, uint32 item_idx, uint32 count )
-{
- WorldPacket data( SMSG_QUESTUPDATE_ADD_ITEM, (4+4) );
- sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_ADD_ITEM" );
- data << pQuest->ReqItemId[item_idx];
- data << count;
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendQuestUpdateAddCreatureOrGo( Quest const* pQuest, uint64 guid, uint32 creatureOrGO_idx, uint32 old_count, uint32 add_count )
-{
- assert(old_count + add_count < 256 && "mob/GO count store in 8 bits 2^8 = 256 (0..256)");
-
- int32 entry = pQuest->ReqCreatureOrGOId[ creatureOrGO_idx ];
- if (entry < 0)
- // client expected gameobject template id in form (id|0x80000000)
- entry = (-entry) | 0x80000000;
-
- WorldPacket data( SMSG_QUESTUPDATE_ADD_KILL, (4*4+8) );
- sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_ADD_KILL" );
- data << uint32(pQuest->GetQuestId());
- data << uint32(entry);
- data << uint32(old_count + add_count);
- data << uint32(pQuest->ReqCreatureOrGOCount[ creatureOrGO_idx ]);
- data << uint64(guid);
- GetSession()->SendPacket(&data);
-
- uint16 log_slot = FindQuestSlot( pQuest->GetQuestId() );
- if( log_slot < MAX_QUEST_LOG_SIZE)
- SetQuestSlotCounter(log_slot,creatureOrGO_idx,GetQuestSlotCounter(log_slot,creatureOrGO_idx)+add_count);
-}
-
-/*********************************************************/
-/*** LOAD SYSTEM ***/
-/*********************************************************/
-
-bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid )
-{
- bool delete_result = true;
- if(!result)
- {
- // 0 1 2 3 4 5 6 7 8
- result = CharacterDatabase.PQuery("SELECT data, name, position_x, position_y, position_z, map, totaltime, leveltime, at_login FROM characters WHERE guid = '%u'",guid);
- if(!result) return false;
- }
- else delete_result = false;
-
- Field *fields = result->Fetch();
-
- if(!LoadValues( fields[0].GetString()))
- {
- sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid));
- if(delete_result) delete result;
- return false;
- }
-
- // overwrite possible wrong/corrupted guid
- SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
-
- m_name = fields[1].GetCppString();
-
- Relocate(fields[2].GetFloat(),fields[3].GetFloat(),fields[4].GetFloat());
- SetMapId(fields[5].GetUInt32());
- // the instance id is not needed at character enum
-
- m_Played_time[0] = fields[6].GetUInt32();
- m_Played_time[1] = fields[7].GetUInt32();
-
- m_atLoginFlags = fields[8].GetUInt32();
-
- // I don't see these used anywhere ..
- /*_LoadGroup();
-
- _LoadBoundInstances();*/
-
- if (delete_result) delete result;
-
- for (int i = 0; i < PLAYER_SLOTS_COUNT; i++)
- m_items[i] = NULL;
-
- if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) )
- m_deathState = DEAD;
-
- return true;
-}
-
-void Player::_LoadDeclinedNames(QueryResult* result)
-{
- if(!result)
- return;
-
- if(m_declinedname)
- delete m_declinedname;
-
- m_declinedname = new DeclinedName;
- Field *fields = result->Fetch();
- for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
- m_declinedname->name[i] = fields[i].GetCppString();
-
- delete result;
-}
-
-bool Player::LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid)
-{
- QueryResult *result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,taxi_path FROM characters WHERE guid = '%u'",GUID_LOPART(guid));
- if(!result)
- return false;
-
- Field *fields = result->Fetch();
-
- x = fields[0].GetFloat();
- y = fields[1].GetFloat();
- z = fields[2].GetFloat();
- o = fields[3].GetFloat();
- mapid = fields[4].GetUInt32();
- in_flight = !fields[5].GetCppString().empty();
-
- delete result;
- return true;
-}
-
-bool Player::LoadValuesArrayFromDB(Tokens& data, uint64 guid)
-{
- QueryResult *result = CharacterDatabase.PQuery("SELECT data FROM characters WHERE guid='%u'",GUID_LOPART(guid));
- if( !result )
- return false;
-
- Field *fields = result->Fetch();
-
- data = StrSplit(fields[0].GetCppString(), " ");
-
- delete result;
-
- return true;
-}
-
-uint32 Player::GetUInt32ValueFromArray(Tokens const& data, uint16 index)
-{
- if(index >= data.size())
- return 0;
-
- return (uint32)atoi(data[index].c_str());
-}
-
-float Player::GetFloatValueFromArray(Tokens const& data, uint16 index)
-{
- float result;
- uint32 temp = Player::GetUInt32ValueFromArray(data,index);
- memcpy(&result, &temp, sizeof(result));
-
- return result;
-}
-
-uint32 Player::GetUInt32ValueFromDB(uint16 index, uint64 guid)
-{
- Tokens data;
- if(!LoadValuesArrayFromDB(data,guid))
- return 0;
-
- return GetUInt32ValueFromArray(data,index);
-}
-
-float Player::GetFloatValueFromDB(uint16 index, uint64 guid)
-{
- float result;
- uint32 temp = Player::GetUInt32ValueFromDB(index, guid);
- memcpy(&result, &temp, sizeof(result));
-
- return result;
-}
-
-bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
-{
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [28] [29] 30 31 32
- //QueryResult *result = CharacterDatabase.PQuery("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);
- QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM);
-
- if(!result)
- {
- sLog.outError("ERROR: Player (GUID: %u) not found in table `characters`, can't load. ",guid);
- return false;
- }
-
- Field *fields = result->Fetch();
-
- uint32 dbAccountId = fields[1].GetUInt32();
-
- // check if the character's account in the db and the logged in account match.
- // player should be able to load/delete character only with correct account!
- if( dbAccountId != GetSession()->GetAccountId() )
- {
- sLog.outError("ERROR: Player (GUID: %u) loading from wrong account (is: %u, should be: %u)",guid,GetSession()->GetAccountId(),dbAccountId);
- delete result;
- return false;
- }
-
- Object::_Create( guid, 0, HIGHGUID_PLAYER );
-
- m_name = fields[3].GetCppString();
-
- // check name limitations
- if(!ObjectMgr::IsValidName(m_name) || GetSession()->GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(m_name))
- {
- delete result;
- CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid ='%u'", uint32(AT_LOGIN_RENAME),guid);
- return false;
- }
-
- if(!LoadValues( fields[2].GetString()))
- {
- sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid));
- delete result;
- return false;
- }
-
- // overwrite possible wrong/corrupted guid
- SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
-
- // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
- for(uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
- {
- SetUInt64Value( (uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2) ), 0 );
- SetVisibleItemSlot(slot,NULL);
-
- if (m_items[slot])
- {
- delete m_items[slot];
- m_items[slot] = NULL;
- }
- }
-
- // update money limits
- if(GetMoney() > MAX_MONEY_AMOUNT)
- SetMoney(MAX_MONEY_AMOUNT);
-
- sLog.outDebug("Load Basic value of player %s is: ", m_name.c_str());
- outDebugValues();
-
- m_race = fields[4].GetUInt8();
- //Need to call it to initialize m_team (m_team can be calculated from m_race)
- //Other way is to saves m_team into characters table.
- setFactionForRace(m_race);
- SetCharm(0);
-
- m_class = fields[5].GetUInt8();
-
- PlayerInfo const *info = objmgr.GetPlayerInfo(m_race, m_class);
- if(!info)
- {
- sLog.outError("Player have incorrect race/class pair. Can't be loaded.");
- delete result;
- return false;
- }
-
- InitPrimaryProffesions(); // to max set before any spell loaded
-
- uint32 transGUID = fields[24].GetUInt32();
- Relocate(fields[6].GetFloat(),fields[7].GetFloat(),fields[8].GetFloat(),fields[10].GetFloat());
- SetMapId(fields[9].GetUInt32());
- SetDifficulty(fields[32].GetUInt32()); // may be changed in _LoadGroup
-
- _LoadGroup(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP));
-
- // check arena teams integrity
- for(uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot)
- {
- uint32 arena_team_id = GetArenaTeamId(arena_slot);
- if(!arena_team_id)
- continue;
-
- if(ArenaTeam * at = objmgr.GetArenaTeamById(arena_team_id))
- if(at->HaveMember(GetGUID()))
- continue;
-
- // arena team not exist or not member, cleanup fields
- for(int j =0; j < 6; ++j)
- SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arena_slot * 6 + j, 0);
- }
-
- _LoadBoundInstances(holder->GetResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES));
-
- if(!IsPositionValid())
- {
- sLog.outError("ERROR: Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
-
- SetMapId(info->mapId);
- Relocate(info->positionX,info->positionY,info->positionZ,0.0f);
-
- transGUID = 0;
-
- m_movementInfo.t_x = 0.0f;
- m_movementInfo.t_y = 0.0f;
- m_movementInfo.t_z = 0.0f;
- m_movementInfo.t_o = 0.0f;
- }
-
- // load the player's map here if it's not already loaded
- Map *map = GetMap();
- // since the player may not be bound to the map yet, make sure subsequent
- // getmap calls won't create new maps
- SetInstanceId(map->GetInstanceId());
-
- SaveRecallPosition();
-
- if (transGUID != 0)
- {
- m_movementInfo.t_x = fields[20].GetFloat();
- m_movementInfo.t_y = fields[21].GetFloat();
- m_movementInfo.t_z = fields[22].GetFloat();
- m_movementInfo.t_o = fields[23].GetFloat();
-
- if( !MaNGOS::IsValidMapCoord(
- GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y,
- GetPositionZ()+m_movementInfo.t_z,GetOrientation()+m_movementInfo.t_o) ||
- // transport size limited
- m_movementInfo.t_x > 50 || m_movementInfo.t_y > 50 || m_movementInfo.t_z > 50 )
- {
- sLog.outError("ERROR: Player (guidlow %d) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
- guid,GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y,
- GetPositionZ()+m_movementInfo.t_z,GetOrientation()+m_movementInfo.t_o);
-
- SetMapId(info->mapId);
- Relocate(info->positionX,info->positionY,info->positionZ,0.0f);
-
- m_movementInfo.t_x = 0.0f;
- m_movementInfo.t_y = 0.0f;
- m_movementInfo.t_z = 0.0f;
- m_movementInfo.t_o = 0.0f;
-
- transGUID = 0;
- }
- }
-
- if (transGUID != 0)
- {
- for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter)
- {
- if( (*iter)->GetGUIDLow() == transGUID)
- {
- m_transport = *iter;
- m_transport->AddPassenger(this);
- SetMapId(m_transport->GetMapId());
- break;
- }
- }
-
- if(!m_transport)
- {
- sLog.outError("ERROR: Player (guidlow %d) have invalid transport guid (%u). Teleport to default race/class locations.",
- guid,transGUID);
-
- SetMapId(info->mapId);
- Relocate(info->positionX,info->positionY,info->positionZ,0.0f);
-
- m_movementInfo.t_x = 0.0f;
- m_movementInfo.t_y = 0.0f;
- m_movementInfo.t_z = 0.0f;
- m_movementInfo.t_o = 0.0f;
-
- transGUID = 0;
- }
- }
-
- time_t now = time(NULL);
- time_t logoutTime = time_t(fields[16].GetUInt64());
-
- // since last logout (in seconds)
- uint64 time_diff = uint64(now - logoutTime);
-
- // set value, including drunk invisibility detection
- // calculate sobering. after 15 minutes logged out, the player will be sober again
- float soberFactor;
- if(time_diff > 15*MINUTE)
- soberFactor = 0;
- else
- soberFactor = 1-time_diff/(15.0f*MINUTE);
- uint16 newDrunkenValue = uint16(soberFactor*(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE));
- SetDrunkValue(newDrunkenValue);
-
- m_rest_bonus = fields[15].GetFloat();
- //speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour)
- float bubble0 = 0.031;
- //speed collect rest bonus in offline, in logout, in tavern, city (section/in hour)
- float bubble1 = 0.125;
-
- if((int32)fields[16].GetUInt32() > 0)
- {
- float bubble = fields[17].GetUInt32() > 0
- ? bubble1*sWorld.getRate(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY)
- : bubble0*sWorld.getRate(RATE_REST_OFFLINE_IN_WILDERNESS);
-
- SetRestBonus(GetRestBonus()+ time_diff*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble);
- }
-
- m_cinematic = fields[12].GetUInt32();
- m_Played_time[0]= fields[13].GetUInt32();
- m_Played_time[1]= fields[14].GetUInt32();
-
- m_resetTalentsCost = fields[18].GetUInt32();
- m_resetTalentsTime = time_t(fields[19].GetUInt64());
-
- // reserve some flags
- uint32 old_safe_flags = GetUInt32Value(PLAYER_FLAGS) & ( PLAYER_FLAGS_HIDE_CLOAK | PLAYER_FLAGS_HIDE_HELM );
-
- if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM) )
- SetUInt32Value(PLAYER_FLAGS, 0 | old_safe_flags);
-
- m_taxi.LoadTaxiMask( fields[11].GetString() ); // must be before InitTaxiNodesForLevel
-
- uint32 gmstate = fields[25].GetUInt32();
-
- m_stableSlots = fields[26].GetUInt32();
- if(m_stableSlots > 2)
- {
- sLog.outError("Player can have not more 2 stable slots, but have in DB %u",uint32(m_stableSlots));
- m_stableSlots = 2;
- }
-
- m_atLoginFlags = fields[27].GetUInt32();
-
- // Honor system
- // Update Honor kills data
- m_lastHonorUpdateTime = logoutTime;
- UpdateHonorFields();
-
- m_deathExpireTime = (time_t)fields[30].GetUInt64();
- if(m_deathExpireTime > now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP)
- m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP-1;
-
- std::string taxi_nodes = fields[31].GetCppString();
-
- delete result;
-
- // clear channel spell data (if saved at channel spell casting)
- SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, 0);
- SetUInt32Value(UNIT_CHANNEL_SPELL,0);
-
- // clear charm/summon related fields
- SetUInt64Value(UNIT_FIELD_CHARM,0);
- SetUInt64Value(UNIT_FIELD_SUMMON,0);
- SetUInt64Value(UNIT_FIELD_CHARMEDBY,0);
- SetUInt64Value(UNIT_FIELD_SUMMONEDBY,0);
- SetUInt64Value(UNIT_FIELD_CREATEDBY,0);
-
- // reset some aura modifiers before aura apply
- SetUInt64Value(PLAYER_FARSIGHT, 0);
- SetUInt32Value(PLAYER_TRACK_CREATURES, 0 );
- SetUInt32Value(PLAYER_TRACK_RESOURCES, 0 );
-
- // reset skill modifiers and set correct unlearn flags
- for (uint32 i = 0; i < PLAYER_MAX_SKILLS; i++)
- {
- SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
-
- // set correct unlearn bit
- uint32 id = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
- if(!id) continue;
-
- SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id);
- if(!pSkill) continue;
-
- // enable unlearn button for primary professions only
- if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
- SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,1));
- else
- SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0));
- }
-
- // make sure the unit is considered out of combat for proper loading
- ClearInCombat();
-
- // make sure the unit is considered not in duel for proper loading
- SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
- SetUInt32Value(PLAYER_DUEL_TEAM, 0);
-
- // remember loaded power/health values to restore after stats initialization and modifier applying
- uint32 savedHealth = GetHealth();
- uint32 savedPower[MAX_POWERS];
- for(uint32 i = 0; i < MAX_POWERS; ++i)
- savedPower[i] = GetPower(Powers(i));
-
- // reset stats before loading any modifiers
- InitStatsForLevel();
- InitTaxiNodesForLevel();
-
- // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
-
- //mails are loaded only when needed ;-) - when player in game click on mailbox.
- //_LoadMail();
-
- _LoadAuras(holder->GetResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff);
-
- // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
- if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) )
- m_deathState = DEAD;
-
- _LoadSpells(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS));
-
- // after spell load
- InitTalentForLevel();
- learnSkillRewardedSpells();
-
- // after spell load, learn rewarded spell if need also
- _LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
- _LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS));
-
- _LoadTutorials(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTUTORIALS));
-
- // must be before inventory (some items required reputation check)
- _LoadReputation(holder->GetResult(PLAYER_LOGIN_QUERY_LOADREPUTATION));
-
- _LoadInventory(holder->GetResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff);
-
- // update items with duration and realtime
- UpdateItemDuration(time_diff, true);
-
- _LoadActions(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACTIONS));
-
- // unread mails and next delivery time, actual mails not loaded
- _LoadMailInit(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILCOUNT), holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILDATE));
-
- m_social = sSocialMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSOCIALLIST), GetGUIDLow());
-
- if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND)))
- return false;
-
- // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES
- // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded
- if(uint32 curTitle = GetUInt32Value(PLAYER_CHOSEN_TITLE))
- {
- if(!HasFlag64(PLAYER__FIELD_KNOWN_TITLES,uint64(1) << curTitle))
- SetUInt32Value(PLAYER_CHOSEN_TITLE,0);
- }
-
- // Not finish taxi flight path
- if(!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes))
- {
- // problems with taxi path loading
- TaxiNodesEntry const* nodeEntry = NULL;
- if(uint32 node_id = m_taxi.GetTaxiSource())
- nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
-
- if(!nodeEntry) // don't know taxi start node, to homebind
- {
- sLog.outError("Character %u have wrong data in taxi destination list, teleport to homebind.",GetGUIDLow());
- SetMapId(m_homebindMapId);
- Relocate( m_homebindX, m_homebindY, m_homebindZ,0.0f);
- SaveRecallPosition(); // save as recall also to prevent recall and fall from sky
- }
- else // have start node, to it
- {
- sLog.outError("Character %u have too short taxi destination list, teleport to original node.",GetGUIDLow());
- SetMapId(nodeEntry->map_id);
- Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z,0.0f);
- SaveRecallPosition(); // save as recall also to prevent recall and fall from sky
- }
- m_taxi.ClearTaxiDestinations();
- }
- else if(uint32 node_id = m_taxi.GetTaxiSource())
- {
- // save source node as recall coord to prevent recall and fall from sky
- TaxiNodesEntry const* nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
- assert(nodeEntry); // checked in m_taxi.LoadTaxiDestinationsFromString
- m_recallMap = nodeEntry->map_id;
- m_recallX = nodeEntry->x;
- m_recallY = nodeEntry->y;
- m_recallZ = nodeEntry->z;
-
- // flight will started later
- }
-
- _LoadSpellCooldowns(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS));
-
- // Spell code allow apply any auras to dead character in load time in aura/spell/item loading
- // Do now before stats re-calculation cleanup for ghost state unexpected auras
- if(!isAlive())
- RemoveAllAurasOnDeath();
-
- //apply all stat bonuses from items and auras
- SetCanModifyStats(true);
- UpdateAllStats();
-
- // restore remembered power/health values (but not more max values)
- SetHealth(savedHealth > GetMaxHealth() ? GetMaxHealth() : savedHealth);
- for(uint32 i = 0; i < MAX_POWERS; ++i)
- SetPower(Powers(i),savedPower[i] > GetMaxPower(Powers(i)) ? GetMaxPower(Powers(i)) : savedPower[i]);
-
- sLog.outDebug("The value of player %s after load item and aura is: ", m_name.c_str());
- outDebugValues();
-
- // GM state
- if(GetSession()->GetSecurity() > SEC_PLAYER)
- {
- switch(sWorld.getConfig(CONFIG_GM_LOGIN_STATE))
- {
- default:
- case 0: break; // disable
- case 1: SetGameMaster(true); break; // enable
- case 2: // save state
- 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;
- }
- }
-
- _LoadDeclinedNames(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES));
-
- return true;
-}
-
-bool Player::isAllowedToLoot(Creature* creature)
-{
- if(Player* recipient = creature->GetLootRecipient())
- {
- if (recipient == this)
- return true;
- if( Group* otherGroup = recipient->GetGroup())
- {
- Group* thisGroup = GetGroup();
- if(!thisGroup)
- return false;
- return thisGroup == otherGroup;
- }
- return false;
- }
- else
- // prevent other players from looting if the recipient got disconnected
- return !creature->hasLootRecipient();
-}
-
-void Player::_LoadActions(QueryResult *result)
-{
- m_actionButtons.clear();
-
- //QueryResult *result = CharacterDatabase.PQuery("SELECT button,action,type,misc FROM character_action WHERE guid = '%u' ORDER BY button",GetGUIDLow());
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
-
- uint8 button = fields[0].GetUInt8();
-
- addActionButton(button, fields[1].GetUInt16(), fields[2].GetUInt8(), fields[3].GetUInt8());
-
- m_actionButtons[button].uState = ACTIONBUTTON_UNCHANGED;
- }
- while( result->NextRow() );
-
- delete result;
- }
-}
-
-void Player::_LoadAuras(QueryResult *result, uint32 timediff)
-{
- m_Auras.clear();
- for (int i = 0; i < TOTAL_AURAS; i++)
- m_modAuras[i].clear();
-
- // all aura related fields
- for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i)
- SetUInt32Value(i, 0);
-
- //QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'",GetGUIDLow());
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
- uint64 caster_guid = fields[0].GetUInt64();
- uint32 spellid = fields[1].GetUInt32();
- uint32 effindex = fields[2].GetUInt32();
- int32 damage = (int32)fields[3].GetUInt32();
- int32 maxduration = (int32)fields[4].GetUInt32();
- int32 remaintime = (int32)fields[5].GetUInt32();
- int32 remaincharges = (int32)fields[6].GetUInt32();
-
- SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
- if(!spellproto)
- {
- sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex);
- continue;
- }
-
- if(effindex >= 3)
- {
- sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex);
- continue;
- }
-
- // negative effects should continue counting down after logout
- if (remaintime != -1 && !IsPositiveEffect(spellid, effindex))
- {
- if(remaintime <= int32(timediff))
- continue;
-
- remaintime -= timediff;
- }
-
- // prevent wrong values of remaincharges
- if(spellproto->procCharges)
- {
- if(remaincharges <= 0 || remaincharges > spellproto->procCharges)
- remaincharges = spellproto->procCharges;
- }
- else
- remaincharges = -1;
-
- //do not load single target auras (unless they were cast by the player)
- if (caster_guid != GetGUID() && IsSingleTargetSpell(spellproto))
- continue;
-
- Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL);
- if(!damage)
- damage = aura->GetModifier()->m_amount;
- aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges);
- AddAura(aura);
- }
- while( result->NextRow() );
-
- delete result;
- }
-
- if(m_class == CLASS_WARRIOR)
- CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true);
-}
-
-void Player::LoadCorpse()
-{
- if( isAlive() )
- {
- ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID());
- }
- else
- {
- if(Corpse *corpse = GetCorpse())
- {
- ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, corpse && !sMapStore.LookupEntry(corpse->GetMapId())->Instanceable() );
- }
- else
- {
- //Prevent Dead Player login without corpse
- ResurrectPlayer(0.5f);
- }
- }
-}
-
-void Player::_LoadInventory(QueryResult *result, uint32 timediff)
-{
- //QueryResult *result = CharacterDatabase.PQuery("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", GetGUIDLow());
- std::map<uint64, Bag*> bagMap; // fast guid lookup for bags
- //NOTE: the "order by `bag`" is important because it makes sure
- //the bagMap is filled before items in the bags are loaded
- //NOTE2: the "order by `slot`" is needed becaue mainhand weapons are (wrongly?)
- //expected to be equipped before offhand items (TODO: fixme)
-
- uint32 zone = GetZoneId();
-
- if (result)
- {
- std::list<Item*> problematicItems;
-
- // prevent items from being added to the queue when stored
- m_itemUpdateQueueBlocked = true;
- do
- {
- Field *fields = result->Fetch();
- uint32 bag_guid = fields[1].GetUInt32();
- uint8 slot = fields[2].GetUInt8();
- uint32 item_guid = fields[3].GetUInt32();
- uint32 item_id = fields[4].GetUInt32();
-
- ItemPrototype const * proto = objmgr.GetItemPrototype(item_id);
-
- if(!proto)
- {
- CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
- CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid);
- sLog.outError( "Player::_LoadInventory: Player %s has an unknown item (id: #%u) in inventory, deleted.", GetName(),item_id );
- continue;
- }
-
- Item *item = NewItemOrBag(proto);
-
- if(!item->LoadFromDB(item_guid, GetGUID(), result))
- {
- sLog.outError( "Player::_LoadInventory: Player %s has broken item (id: #%u) in inventory, deleted.", GetName(),item_id );
- CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
- item->FSetState(ITEM_REMOVED);
- item->SaveToDB(); // it also deletes item object !
- continue;
- }
-
- // not allow have in alive state item limited to another map/zone
- if(isAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(),zone) )
- {
- CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
- item->FSetState(ITEM_REMOVED);
- item->SaveToDB(); // it also deletes item object !
- continue;
- }
-
- // "Conjured items disappear if you are logged out for more than 15 minutes"
- if ((timediff > 15*60) && (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED)))
- {
- CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
- item->FSetState(ITEM_REMOVED);
- item->SaveToDB(); // it also deletes item object !
- continue;
- }
-
- bool success = true;
-
- if (!bag_guid)
- {
- // the item is not in a bag
- item->SetContainer( NULL );
- item->SetSlot(slot);
-
- if( IsInventoryPos( INVENTORY_SLOT_BAG_0, slot ) )
- {
- ItemPosCountVec dest;
- if( CanStoreItem( INVENTORY_SLOT_BAG_0, slot, dest, item, false ) == EQUIP_ERR_OK )
- item = StoreItem(dest, item, true);
- else
- success = false;
- }
- else if( IsEquipmentPos( INVENTORY_SLOT_BAG_0, slot ) )
- {
- uint16 dest;
- if( CanEquipItem( slot, dest, item, false, false ) == EQUIP_ERR_OK )
- QuickEquipItem(dest, item);
- else
- success = false;
- }
- else if( IsBankPos( INVENTORY_SLOT_BAG_0, slot ) )
- {
- ItemPosCountVec dest;
- if( CanBankItem( INVENTORY_SLOT_BAG_0, slot, dest, item, false, false ) == EQUIP_ERR_OK )
- item = BankItem(dest, item, true);
- else
- success = false;
- }
-
- if(success)
- {
- // store bags that may contain items in them
- if(item->IsBag() && IsBagPos(item->GetPos()))
- bagMap[item_guid] = (Bag*)item;
- }
- }
- else
- {
- item->SetSlot(NULL_SLOT);
- // the item is in a bag, find the bag
- std::map<uint64, Bag*>::iterator itr = bagMap.find(bag_guid);
- if(itr != bagMap.end())
- itr->second->StoreItem(slot, item, true );
- else
- success = false;
- }
-
- // item's state may have changed after stored
- if (success)
- item->SetState(ITEM_UNCHANGED, this);
- else
- {
- sLog.outError("Player::_LoadInventory: Player %s has item (GUID: %u Entry: %u) can't be loaded to inventory (Bag GUID: %u Slot: %u) by some reason, will send by mail.", GetName(),item_guid, item_id, bag_guid, slot);
- CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
- problematicItems.push_back(item);
- }
- } while (result->NextRow());
-
- delete result;
- m_itemUpdateQueueBlocked = false;
-
- // send by mail problematic items
- while(!problematicItems.empty())
- {
- // fill mail
- MailItemsInfo mi; // item list prepering
-
- for(int i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i)
- {
- Item* item = problematicItems.front();
- problematicItems.pop_front();
-
- mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
- }
-
- std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM);
-
- WorldSession::SendMailTo(this, MAIL_NORMAL, MAIL_STATIONERY_GM, GetGUIDLow(), GetGUIDLow(), subject, 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
- }
- }
- //if(isAlive())
- _ApplyAllItemMods();
-}
-
-// load mailed item which should receive current player
-void Player::_LoadMailedItems(Mail *mail)
-{
- QueryResult* result = CharacterDatabase.PQuery("SELECT item_guid, item_template FROM mail_items WHERE mail_id='%u'", mail->messageID);
- if(!result)
- return;
-
- do
- {
- Field *fields = result->Fetch();
- uint32 item_guid_low = fields[0].GetUInt32();
- uint32 item_template = fields[1].GetUInt32();
-
- mail->AddItem(item_guid_low, item_template);
-
- ItemPrototype const *proto = objmgr.GetItemPrototype(item_template);
-
- if(!proto)
- {
- sLog.outError( "Player %u have unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), item_guid_low, item_template,mail->messageID);
- CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low);
- CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid_low);
- continue;
- }
-
- Item *item = NewItemOrBag(proto);
-
- if(!item->LoadFromDB(item_guid_low, 0))
- {
- sLog.outError( "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, item_guid_low);
- CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low);
- item->FSetState(ITEM_REMOVED);
- item->SaveToDB(); // it also deletes item object !
- continue;
- }
-
- AddMItem(item);
- } while (result->NextRow());
-
- delete result;
-}
-
-void Player::_LoadMailInit(QueryResult *resultUnread, QueryResult *resultDelivery)
-{
- //set a count of unread mails
- //QueryResult *resultMails = CharacterDatabase.PQuery("SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" I64FMTD "'", GUID_LOPART(playerGuid),(uint64)cTime);
- if (resultUnread)
- {
- Field *fieldMail = resultUnread->Fetch();
- unReadMails = fieldMail[0].GetUInt8();
- delete resultUnread;
- }
-
- // store nearest delivery time (it > 0 and if it < current then at next player update SendNewMaill will be called)
- //resultMails = CharacterDatabase.PQuery("SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(playerGuid));
- if (resultDelivery)
- {
- Field *fieldMail = resultDelivery->Fetch();
- m_nextMailDelivereTime = (time_t)fieldMail[0].GetUInt64();
- delete resultDelivery;
- }
-}
-
-void Player::_LoadMail()
-{
- m_mail.clear();
- //mails are in right order 0 1 2 3 4 5 6 7 8 9 10 11 12 13
- QueryResult *result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,subject,itemTextId,has_items,expire_time,deliver_time,money,cod,checked,stationery,mailTemplateId FROM mail WHERE receiver = '%u' ORDER BY id DESC",GetGUIDLow());
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
- Mail *m = new Mail;
- m->messageID = fields[0].GetUInt32();
- m->messageType = fields[1].GetUInt8();
- m->sender = fields[2].GetUInt32();
- m->receiver = fields[3].GetUInt32();
- m->subject = fields[4].GetCppString();
- m->itemTextId = fields[5].GetUInt32();
- bool has_items = fields[6].GetBool();
- m->expire_time = (time_t)fields[7].GetUInt64();
- m->deliver_time = (time_t)fields[8].GetUInt64();
- m->money = fields[9].GetUInt32();
- m->COD = fields[10].GetUInt32();
- m->checked = fields[11].GetUInt32();
- m->stationery = fields[12].GetUInt8();
- m->mailTemplateId = fields[13].GetInt16();
-
- if(m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId))
- {
- sLog.outError( "Player::_LoadMail - Mail (%u) have not existed MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId);
- m->mailTemplateId = 0;
- }
-
- m->state = MAIL_STATE_UNCHANGED;
-
- if (has_items)
- _LoadMailedItems(m);
-
- m_mail.push_back(m);
- } while( result->NextRow() );
- delete result;
- }
- m_mailsLoaded = true;
-}
-
-void Player::LoadPet()
-{
- //fixme: the pet should still be loaded if the player is not in world
- // just not added to the map
- if(IsInWorld())
- {
- Pet *pet = new Pet;
- if(!pet->LoadPetFromDB(this,0,0,true))
- delete pet;
- }
-}
-
-void Player::_LoadQuestStatus(QueryResult *result)
-{
- mQuestStatus.clear();
-
- uint32 slot = 0;
-
- //// 0 1 2 3 4 5 6 7 8 9 10 11 12
- //QueryResult *result = CharacterDatabase.PQuery("SELECT quest, status, rewarded, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3, itemcount4 FROM character_queststatus WHERE guid = '%u'", GetGUIDLow());
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
-
- uint32 quest_id = fields[0].GetUInt32();
- // used to be new, no delete?
- Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
- if( pQuest )
- {
- // find or create
- QuestStatusData& questStatusData = mQuestStatus[quest_id];
-
- uint32 qstatus = fields[1].GetUInt32();
- if(qstatus < MAX_QUEST_STATUS)
- questStatusData.m_status = QuestStatus(qstatus);
- else
- {
- questStatusData.m_status = QUEST_STATUS_NONE;
- sLog.outError("Player %s have invalid quest %d status (%d), replaced by QUEST_STATUS_NONE(0).",GetName(),quest_id,qstatus);
- }
-
- questStatusData.m_rewarded = ( fields[2].GetUInt8() > 0 );
- questStatusData.m_explored = ( fields[3].GetUInt8() > 0 );
-
- time_t quest_time = time_t(fields[4].GetUInt64());
-
- if( pQuest->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) && !GetQuestRewardStatus(quest_id) && questStatusData.m_status != QUEST_STATUS_NONE )
- {
- AddTimedQuest( quest_id );
-
- if (quest_time <= sWorld.GetGameTime())
- questStatusData.m_timer = 1;
- else
- questStatusData.m_timer = (quest_time - sWorld.GetGameTime()) * 1000;
- }
- else
- quest_time = 0;
-
- questStatusData.m_creatureOrGOcount[0] = fields[5].GetUInt32();
- questStatusData.m_creatureOrGOcount[1] = fields[6].GetUInt32();
- questStatusData.m_creatureOrGOcount[2] = fields[7].GetUInt32();
- questStatusData.m_creatureOrGOcount[3] = fields[8].GetUInt32();
- questStatusData.m_itemcount[0] = fields[9].GetUInt32();
- questStatusData.m_itemcount[1] = fields[10].GetUInt32();
- questStatusData.m_itemcount[2] = fields[11].GetUInt32();
- questStatusData.m_itemcount[3] = fields[12].GetUInt32();
-
- questStatusData.uState = QUEST_UNCHANGED;
-
- // add to quest log
- if( slot < MAX_QUEST_LOG_SIZE &&
- ( questStatusData.m_status==QUEST_STATUS_INCOMPLETE ||
- questStatusData.m_status==QUEST_STATUS_COMPLETE && !questStatusData.m_rewarded ) )
- {
- SetQuestSlot(slot,quest_id,quest_time);
-
- if(questStatusData.m_status == QUEST_STATUS_COMPLETE)
- SetQuestSlotState(slot,QUEST_STATE_COMPLETE);
-
- for(uint8 idx = 0; idx < QUEST_OBJECTIVES_COUNT; ++idx)
- if(questStatusData.m_creatureOrGOcount[idx])
- SetQuestSlotCounter(slot,idx,questStatusData.m_creatureOrGOcount[idx]);
-
- ++slot;
- }
-
- if(questStatusData.m_rewarded)
- {
- // learn rewarded spell if unknown
- learnQuestRewardedSpells(pQuest);
-
- // set rewarded title if any
- if(pQuest->GetCharTitleId())
- {
- if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
- SetFlag64(PLAYER__FIELD_KNOWN_TITLES, (uint64(1) << titleEntry->bit_index));
- }
- }
-
- sLog.outDebug("Quest status is {%u} for quest {%u} for player (GUID: %u)", questStatusData.m_status, quest_id, GetGUIDLow());
- }
- }
- while( result->NextRow() );
-
- delete result;
- }
-
- // clear quest log tail
- for ( uint16 i = slot; i < MAX_QUEST_LOG_SIZE; ++i )
- SetQuestSlot(i,0);
-}
-
-void Player::_LoadDailyQuestStatus(QueryResult *result)
-{
- for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
- SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,0);
-
- //QueryResult *result = CharacterDatabase.PQuery("SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GetGUIDLow());
-
- if(result)
- {
- uint32 quest_daily_idx = 0;
-
- do
- {
- if(quest_daily_idx >= PLAYER_MAX_DAILY_QUESTS) // max amount with exist data in query
- {
- sLog.outError("Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`",GetGUIDLow());
- break;
- }
-
- Field *fields = result->Fetch();
-
- uint32 quest_id = fields[0].GetUInt32();
-
- // save _any_ from daily quest times (it must be after last reset anyway)
- m_lastDailyQuestTime = (time_t)fields[1].GetUInt64();
-
- Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
- if( !pQuest )
- continue;
-
- SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,quest_id);
- ++quest_daily_idx;
-
- sLog.outDebug("Daily quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
- }
- while( result->NextRow() );
-
- delete result;
- }
-
- m_DailyQuestChanged = false;
-}
-
-void Player::_LoadReputation(QueryResult *result)
-{
- m_factions.clear();
-
- // Set initial reputations (so everything is nifty before DB data load)
- SetInitialFactions();
-
- //QueryResult *result = CharacterDatabase.PQuery("SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'",GetGUIDLow());
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
-
- FactionEntry const *factionEntry = sFactionStore.LookupEntry(fields[0].GetUInt32());
- if( factionEntry && (factionEntry->reputationListID >= 0))
- {
- FactionState* faction = &m_factions[factionEntry->reputationListID];
-
- // update standing to current
- faction->Standing = int32(fields[1].GetUInt32());
-
- uint32 dbFactionFlags = fields[2].GetUInt32();
-
- if( dbFactionFlags & FACTION_FLAG_VISIBLE )
- SetFactionVisible(faction); // have internal checks for forced invisibility
-
- if( dbFactionFlags & FACTION_FLAG_INACTIVE)
- SetFactionInactive(faction,true); // have internal checks for visibility requirement
-
- if( dbFactionFlags & FACTION_FLAG_AT_WAR ) // DB at war
- SetFactionAtWar(faction,true); // have internal checks for FACTION_FLAG_PEACE_FORCED
- else // DB not at war
- {
- // allow remove if visible (and then not FACTION_FLAG_INVISIBLE_FORCED or FACTION_FLAG_HIDDEN)
- if( faction->Flags & FACTION_FLAG_VISIBLE )
- SetFactionAtWar(faction,false); // have internal checks for FACTION_FLAG_PEACE_FORCED
- }
-
- // set atWar for hostile
- if(GetReputationRank(factionEntry) <= REP_HOSTILE)
- SetFactionAtWar(faction,true);
-
- // reset changed flag if values similar to saved in DB
- if(faction->Flags==dbFactionFlags)
- faction->Changed = false;
- }
- }
- while( result->NextRow() );
-
- delete result;
- }
-}
-
-void Player::_LoadSpells(QueryResult *result)
-{
- for (PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- delete itr->second;
- m_spells.clear();
-
- //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM character_spell WHERE guid = '%u'",GetGUIDLow());
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
-
- addSpell(fields[0].GetUInt16(), fields[2].GetBool(), false, true, fields[1].GetUInt16(), fields[3].GetBool());
- }
- while( result->NextRow() );
-
- delete result;
- }
-}
-
-void Player::_LoadTutorials(QueryResult *result)
-{
- //QueryResult *result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmid);
-
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
-
- for (int iI=0; iI<8; iI++)
- m_Tutorials[iI] = fields[iI].GetUInt32();
- }
- while( result->NextRow() );
-
- delete result;
- }
-
- m_TutorialsChanged = false;
-}
-
-void Player::_LoadGroup(QueryResult *result)
-{
- //QueryResult *result = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", GetGUIDLow());
- if(result)
- {
- uint64 leaderGuid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
- delete result;
- Group* group = objmgr.GetGroupByLeader(leaderGuid);
- if(group)
- {
- uint8 subgroup = group->GetMemberGroup(GetGUID());
- SetGroup(group, subgroup);
- if(getLevel() >= LEVELREQUIREMENT_HEROIC)
- {
- // the group leader may change the instance difficulty while the player is offline
- SetDifficulty(group->GetDifficulty());
- }
- }
- }
-}
-
-void Player::_LoadBoundInstances(QueryResult *result)
-{
- for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
- m_boundInstances[i].clear();
-
- Group *group = GetGroup();
-
- //QueryResult *result = CharacterDatabase.PQuery("SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
- if(result)
- {
- do
- {
- Field *fields = result->Fetch();
- bool perm = fields[1].GetBool();
- uint32 mapId = fields[2].GetUInt32();
- uint32 instanceId = fields[0].GetUInt32();
- uint8 difficulty = fields[3].GetUInt8();
- time_t resetTime = (time_t)fields[4].GetUInt64();
- // the resettime for normal instances is only saved when the InstanceSave is unloaded
- // so the value read from the DB may be wrong here but only if the InstanceSave is loaded
- // and in that case it is not used
-
- if(!perm && group)
- {
- sLog.outError("_LoadBoundInstances: player %s(%d) is in group %d but has a non-permanent character bind to map %d,%d,%d", GetName(), GetGUIDLow(), GUID_LOPART(group->GetLeaderGUID()), mapId, instanceId, difficulty);
- CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'", GetGUIDLow(), instanceId);
- continue;
- }
-
- // since non permanent binds are always solo bind, they can always be reset
- InstanceSave *save = sInstanceSaveManager.AddInstanceSave(mapId, instanceId, difficulty, resetTime, !perm, true);
- if(save) BindToInstance(save, perm, true);
- } while(result->NextRow());
- delete result;
- }
-}
-
-InstancePlayerBind* Player::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;
-}
-
-void Player::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload)
-{
- BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
- UnbindInstance(itr, difficulty, unload);
-}
-
-void Player::UnbindInstance(BoundInstancesMap::iterator &itr, uint8 difficulty, bool unload)
-{
- if(itr != m_boundInstances[difficulty].end())
- {
- if(!unload) CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), itr->second.save->GetInstanceId());
- itr->second.save->RemovePlayer(this); // save can become invalid
- m_boundInstances[difficulty].erase(itr++);
- }
-}
-
-InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent, bool load)
-{
- if(save)
- {
- InstancePlayerBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
- if(bind.save)
- {
- // update the save when the group kills a boss
- if(permanent != bind.perm || save != bind.save)
- if(!load) CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GetGUIDLow(), bind.save->GetInstanceId());
- }
- else
- if(!load) CharacterDatabase.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')", GetGUIDLow(), save->GetInstanceId(), permanent);
-
- if(bind.save != save)
- {
- if(bind.save) bind.save->RemovePlayer(this);
- save->AddPlayer(this);
- }
-
- if(permanent) save->SetCanReset(false);
-
- bind.save = save;
- bind.perm = permanent;
- if(!load) sLog.outDebug("Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", GetName(), GetGUIDLow(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
- return &bind;
- }
- else
- return NULL;
-}
-
-void Player::SendRaidInfo()
-{
- WorldPacket data(SMSG_RAID_INSTANCE_INFO, 4);
-
- uint32 counter = 0, i;
- for(i = 0; i < TOTAL_DIFFICULTIES; i++)
- for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); itr++)
- if(itr->second.perm) counter++;
-
- data << counter;
- for(i = 0; i < TOTAL_DIFFICULTIES; i++)
- {
- for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); itr++)
- {
- if(itr->second.perm)
- {
- InstanceSave *save = itr->second.save;
- data << (save->GetMapId());
- data << (uint32)(save->GetResetTime() - time(NULL));
- data << save->GetInstanceId();
- data << uint32(counter);
- counter--;
- }
- }
- }
- GetSession()->SendPacket(&data);
-}
-
-/*
-- called on every successful teleportation to a map
-*/
-void Player::SendSavedInstances()
-{
- bool hasBeenSaved = false;
- WorldPacket data;
-
- for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
- {
- for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
- {
- if(itr->second.perm) // only permanent binds are sent
- {
- hasBeenSaved = true;
- break;
- }
- }
- }
-
- //Send opcode 811. true or flase means, whether you have current raid/heroic instances
- data.Initialize(SMSG_UPDATE_INSTANCE_OWNERSHIP);
- data << uint32(hasBeenSaved);
- GetSession()->SendPacket(&data);
-
- if(!hasBeenSaved)
- return;
-
- for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
- {
- for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
- {
- if(itr->second.perm)
- {
- data.Initialize(SMSG_UPDATE_LAST_INSTANCE);
- data << uint32(itr->second.save->GetMapId());
- GetSession()->SendPacket(&data);
- }
- }
- }
-}
-
-/// convert the player's binds to the group
-void Player::ConvertInstancesToGroup(Player *player, Group *group, uint64 player_guid)
-{
- bool has_binds = false;
- bool has_solo = false;
-
- if(player) { player_guid = player->GetGUID(); if(!group) group = player->GetGroup(); }
- assert(player_guid);
-
- // copy all binds to the group, when changing leader it's assumed the character
- // will not have any solo binds
-
- if(player)
- {
- for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
- {
- for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();)
- {
- has_binds = true;
- if(group) group->BindToInstance(itr->second.save, itr->second.perm, true);
- // permanent binds are not removed
- if(!itr->second.perm)
- {
- player->UnbindInstance(itr, i, true); // increments itr
- has_solo = true;
- }
- else
- ++itr;
- }
- }
- }
-
- // if the player's not online we don't know what binds it has
- if(!player || !group || has_binds) CharacterDatabase.PExecute("INSERT INTO group_instance SELECT guid, instance, permanent FROM character_instance WHERE guid = '%u'", GUID_LOPART(player_guid));
- // the following should not get executed when changing leaders
- if(!player || has_solo) CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND permanent = 0", GUID_LOPART(player_guid));
-}
-
-bool Player::_LoadHomeBind(QueryResult *result)
-{
- bool ok = false;
- //QueryResult *result = CharacterDatabase.PQuery("SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(playerGuid));
- if (result)
- {
- Field *fields = result->Fetch();
- m_homebindMapId = fields[0].GetUInt32();
- m_homebindZoneId = fields[1].GetUInt16();
- m_homebindX = fields[2].GetFloat();
- m_homebindY = fields[3].GetFloat();
- m_homebindZ = fields[4].GetFloat();
- delete result;
-
- // accept saved data only for valid position (and non instanceable)
- if( MapManager::IsValidMapCoord(m_homebindMapId,m_homebindX,m_homebindY,m_homebindZ) &&
- !sMapStore.LookupEntry(m_homebindMapId)->Instanceable() )
- {
- ok = true;
- }
- else
- CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'", GetGUIDLow());
- }
-
- if(!ok)
- {
- PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass());
- if(!info) return false;
-
- m_homebindMapId = info->mapId;
- m_homebindZoneId = info->zoneId;
- m_homebindX = info->positionX;
- m_homebindY = info->positionY;
- m_homebindZ = info->positionZ;
-
- CharacterDatabase.PExecute("INSERT INTO character_homebind (guid,map,zone,position_x,position_y,position_z) VALUES ('%u', '%u', '%u', '%f', '%f', '%f')", GetGUIDLow(), m_homebindMapId, (uint32)m_homebindZoneId, m_homebindX, m_homebindY, m_homebindZ);
- }
-
- DEBUG_LOG("Setting player home position: mapid is: %u, zoneid is %u, X is %f, Y is %f, Z is %f\n",
- m_homebindMapId, m_homebindZoneId, m_homebindX, m_homebindY, m_homebindZ);
-
- return true;
-}
-
-/*********************************************************/
-/*** SAVE SYSTEM ***/
-/*********************************************************/
-
-void Player::SaveToDB()
-{
- // delay auto save at any saves (manual, in code, or autosave)
- m_nextSave = sWorld.getConfig(CONFIG_INTERVAL_SAVE);
-
- // first save/honor gain after midnight will also update the player's honor fields
- UpdateHonorFields();
-
- // players aren't saved on battleground maps
- uint32 mapid = IsBeingTeleported() ? GetTeleportDest().mapid : GetMapId();
- const MapEntry * me = sMapStore.LookupEntry(mapid);
- if(!me || me->IsBattleGroundOrArena())
- return;
-
- int is_save_resting = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0;
- //save, far from tavern/city
- //save, but in tavern/city
- sLog.outDebug("The value of player %s at save: ", m_name.c_str());
- outDebugValues();
-
- // save state (after auras removing), if aura remove some flags then it must set it back by self)
- uint32 tmp_bytes = GetUInt32Value(UNIT_FIELD_BYTES_1);
- uint32 tmp_bytes2 = GetUInt32Value(UNIT_FIELD_BYTES_2);
- uint32 tmp_flags = GetUInt32Value(UNIT_FIELD_FLAGS);
- uint32 tmp_pflags = GetUInt32Value(PLAYER_FLAGS);
- uint32 tmp_displayid = GetDisplayId();
-
- // Set player sit state to standing on save, also stealth and shifted form
- SetByteValue(UNIT_FIELD_BYTES_1, 0, 0); // stand state
- SetByteValue(UNIT_FIELD_BYTES_2, 3, 0); // shapeshift
- SetByteValue(UNIT_FIELD_BYTES_1, 3, 0); // stand flags?
- RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
- SetDisplayId(GetNativeDisplayId());
-
- bool inworld = IsInWorld();
-
- CharacterDatabase.BeginTransaction();
-
- CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",GetGUIDLow());
-
- std::string sql_name = m_name;
- CharacterDatabase.escape_string(sql_name);
-
- std::ostringstream ss;
- ss << "INSERT INTO characters (guid,account,name,race,class,"
- "map, dungeon_difficulty, position_x, position_y, position_z, orientation, data, "
- "taximask, online, 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, "
- "death_expire_time, taxi_path) VALUES ("
- << GetGUIDLow() << ", "
- << GetSession()->GetAccountId() << ", '"
- << sql_name << "', "
- << m_race << ", "
- << m_class << ", ";
-
- bool save_to_dest = false;
- if(IsBeingTeleported())
- {
- // don't save to battlegrounds or arenas
- const MapEntry *entry = sMapStore.LookupEntry(GetTeleportDest().mapid);
- if(entry && entry->map_type != MAP_BATTLEGROUND && entry->map_type != MAP_ARENA)
- save_to_dest = true;
- }
-
- if(!save_to_dest)
- {
- ss << GetMapId() << ", "
- << (uint32)GetDifficulty() << ", "
- << finiteAlways(GetPositionX()) << ", "
- << finiteAlways(GetPositionY()) << ", "
- << finiteAlways(GetPositionZ()) << ", "
- << finiteAlways(GetOrientation()) << ", '";
- }
- else
- {
- ss << GetTeleportDest().mapid << ", "
- << (uint32)GetDifficulty() << ", "
- << finiteAlways(GetTeleportDest().x) << ", "
- << finiteAlways(GetTeleportDest().y) << ", "
- << finiteAlways(GetTeleportDest().z) << ", "
- << finiteAlways(GetTeleportDest().o) << ", '";
- }
-
- uint16 i;
- for( i = 0; i < m_valuesCount; i++ )
- {
- ss << GetUInt32Value(i) << " ";
- }
-
- ss << "', '";
-
- for( i = 0; i < 8; i++ )
- ss << m_taxi.GetTaximask(i) << " ";
-
- ss << "', ";
- ss << (inworld ? 1 : 0);
-
- ss << ", ";
- ss << m_cinematic;
-
- ss << ", ";
- ss << m_Played_time[0];
- ss << ", ";
- ss << m_Played_time[1];
-
- ss << ", ";
- ss << finiteAlways(m_rest_bonus);
- ss << ", ";
- ss << (uint64)time(NULL);
- ss << ", ";
- ss << is_save_resting;
- ss << ", ";
- ss << m_resetTalentsCost;
- ss << ", ";
- ss << (uint64)m_resetTalentsTime;
-
- ss << ", ";
- ss << finiteAlways(m_movementInfo.t_x);
- ss << ", ";
- ss << finiteAlways(m_movementInfo.t_y);
- ss << ", ";
- ss << finiteAlways(m_movementInfo.t_z);
- ss << ", ";
- ss << finiteAlways(m_movementInfo.t_o);
- ss << ", ";
- if (m_transport)
- ss << m_transport->GetGUIDLow();
- else
- ss << "0";
-
- ss << ", ";
- ss << m_ExtraFlags;
-
- ss << ", ";
- ss << uint32(m_stableSlots); // to prevent save uint8 as char
-
- ss << ", ";
- ss << uint32(m_atLoginFlags);
-
- ss << ", ";
- ss << GetZoneId();
-
- ss << ", ";
- ss << (uint64)m_deathExpireTime;
-
- ss << ", '";
- ss << m_taxi.SaveTaxiDestinationsToString();
- ss << "' )";
-
- CharacterDatabase.Execute( ss.str().c_str() );
-
- if(m_mailsUpdated) //save mails only when needed
- _SaveMail();
-
- _SaveInventory();
- _SaveQuestStatus();
- _SaveDailyQuestStatus();
- _SaveTutorials();
- _SaveSpells();
- _SaveSpellCooldowns();
- _SaveActions();
- _SaveAuras();
- _SaveReputation();
-
- CharacterDatabase.CommitTransaction();
-
- // restore state (before aura apply, if aura remove flag then aura must set it ack by self)
- SetDisplayId(tmp_displayid);
- SetUInt32Value(UNIT_FIELD_BYTES_1, tmp_bytes);
- SetUInt32Value(UNIT_FIELD_BYTES_2, tmp_bytes2);
- SetUInt32Value(UNIT_FIELD_FLAGS, tmp_flags);
- SetUInt32Value(PLAYER_FLAGS, tmp_pflags);
-
- // save pet (hunter pet level and experience and all type pets health/mana).
- if(Pet* pet = GetPet())
- pet->SavePetToDB(PET_SAVE_AS_CURRENT);
-}
-
-// fast save function for item/money cheating preventing - save only inventory and money state
-void Player::SaveInventoryAndGoldToDB()
-{
- _SaveInventory();
- SetUInt32ValueInDB(PLAYER_FIELD_COINAGE,GetMoney(),GetGUID());
-}
-
-void Player::_SaveActions()
-{
- for(ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end(); )
- {
- switch (itr->second.uState)
- {
- case ACTIONBUTTON_NEW:
- CharacterDatabase.PExecute("INSERT INTO character_action (guid,button,action,type,misc) VALUES ('%u', '%u', '%u', '%u', '%u')",
- GetGUIDLow(), (uint32)itr->first, (uint32)itr->second.action, (uint32)itr->second.type, (uint32)itr->second.misc );
- itr->second.uState = ACTIONBUTTON_UNCHANGED;
- ++itr;
- break;
- case ACTIONBUTTON_CHANGED:
- CharacterDatabase.PExecute("UPDATE character_action SET action = '%u', type = '%u', misc= '%u' WHERE guid= '%u' AND button= '%u' ",
- (uint32)itr->second.action, (uint32)itr->second.type, (uint32)itr->second.misc, GetGUIDLow(), (uint32)itr->first );
- itr->second.uState = ACTIONBUTTON_UNCHANGED;
- ++itr;
- break;
- case ACTIONBUTTON_DELETED:
- CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u' and button = '%u'", GetGUIDLow(), (uint32)itr->first );
- m_actionButtons.erase(itr++);
- break;
- default:
- ++itr;
- break;
- };
- }
-}
-
-void Player::_SaveAuras()
-{
- CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",GetGUIDLow());
-
- AuraMap const& auras = GetAuras();
- for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
- {
- SpellEntry const *spellInfo = itr->second->GetSpellProto();
-
- //skip all auras from spells that are passive or need a shapeshift
- if (itr->second->IsPassive() || itr->second->IsRemovedOnShapeLost())
- continue;
-
- //do not save single target auras (unless they were cast by the player)
- if (itr->second->GetCasterGUID() != GetGUID() && IsSingleTargetSpell(spellInfo))
- continue;
-
- uint8 i;
- // or apply at cast SPELL_AURA_MOD_SHAPESHIFT or SPELL_AURA_MOD_STEALTH auras
- for (i = 0; i < 3; i++)
- if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT ||
- spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH)
- break;
-
- if (i == 3)
- {
- CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u' and spell = '%u' and effect_index= '%u'",GetGUIDLow(),(uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex());
- CharacterDatabase.PExecute("INSERT INTO character_aura (guid,caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges) "
- "VALUES ('%u', '" I64FMTD "' ,'%u', '%u', '%d', '%d', '%d', '%d')",
- GetGUIDLow(), itr->second->GetCasterGUID(), (uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex(), (*itr).second->GetModifier()->m_amount,int((*itr).second->GetAuraMaxDuration()),int((*itr).second->GetAuraDuration()),int((*itr).second->m_procCharges));
- }
- }
-}
-
-void Player::_SaveInventory()
-{
- // force items in buyback slots to new state
- // and remove those that aren't already
- for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; i++)
- {
- Item *item = m_items[i];
- if (!item || item->GetState() == ITEM_NEW) continue;
- CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item->GetGUIDLow());
- CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item->GetGUIDLow());
- m_items[i]->FSetState(ITEM_NEW);
- }
-
- // update enchantment durations
- for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();++itr)
- {
- itr->item->SetEnchantmentDuration(itr->slot,itr->leftduration);
- }
-
- // if no changes
- if (m_itemUpdateQueue.empty()) return;
-
- // do not save if the update queue is corrupt
- bool error = false;
- for(size_t i = 0; i < m_itemUpdateQueue.size(); i++)
- {
- Item *item = m_itemUpdateQueue[i];
- if(!item || item->GetState() == ITEM_REMOVED) continue;
- Item *test = GetItemByPos( item->GetBagSlot(), item->GetSlot());
-
- if (test == NULL)
- {
- sLog.outError("Player(GUID: %u Name: %s)::_SaveInventory - the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the player doesn't have an item at that position!", GetGUIDLow(), GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow());
- error = true;
- }
- else if (test != item)
- {
- sLog.outError("Player(GUID: %u Name: %s)::_SaveInventory - the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the item with guid %d is there instead!", GetGUIDLow(), GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), test->GetGUIDLow());
- error = true;
- }
- }
-
- if (error)
- {
- sLog.outError("Player::_SaveInventory - one or more errors occurred save aborted!");
- ChatHandler(this).SendSysMessage(LANG_ITEM_SAVE_FAILED);
- return;
- }
-
- for(size_t i = 0; i < m_itemUpdateQueue.size(); i++)
- {
- Item *item = m_itemUpdateQueue[i];
- if(!item) continue;
-
- Bag *container = item->GetContainer();
- uint32 bag_guid = container ? container->GetGUIDLow() : 0;
-
- switch(item->GetState())
- {
- case ITEM_NEW:
- CharacterDatabase.PExecute("INSERT INTO character_inventory (guid,bag,slot,item,item_template) VALUES ('%u', '%u', '%u', '%u', '%u')", GetGUIDLow(), bag_guid, item->GetSlot(), item->GetGUIDLow(), item->GetEntry());
- break;
- case ITEM_CHANGED:
- CharacterDatabase.PExecute("UPDATE character_inventory SET guid='%u', bag='%u', slot='%u', item_template='%u' WHERE item='%u'", GetGUIDLow(), bag_guid, item->GetSlot(), item->GetEntry(), item->GetGUIDLow());
- break;
- case ITEM_REMOVED:
- CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item->GetGUIDLow());
- break;
- case ITEM_UNCHANGED:
- break;
- }
-
- item->SaveToDB(); // item have unchanged inventory record and can be save standalone
- }
- m_itemUpdateQueue.clear();
-}
-
-void Player::_SaveMail()
-{
- if (!m_mailsLoaded)
- return;
-
- for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); itr++)
- {
- Mail *m = (*itr);
- if (m->state == MAIL_STATE_CHANGED)
- {
- CharacterDatabase.PExecute("UPDATE mail SET itemTextId = '%u',has_items = '%u',expire_time = '" I64FMTD "', deliver_time = '" I64FMTD "',money = '%u',cod = '%u',checked = '%u' WHERE id = '%u'",
- m->itemTextId, m->HasItems() ? 1 : 0, (uint64)m->expire_time, (uint64)m->deliver_time, m->money, m->COD, m->checked, m->messageID);
- if(m->removedItems.size())
- {
- for(std::vector<uint32>::iterator itr2 = m->removedItems.begin(); itr2 != m->removedItems.end(); ++itr2)
- CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", *itr2);
- m->removedItems.clear();
- }
- m->state = MAIL_STATE_UNCHANGED;
- }
- else if (m->state == MAIL_STATE_DELETED)
- {
- if (m->HasItems())
- for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
- CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
- if (m->itemTextId)
- CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", m->itemTextId);
- CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
- CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", m->messageID);
- }
- }
-
- //deallocate deleted mails...
- for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); )
- {
- if ((*itr)->state == MAIL_STATE_DELETED)
- {
- Mail* m = *itr;
- m_mail.erase(itr);
- delete m;
- itr = m_mail.begin();
- }
- else
- ++itr;
- }
-
- m_mailsUpdated = false;
-}
-
-void Player::_SaveQuestStatus()
-{
- // we don't need transactions here.
- for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
- {
- switch (i->second.uState)
- {
- case QUEST_NEW :
- CharacterDatabase.PExecute("INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4) "
- "VALUES ('%u', '%u', '%u', '%u', '%u', '" I64FMTD "', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
- GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]);
- break;
- case QUEST_CHANGED :
- CharacterDatabase.PExecute("UPDATE character_queststatus SET status = '%u',rewarded = '%u',explored = '%u',timer = '" I64FMTD "',mobcount1 = '%u',mobcount2 = '%u',mobcount3 = '%u',mobcount4 = '%u',itemcount1 = '%u',itemcount2 = '%u',itemcount3 = '%u',itemcount4 = '%u' WHERE guid = '%u' AND quest = '%u' ",
- i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first );
- break;
- case QUEST_UNCHANGED:
- break;
- };
- i->second.uState = QUEST_UNCHANGED;
- }
-}
-
-void Player::_SaveDailyQuestStatus()
-{
- if(!m_DailyQuestChanged)
- return;
-
- m_DailyQuestChanged = false;
-
- // save last daily quest time for all quests: we need only mostly reset time for reset check anyway
-
- // we don't need transactions here.
- CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'",GetGUIDLow());
- for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
- if(GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
- CharacterDatabase.PExecute("INSERT INTO character_queststatus_daily (guid,quest,time) VALUES ('%u', '%u','" I64FMTD "')",
- GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx),uint64(m_lastDailyQuestTime));
-}
-
-void Player::_SaveReputation()
-{
- for(FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
- {
- if (itr->second.Changed)
- {
- CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u' AND faction='%u'", GetGUIDLow(), itr->second.ID);
- CharacterDatabase.PExecute("INSERT INTO character_reputation (guid,faction,standing,flags) VALUES ('%u', '%u', '%i', '%u')", GetGUIDLow(), itr->second.ID, itr->second.Standing, itr->second.Flags);
- itr->second.Changed = false;
- }
- }
-}
-
-void Player::_SaveSpells()
-{
- for (PlayerSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
- {
- ++next;
- if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
- CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first);
- if (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED)
- CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,slot,active,disabled) VALUES ('%u', '%u', '%u','%u','%u')", GetGUIDLow(), itr->first, itr->second->slotId,itr->second->active ? 1 : 0,itr->second->disabled ? 1 : 0);
-
- if (itr->second->state == PLAYERSPELL_REMOVED)
- _removeSpell(itr->first);
- else
- itr->second->state = PLAYERSPELL_UNCHANGED;
- }
-}
-
-void Player::_SaveTutorials()
-{
- if(!m_TutorialsChanged)
- return;
-
- uint32 Rows=0;
- // it's better than rebuilding indexes multiple times
- QueryResult *result = CharacterDatabase.PQuery("SELECT count(*) AS r FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetSession()->GetAccountId(), realmID );
- if(result)
- {
- Rows = result->Fetch()[0].GetUInt32();
- delete result;
- }
-
- if (Rows)
- {
- CharacterDatabase.PExecute("UPDATE character_tutorial SET tut0='%u', tut1='%u', tut2='%u', tut3='%u', tut4='%u', tut5='%u', tut6='%u', tut7='%u' WHERE account = '%u' AND realmid = '%u'",
- m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7], GetSession()->GetAccountId(), realmID );
- }
- else
- {
- CharacterDatabase.PExecute("INSERT INTO character_tutorial (account,realmid,tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7) VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", GetSession()->GetAccountId(), realmID, m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7]);
- };
-
- m_TutorialsChanged = false;
-}
-
-void Player::outDebugValues() const
-{
- if(!sLog.IsOutDebug()) // optimize disabled debug output
- return;
-
- sLog.outDebug("HP is: \t\t\t%u\t\tMP is: \t\t\t%u",GetMaxHealth(), GetMaxPower(POWER_MANA));
- sLog.outDebug("AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f",GetStat(STAT_AGILITY), GetStat(STAT_STRENGTH));
- sLog.outDebug("INTELLECT is: \t\t%f\t\tSPIRIT is: \t\t%f",GetStat(STAT_INTELLECT), GetStat(STAT_SPIRIT));
- sLog.outDebug("STAMINA is: \t\t%f\t\tSPIRIT is: \t\t%f",GetStat(STAT_STAMINA), GetStat(STAT_SPIRIT));
- sLog.outDebug("Armor is: \t\t%u\t\tBlock is: \t\t%f",GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE));
- sLog.outDebug("HolyRes is: \t\t%u\t\tFireRes is: \t\t%u",GetResistance(SPELL_SCHOOL_HOLY), GetResistance(SPELL_SCHOOL_FIRE));
- sLog.outDebug("NatureRes is: \t\t%u\t\tFrostRes is: \t\t%u",GetResistance(SPELL_SCHOOL_NATURE), GetResistance(SPELL_SCHOOL_FROST));
- sLog.outDebug("ShadowRes is: \t\t%u\t\tArcaneRes is: \t\t%u",GetResistance(SPELL_SCHOOL_SHADOW), GetResistance(SPELL_SCHOOL_ARCANE));
- sLog.outDebug("MIN_DAMAGE is: \t\t%f\tMAX_DAMAGE is: \t\t%f",GetFloatValue(UNIT_FIELD_MINDAMAGE), GetFloatValue(UNIT_FIELD_MAXDAMAGE));
- sLog.outDebug("MIN_OFFHAND_DAMAGE is: \t%f\tMAX_OFFHAND_DAMAGE is: \t%f",GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE), GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE));
- sLog.outDebug("MIN_RANGED_DAMAGE is: \t%f\tMAX_RANGED_DAMAGE is: \t%f",GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE), GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE));
- sLog.outDebug("ATTACK_TIME is: \t%u\t\tRANGE_ATTACK_TIME is: \t%u",GetAttackTime(BASE_ATTACK), GetAttackTime(RANGED_ATTACK));
-}
-
-/*********************************************************/
-/*** FLOOD FILTER SYSTEM ***/
-/*********************************************************/
-
-void Player::UpdateSpeakTime()
-{
- // ignore chat spam protection for GMs in any mode
- if(GetSession()->GetSecurity() > SEC_PLAYER)
- return;
-
- time_t current = time (NULL);
- if(m_speakTime > current)
- {
- uint32 max_count = sWorld.getConfig(CONFIG_CHATFLOOD_MESSAGE_COUNT);
- if(!max_count)
- return;
-
- ++m_speakCount;
- if(m_speakCount >= max_count)
- {
- // prevent overwrite mute time, if message send just before mutes set, for example.
- time_t new_mute = current + sWorld.getConfig(CONFIG_CHATFLOOD_MUTE_TIME);
- if(GetSession()->m_muteTime < new_mute)
- GetSession()->m_muteTime = new_mute;
-
- m_speakCount = 0;
- }
- }
- else
- m_speakCount = 0;
-
- m_speakTime = current + sWorld.getConfig(CONFIG_CHATFLOOD_MESSAGE_DELAY);
-}
-
-bool Player::CanSpeak() const
-{
- return GetSession()->m_muteTime <= time (NULL);
-}
-
-/*********************************************************/
-/*** LOW LEVEL FUNCTIONS:Notifiers ***/
-/*********************************************************/
-
-void Player::SendAttackSwingNotInRange()
-{
- WorldPacket data(SMSG_ATTACKSWING_NOTINRANGE, 0);
- GetSession()->SendPacket( &data );
-}
-
-void Player::SavePositionInDB(uint32 mapid, float x,float y,float z,float o,uint32 zone,uint64 guid)
-{
- std::ostringstream ss;
- ss << "UPDATE characters SET position_x='"<<x<<"',position_y='"<<y
- << "',position_z='"<<z<<"',orientation='"<<o<<"',map='"<<mapid
- << "',zone='"<<zone<<"',trans_x='0',trans_y='0',trans_z='0',"
- << "transguid='0',taxi_path='' WHERE guid='"<< GUID_LOPART(guid) <<"'";
- sLog.outDebug(ss.str().c_str());
- CharacterDatabase.Execute(ss.str().c_str());
-}
-
-bool Player::SaveValuesArrayInDB(Tokens const& tokens, uint64 guid)
-{
- std::ostringstream ss2;
- ss2<<"UPDATE characters SET data='";
- int i=0;
- for (Tokens::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter, ++i)
- {
- ss2<<tokens[i]<<" ";
- }
- ss2<<"' WHERE guid='"<< GUID_LOPART(guid) <<"'";
-
- return CharacterDatabase.Execute(ss2.str().c_str());
-}
-
-void Player::SetUInt32ValueInArray(Tokens& tokens,uint16 index, uint32 value)
-{
- char buf[11];
- snprintf(buf,11,"%u",value);
-
- if(index >= tokens.size())
- return;
-
- tokens[index] = buf;
-}
-
-void Player::SetUInt32ValueInDB(uint16 index, uint32 value, uint64 guid)
-{
- Tokens tokens;
- if(!LoadValuesArrayFromDB(tokens,guid))
- return;
-
- if(index >= tokens.size())
- return;
-
- char buf[11];
- snprintf(buf,11,"%u",value);
- tokens[index] = buf;
-
- SaveValuesArrayInDB(tokens,guid);
-}
-
-void Player::SetFloatValueInDB(uint16 index, float value, uint64 guid)
-{
- uint32 temp;
- memcpy(&temp, &value, sizeof(value));
- Player::SetUInt32ValueInDB(index, temp, guid);
-}
-
-void Player::SendAttackSwingNotStanding()
-{
- WorldPacket data(SMSG_ATTACKSWING_NOTSTANDING, 0);
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendAttackSwingDeadTarget()
-{
- WorldPacket data(SMSG_ATTACKSWING_DEADTARGET, 0);
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendAttackSwingCantAttack()
-{
- WorldPacket data(SMSG_ATTACKSWING_CANT_ATTACK, 0);
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendAttackSwingCancelAttack()
-{
- WorldPacket data(SMSG_CANCEL_COMBAT, 0);
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendAttackSwingBadFacingAttack()
-{
- WorldPacket data(SMSG_ATTACKSWING_BADFACING, 0);
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendAutoRepeatCancel()
-{
- WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, 0);
- GetSession()->SendPacket( &data );
-}
-
-void Player::SendExplorationExperience(uint32 Area, uint32 Experience)
-{
- WorldPacket data( SMSG_EXPLORATION_EXPERIENCE, 8 );
- data << Area;
- data << Experience;
- GetSession()->SendPacket(&data);
-}
-
-void Player::SendDungeonDifficulty(bool IsInGroup)
-{
- uint8 val = 0x00000001;
- WorldPacket data(MSG_SET_DUNGEON_DIFFICULTY, 12);
- data << (uint32)GetDifficulty();
- data << uint32(val);
- data << uint32(IsInGroup);
- GetSession()->SendPacket(&data);
-}
-
-void Player::SendResetFailedNotify(uint32 mapid)
-{
- WorldPacket data(SMSG_RESET_FAILED_NOTIFY, 4);
- data << uint32(mapid);
- GetSession()->SendPacket(&data);
-}
-
-/// Reset all solo instances and optionally send a message on success for each
-void Player::ResetInstances(uint8 method)
-{
- // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN
-
- // 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())
- {
- ++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;
- }
- }
-
- // if the map is loaded, reset it
- Map *map = MapManager::Instance().FindMap(p->GetMapId(), p->GetInstanceId());
- if(map && map->IsDungeon())
- ((InstanceMap*)map)->Reset(method);
-
- // since this is a solo instance there should not be any players inside
- if(method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
- SendResetInstanceSuccess(p->GetMapId());
-
- p->DeleteFromDB();
- m_boundInstances[dif].erase(itr++);
-
- // the following should remove the instance save from the manager and delete it as well
- p->RemovePlayer(this);
- }
-}
-
-void Player::SendResetInstanceSuccess(uint32 MapId)
-{
- WorldPacket data(SMSG_INSTANCE_RESET, 4);
- data << MapId;
- GetSession()->SendPacket(&data);
-}
-
-void Player::SendResetInstanceFailed(uint32 reason, uint32 MapId)
-{
- // TODO: find what other fail reasons there are besides players in the instance
- WorldPacket data(SMSG_INSTANCE_RESET_FAILED, 4);
- data << reason;
- data << MapId;
- GetSession()->SendPacket(&data);
-}
-
-/*********************************************************/
-/*** Update timers ***/
-/*********************************************************/
-
-///checks the 15 afk reports per 5 minutes limit
-void Player::UpdateAfkReport(time_t currTime)
-{
- if(m_bgAfkReportedTimer <= currTime)
- {
- m_bgAfkReportedCount = 0;
- m_bgAfkReportedTimer = currTime+5*MINUTE;
- }
-}
-
-void Player::UpdateContestedPvP(uint32 diff)
-{
- if(!m_contestedPvPTimer||isInCombat())
- return;
- if(m_contestedPvPTimer <= diff)
- {
- ResetContestedPvP();
- }
- else
- m_contestedPvPTimer -= diff;
-}
-
-void Player::UpdatePvPFlag(time_t currTime)
-{
- if(!IsPvP())
- return;
- if(pvpInfo.endTimer == 0 || currTime < (pvpInfo.endTimer + 300))
- return;
-
- UpdatePvP(false);
-}
-
-void Player::UpdateDuelFlag(time_t currTime)
-{
- if(!duel || duel->startTimer == 0 ||currTime < duel->startTimer + 3)
- return;
-
- SetUInt32Value(PLAYER_DUEL_TEAM, 1);
- duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 2);
-
- duel->startTimer = 0;
- duel->startTime = currTime;
- duel->opponent->duel->startTimer = 0;
- duel->opponent->duel->startTime = currTime;
-}
-
-void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
-{
- if(!pet)
- pet = GetPet();
-
- if(returnreagent && (pet || m_temporaryUnsummonedPetNumber))
- {
- //returning of reagents only for players, so best done here
- uint32 spellId = pet ? pet->GetUInt32Value(UNIT_CREATED_BY_SPELL) : m_oldpetspell;
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
-
- if(spellInfo)
- {
- for(uint32 i = 0; i < 7; ++i)
- {
- if(spellInfo->Reagent[i] > 0)
- {
- ItemPosCountVec dest; //for succubus, voidwalker, felhunter and felguard credit soulshard when despawn reason other than death (out of range, logout)
- uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->Reagent[i], spellInfo->ReagentCount[i] );
- if( msg == EQUIP_ERR_OK )
- {
- Item* item = StoreNewItem( dest, spellInfo->Reagent[i], true);
- if(IsInWorld())
- SendNewItem(item,spellInfo->ReagentCount[i],true,false);
- }
- }
- }
- }
- m_temporaryUnsummonedPetNumber = 0;
- }
-
- if(!pet || pet->GetOwnerGUID()!=GetGUID())
- return;
-
- // only if current pet in slot
- switch(pet->getPetType())
- {
- case MINI_PET:
- m_miniPet = 0;
- break;
- case GUARDIAN_PET:
- m_guardianPets.erase(pet->GetGUID());
- break;
- default:
- if(GetPetGUID()==pet->GetGUID())
- SetPet(0);
- break;
- }
-
- pet->CombatStop();
-
- if(returnreagent)
- {
- switch(pet->GetEntry())
- {
- //warlock pets except imp are removed(?) when logging out
- case 1860:
- case 1863:
- case 417:
- case 17252:
- mode = PET_SAVE_NOT_IN_SLOT;
- break;
- }
- }
-
- pet->SavePetToDB(mode);
-
- pet->CleanupsBeforeDelete();
- pet->AddObjectToRemoveList();
- pet->m_removed = true;
-
- if(pet->isControlled())
- {
- WorldPacket data(SMSG_PET_SPELLS, 8);
- data << uint64(0);
- GetSession()->SendPacket(&data);
-
- if(GetGroup())
- SetGroupUpdateFlag(GROUP_UPDATE_PET);
- }
-}
-
-
-void Player::RemoveMiniPet()
-{
- if(Pet* pet = GetMiniPet())
- {
- pet->Remove(PET_SAVE_AS_DELETED);
- m_miniPet = 0;
- }
-}
-
-Pet* Player::GetMiniPet()
-{
- if(!m_miniPet)
- return NULL;
- return ObjectAccessor::GetPet(m_miniPet);
-}
-
-void Player::RemoveGuardians()
-{
- while(!m_guardianPets.empty())
- {
- uint64 guid = *m_guardianPets.begin();
- if(Pet* pet = ObjectAccessor::GetPet(guid))
- pet->Remove(PET_SAVE_AS_DELETED);
-
- m_guardianPets.erase(guid);
- }
-}
-
-bool Player::HasGuardianWithEntry(uint32 entry)
-{
- // pet guid middle part is entry (and creature also)
- // and in guardian list must be guardians with same entry _always_
- for(GuardianPetList::const_iterator itr = m_guardianPets.begin(); itr != m_guardianPets.end(); ++itr)
- if(GUID_ENPART(*itr)==entry)
- return true;
-
- return false;
-}
-
-void Player::Uncharm()
-{
- Unit* charm = GetCharm();
- if(!charm)
- return;
-
- charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_CHARM);
- charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS);
-}
-
-void Player::BuildPlayerChat(WorldPacket *data, uint8 msgtype, std::string text, uint32 language) const
-{
- *data << (uint8)msgtype;
- *data << (uint32)language;
- *data << (uint64)GetGUID();
- *data << (uint32)language; //language 2.1.0 ?
- *data << (uint64)GetGUID();
- *data << (uint32)(text.length()+1);
- *data << text;
- *data << (uint8)chatTag();
-}
-
-void Player::Say(const std::string text, const uint32 language)
-{
- WorldPacket data(SMSG_MESSAGECHAT, 200);
- BuildPlayerChat(&data, CHAT_MSG_SAY, text, language);
- SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),true);
-}
-
-void Player::Yell(const std::string text, const uint32 language)
-{
- WorldPacket data(SMSG_MESSAGECHAT, 200);
- BuildPlayerChat(&data, CHAT_MSG_YELL, text, language);
- SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL),true);
-}
-
-void Player::TextEmote(const std::string text)
-{
- WorldPacket data(SMSG_MESSAGECHAT, 200);
- BuildPlayerChat(&data, CHAT_MSG_EMOTE, text, LANG_UNIVERSAL);
- SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true, !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) );
-}
-
-void Player::Whisper(std::string text, uint32 language,uint64 receiver)
-{
- if (language != LANG_ADDON) // if not addon data
- language = LANG_UNIVERSAL; // whispers should always be readable
-
- Player *rPlayer = objmgr.GetPlayer(receiver);
-
- // when player you are whispering to is dnd, he cannot receive your message, unless you are in gm mode
- if(!rPlayer->isDND() || isGameMaster())
- {
- WorldPacket data(SMSG_MESSAGECHAT, 200);
- BuildPlayerChat(&data, CHAT_MSG_WHISPER, text, language);
- rPlayer->GetSession()->SendPacket(&data);
-
- data.Initialize(SMSG_MESSAGECHAT, 200);
- rPlayer->BuildPlayerChat(&data, CHAT_MSG_REPLY, text, language);
- GetSession()->SendPacket(&data);
- }
- else
- {
- // announce to player that player he is whispering to is dnd and cannot receive his message
- ChatHandler(this).PSendSysMessage(LANG_PLAYER_DND, rPlayer->GetName(), rPlayer->dndMsg.c_str());
- }
-
- if(!isAcceptWhispers())
- {
- SetAcceptWhispers(true);
- ChatHandler(this).SendSysMessage(LANG_COMMAND_WHISPERON);
- }
-
- // announce to player that player he is whispering to is afk
- if(rPlayer->isAFK())
- ChatHandler(this).PSendSysMessage(LANG_PLAYER_AFK, rPlayer->GetName(), rPlayer->afkMsg.c_str());
-
- // if player whisper someone, auto turn of dnd to be able to receive an answer
- if(isDND() && !rPlayer->isGameMaster())
- ToggleDND();
-}
-
-void Player::PetSpellInitialize()
-{
- Pet* pet = GetPet();
-
- if(pet)
- {
- uint8 addlist = 0;
-
- sLog.outDebug("Pet Spells Groups");
-
- CreatureInfo const *cinfo = pet->GetCreatureInfo();
-
- if(pet->isControlled() && (pet->getPetType() == HUNTER_PET || cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK))
- {
- for(PetSpellMap::iterator itr = pet->m_spells.begin();itr != pet->m_spells.end();itr++)
- {
- if(itr->second->state == PETSPELL_REMOVED)
- continue;
- ++addlist;
- }
- }
-
- // first line + actionbar + spellcount + spells + last adds
- WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);
-
- CharmInfo *charmInfo = pet->GetCharmInfo();
-
- //16
- data << (uint64)pet->GetGUID() << uint32(0x00000000) << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0);
-
- for(uint32 i = 0; i < 10; i++) //40
- {
- data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
- }
-
- data << uint8(addlist); //1
-
- if(addlist && pet->isControlled())
- {
- for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
- {
- if(itr->second->state == PETSPELL_REMOVED)
- continue;
-
- data << uint16(itr->first);
- data << uint16(itr->second->active); // pet spell active state isn't boolean
- }
- }
-
- //data << uint8(0x01) << uint32(0x6010) << uint32(0x01) << uint32(0x05) << uint16(0x00); //15
- uint8 count = 3; //1+8+8+8=25
-
- // if count = 0, then end of packet...
- data << count;
- // uint32 value is spell id...
- // uint64 value is constant 0, unknown...
- data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3
- //data << uint32(0x5fd1) << uint64(0); // if count = 2
- data << uint32(0x8e8c) << uint64(0); // if count = 3
- data << uint32(0x8e8b) << uint64(0); // if count = 3
-
- GetSession()->SendPacket(&data);
- }
-}
-
-void Player::PossessSpellInitialize()
-{
- Unit* charm = GetCharm();
-
- if(!charm)
- return;
-
- CharmInfo *charmInfo = charm->GetCharmInfo();
-
- if(!charmInfo)
- {
- sLog.outError("Player::PossessSpellInitialize(): charm ("I64FMTD") has no charminfo!", charm->GetGUID());
- return;
- }
-
- uint8 addlist = 0;
- WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);// first line + actionbar + spellcount + spells + last adds
-
- //16
- data << (uint64)charm->GetGUID() << uint32(0x00000000) << uint8(0) << uint8(0) << uint16(0);
-
- for(uint32 i = 0; i < 10; i++) //40
- {
- data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
- }
-
- data << uint8(addlist); //1
-
- uint8 count = 3;
- data << count;
- data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3
- data << uint32(0x8e8c) << uint64(0); // if count = 3
- data << uint32(0x8e8b) << uint64(0); // if count = 3
-
- GetSession()->SendPacket(&data);
-}
-
-void Player::CharmSpellInitialize()
-{
- Unit* charm = GetCharm();
-
- if(!charm)
- return;
-
- CharmInfo *charmInfo = charm->GetCharmInfo();
- if(!charmInfo)
- {
- sLog.outError("Player::CharmSpellInitialize(): the player's charm ("I64FMTD") has no charminfo!", charm->GetGUID());
- return;
- }
-
- uint8 addlist = 0;
-
- if(charm->GetTypeId() != TYPEID_PLAYER)
- {
- CreatureInfo const *cinfo = ((Creature*)charm)->GetCreatureInfo();
-
- if(cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK)
- {
- for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
- {
- if(charmInfo->GetCharmSpell(i)->spellId)
- ++addlist;
- }
- }
- }
-
- WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);// first line + actionbar + spellcount + spells + last adds
-
- data << (uint64)charm->GetGUID() << uint32(0x00000000);
-
- if(charm->GetTypeId() != TYPEID_PLAYER)
- data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState());
- else
- data << uint8(0) << uint8(0);
-
- data << uint16(0);
-
- for(uint32 i = 0; i < 10; i++) //40
- {
- data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
- }
-
- data << uint8(addlist); //1
-
- if(addlist)
- {
- for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
- {
- CharmSpellEntry *cspell = charmInfo->GetCharmSpell(i);
- if(cspell->spellId)
- {
- data << uint16(cspell->spellId);
- data << uint16(cspell->active);
- }
- }
- }
-
- uint8 count = 3;
- data << count;
- data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3
- data << uint32(0x8e8c) << uint64(0); // if count = 3
- data << uint32(0x8e8b) << uint64(0); // if count = 3
-
- GetSession()->SendPacket(&data);
-}
-
-int32 Player::GetTotalFlatMods(uint32 spellId, SpellModOp op)
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
- if (!spellInfo) return 0;
- int32 total = 0;
- for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr)
- {
- SpellModifier *mod = *itr;
-
- if(!IsAffectedBySpellmod(spellInfo,mod))
- continue;
-
- if (mod->type == SPELLMOD_FLAT)
- total += mod->value;
- }
- return total;
-}
-
-int32 Player::GetTotalPctMods(uint32 spellId, SpellModOp op)
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
- if (!spellInfo) return 0;
- int32 total = 0;
- for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr)
- {
- SpellModifier *mod = *itr;
-
- if(!IsAffectedBySpellmod(spellInfo,mod))
- continue;
-
- if (mod->type == SPELLMOD_PCT)
- total += mod->value;
- }
- return total;
-}
-
-bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell)
-{
- if (!mod || !spellInfo)
- return false;
-
- if(mod->charges == -1 && mod->lastAffected ) // marked as expired but locked until spell casting finish
- {
- // prevent apply to any spell except spell that trigger expire
- if(spell)
- {
- if(mod->lastAffected != spell)
- return false;
- }
- else if(mod->lastAffected != FindCurrentSpellBySpellId(spellInfo->Id))
- return false;
- }
-
- return spellmgr.IsAffectedBySpell(spellInfo,mod->spellId,mod->effectId,mod->mask);
-}
-
-void Player::AddSpellMod(SpellModifier* mod, bool apply)
-{
- uint16 Opcode= (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER;
-
- for(int eff=0;eff<64;++eff)
- {
- uint64 _mask = uint64(1) << eff;
- if ( mod->mask & _mask)
- {
- int32 val = 0;
- for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr)
- {
- if ((*itr)->type == mod->type && (*itr)->mask & _mask)
- val += (*itr)->value;
- }
- val += apply ? mod->value : -(mod->value);
- WorldPacket data(Opcode, (1+1+4));
- data << uint8(eff);
- data << uint8(mod->op);
- data << int32(val);
- SendDirectMessage(&data);
- }
- }
-
- if (apply)
- m_spellMods[mod->op].push_back(mod);
- else
- {
- if (mod->charges == -1)
- --m_SpellModRemoveCount;
- m_spellMods[mod->op].remove(mod);
- delete mod;
- }
-}
-
-void Player::RemoveSpellMods(Spell const* spell)
-{
- if(!spell || (m_SpellModRemoveCount == 0))
- return;
-
- for(int i=0;i<MAX_SPELLMOD;++i)
- {
- for (SpellModList::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();)
- {
- SpellModifier *mod = *itr;
- ++itr;
-
- if (mod && mod->charges == -1 && (mod->lastAffected == spell || mod->lastAffected==NULL))
- {
- RemoveAurasDueToSpell(mod->spellId);
- if (m_spellMods[i].empty())
- break;
- else
- itr = m_spellMods[i].begin();
- }
- }
- }
-}
-
-// send Proficiency
-void Player::SendProficiency(uint8 pr1, uint32 pr2)
-{
- WorldPacket data(SMSG_SET_PROFICIENCY, 8);
- data << pr1 << pr2;
- GetSession()->SendPacket (&data);
-}
-
-void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type)
-{
- QueryResult *result = NULL;
- if(type==10)
- result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid));
- else
- result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
- if(result)
- {
- do // this part effectively does nothing, since the deletion / modification only takes place _after_ the PetitionQuery. Though I don't know if the result remains intact if I execute the delete query beforehand.
- { // and SendPetitionQueryOpcode reads data from the DB
- Field *fields = result->Fetch();
- uint64 ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
- uint64 petitionguid = MAKE_NEW_GUID(fields[1].GetUInt32(), 0, HIGHGUID_ITEM);
-
- // send update if charter owner in game
- Player* owner = objmgr.GetPlayer(ownerguid);
- if(owner)
- owner->GetSession()->SendPetitionQueryOpcode(petitionguid);
-
- } while ( result->NextRow() );
-
- delete result;
-
- if(type==10)
- CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid));
- else
- CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
- }
-
- CharacterDatabase.BeginTransaction();
- if(type == 10)
- {
- CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u'", GUID_LOPART(guid));
- CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u'", GUID_LOPART(guid));
- }
- else
- {
- CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
- CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
- }
- CharacterDatabase.CommitTransaction();
-}
-
-void Player::SetRestBonus (float rest_bonus_new)
-{
- // Prevent resting on max level
- if(getLevel() >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
- rest_bonus_new = 0;
-
- if(rest_bonus_new < 0)
- rest_bonus_new = 0;
-
- float rest_bonus_max = (float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)*1.5/2;
-
- if(rest_bonus_new > rest_bonus_max)
- m_rest_bonus = rest_bonus_max;
- else
- m_rest_bonus = rest_bonus_new;
-
- // update data for client
- if(m_rest_bonus>10)
- SetByteValue(PLAYER_BYTES_2, 3, 0x01); // Set Reststate = Rested
- else if(m_rest_bonus<=1)
- SetByteValue(PLAYER_BYTES_2, 3, 0x02); // Set Reststate = Normal
-
- //RestTickUpdate
- SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus));
-}
-
-void Player::HandleStealthedUnitsDetection()
-{
- std::list<Unit*> stealthedUnits;
-
- CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- MaNGOS::AnyStealthedCheck u_check;
- MaNGOS::UnitListSearcher<MaNGOS::AnyStealthedCheck > searcher(stealthedUnits, u_check);
-
- TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyStealthedCheck >, WorldTypeMapContainer > world_unit_searcher(searcher);
- TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyStealthedCheck >, GridTypeMapContainer > grid_unit_searcher(searcher);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
- cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
-
- for (std::list<Unit*>::iterator i = stealthedUnits.begin(); i != stealthedUnits.end();)
- {
- if((*i)==this)
- {
- i = stealthedUnits.erase(i);
- continue;
- }
-
- if ((*i)->isVisibleForOrDetect(this,true))
- {
-
- (*i)->SendUpdateToPlayer(this);
- m_clientGUIDs.insert((*i)->GetGUID());
-
- #ifdef MANGOS_DEBUG
- if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
- sLog.outDebug("Object %u (Type: %u) is detected in stealth by player %u. Distance = %f",(*i)->GetGUIDLow(),(*i)->GetTypeId(),GetGUIDLow(),GetDistance(*i));
- #endif
-
- // target aura duration for caster show only if target exist at caster client
- // send data at target visibility change (adding to client)
- if((*i)!=this && (*i)->isType(TYPEMASK_UNIT))
- SendAuraDurationsForTarget(*i);
-
- i = stealthedUnits.erase(i);
- continue;
- }
-
- ++i;
- }
-}
-
-bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, uint32 mount_id, Creature* npc)
-{
- if(nodes.size() < 2)
- return false;
-
- // not let cheating with start flight mounted
- if(IsMounted())
- {
- WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
- data << uint32(ERR_TAXIPLAYERALREADYMOUNTED);
- GetSession()->SendPacket(&data);
- return false;
- }
-
- if( m_ShapeShiftFormSpellId && m_form != FORM_BATTLESTANCE && m_form != FORM_BERSERKERSTANCE && m_form != FORM_DEFENSIVESTANCE && m_form != FORM_SHADOW )
- {
- WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
- data << uint32(ERR_TAXIPLAYERSHAPESHIFTED);
- GetSession()->SendPacket(&data);
- return false;
- }
-
- // not let cheating with start flight in time of logout process || if casting not finished || while in combat || if not use Spell's with EffectSendTaxi
- if(GetSession()->isLogingOut() ||
- (!m_currentSpells[CURRENT_GENERIC_SPELL] ||
- m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Effect[0] != SPELL_EFFECT_SEND_TAXI)&&
- IsNonMeleeSpellCasted(false) ||
- isInCombat())
- {
- WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
- data << uint32(ERR_TAXIPLAYERBUSY);
- GetSession()->SendPacket(&data);
- return false;
- }
-
- if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
- return false;
-
- uint32 sourcenode = nodes[0];
-
- // starting node too far away (cheat?)
- TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(sourcenode);
- if( !node || node->map_id != GetMapId() ||
- (node->x - GetPositionX())*(node->x - GetPositionX())+
- (node->y - GetPositionY())*(node->y - GetPositionY())+
- (node->z - GetPositionZ())*(node->z - GetPositionZ()) >
- (2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE) )
- {
- WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
- data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR);
- GetSession()->SendPacket(&data);
- return false;
- }
-
- // Prepare to flight start now
-
- // stop combat at start taxi flight if any
- CombatStop();
-
- // stop trade (client cancel trade at taxi map open but cheating tools can be used for reopen it)
- TradeCancel(true);
-
- // clean not finished taxi path if any
- m_taxi.ClearTaxiDestinations();
-
- // 0 element current node
- m_taxi.AddTaxiDestination(sourcenode);
-
- // fill destinations path tail
- uint32 sourcepath = 0;
- uint32 totalcost = 0;
-
- uint32 prevnode = sourcenode;
- uint32 lastnode = 0;
-
- for(uint32 i = 1; i < nodes.size(); ++i)
- {
- uint32 path, cost;
-
- lastnode = nodes[i];
- objmgr.GetTaxiPath(prevnode, lastnode, path, cost);
-
- if(!path)
- {
- m_taxi.ClearTaxiDestinations();
- return false;
- }
-
- totalcost += cost;
-
- if(prevnode == sourcenode)
- sourcepath = path;
-
- m_taxi.AddTaxiDestination(lastnode);
-
- prevnode = lastnode;
- }
-
- if(!mount_id) // if not provide then attempt use default.
- mount_id = objmgr.GetTaxiMount(sourcenode, GetTeam());
-
- if (mount_id == 0 || sourcepath == 0)
- {
- WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
- data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR);
- GetSession()->SendPacket(&data);
- m_taxi.ClearTaxiDestinations();
- return false;
- }
-
- uint32 money = GetMoney();
-
- if(npc)
- {
- totalcost = (uint32)ceil(totalcost*GetReputationPriceDiscount(npc));
- }
-
- if(money < totalcost)
- {
- WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
- data << uint32(ERR_TAXINOTENOUGHMONEY);
- GetSession()->SendPacket(&data);
- m_taxi.ClearTaxiDestinations();
- return false;
- }
-
- //Checks and preparations done, DO FLIGHT
- ModifyMoney(-(int32)totalcost);
-
- // prevent stealth flight
- RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
-
- WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
- data << uint32(ERR_TAXIOK);
- GetSession()->SendPacket(&data);
-
- sLog.outDebug("WORLD: Sent SMSG_ACTIVATETAXIREPLY");
-
- GetSession()->SendDoFlight(mount_id, sourcepath);
-
- return true;
-}
-
-void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs )
-{
- // last check 2.0.10
- WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+m_spells.size()*8);
- data << GetGUID();
- data << uint8(0x0);
- time_t curTime = time(NULL);
- for(PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- {
- if (itr->second->state == PLAYERSPELL_REMOVED)
- continue;
- uint32 unSpellId = itr->first;
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(unSpellId);
- if (!spellInfo)
- {
- ASSERT(spellInfo);
- continue;
- }
-
- // Not send cooldown for this spells
- if (spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
- continue;
-
- if((idSchoolMask & GetSpellSchoolMask(spellInfo)) && GetSpellCooldownDelay(unSpellId) < unTimeMs )
- {
- data << unSpellId;
- data << unTimeMs; // in m.secs
- AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/1000);
- }
- }
- GetSession()->SendPacket(&data);
-}
-
-void Player::InitDataForForm(bool reapplyMods)
-{
- SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(m_form);
- if(ssEntry && ssEntry->attackSpeed)
- {
- SetAttackTime(BASE_ATTACK,ssEntry->attackSpeed);
- SetAttackTime(OFF_ATTACK,ssEntry->attackSpeed);
- SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
- }
- else
- SetRegularAttackTime();
-
- switch(m_form)
- {
- case FORM_CAT:
- {
- if(getPowerType()!=POWER_ENERGY)
- setPowerType(POWER_ENERGY);
- break;
- }
- case FORM_BEAR:
- case FORM_DIREBEAR:
- {
- if(getPowerType()!=POWER_RAGE)
- setPowerType(POWER_RAGE);
- break;
- }
- default: // 0, for example
- {
- ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(getClass());
- if(cEntry && cEntry->powerType < MAX_POWERS && uint32(getPowerType()) != cEntry->powerType)
- setPowerType(Powers(cEntry->powerType));
- break;
- }
- }
-
- // update auras at form change, ignore this at mods reapply (.reset stats/etc) when form not change.
- if (!reapplyMods)
- UpdateEquipSpellsAtFormChange();
-
- UpdateAttackPowerAndDamage();
- UpdateAttackPowerAndDamage(true);
-}
-
-// Return true is the bought item has a max count to force refresh of window by caller
-bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint64 bagguid, uint8 slot)
-{
- // cheating attempt
- if(count < 1) count = 1;
-
- if(!isAlive())
- return false;
-
- ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
- if( !pProto )
- {
- SendBuyError( BUY_ERR_CANT_FIND_ITEM, NULL, item, 0);
- return false;
- }
-
- Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*this, vendorguid,UNIT_NPC_FLAG_VENDOR);
- if (!pCreature)
- {
- sLog.outDebug( "WORLD: BuyItemFromVendor - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
- SendBuyError( BUY_ERR_DISTANCE_TOO_FAR, NULL, item, 0);
- return false;
- }
-
- VendorItemData const* vItems = pCreature->GetVendorItems();
- if(!vItems || vItems->Empty())
- {
- SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
- return false;
- }
-
- 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 )
- {
- if(pCreature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count )
- {
- SendBuyError( BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0);
- return false;
- }
- }
-
- if( uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank)
- {
- SendBuyError( BUY_ERR_REPUTATION_REQUIRE, pCreature, item, 0);
- return false;
- }
-
- if(crItem->ExtendedCost)
- {
- ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
- if(!iece)
- {
- sLog.outError("Item %u have wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost);
- return false;
- }
-
- // honor points price
- if(GetHonorPoints() < (iece->reqhonorpoints * count))
- {
- SendEquipError(EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS, NULL, NULL);
- return false;
- }
-
- // arena points price
- if(GetArenaPoints() < (iece->reqarenapoints * count))
- {
- SendEquipError(EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS, NULL, NULL);
- return false;
- }
-
- // item base price
- for (uint8 i = 0; i < 5; ++i)
- {
- if(iece->reqitem[i] && !HasItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count)))
- {
- SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
- return false;
- }
- }
-
- // check for personal arena rating requirement
- if( GetMaxPersonalArenaRatingRequirement() < iece->reqpersonalarenarating )
- {
- // probably not the proper equip err
- SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK,NULL,NULL);
- return false;
- }
- }
-
- uint32 price = pProto->BuyPrice * count;
-
- // reputation discount
- price = uint32(floor(price * GetReputationPriceDiscount(pCreature)));
-
- if( GetMoney() < price )
- {
- SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, item, 0);
- return false;
- }
-
- uint8 bag = 0; // init for case invalid bagGUID
-
- if (bagguid != NULL_BAG && slot != NULL_SLOT)
- {
- Bag *pBag;
- if( bagguid == GetGUID() )
- {
- bag = INVENTORY_SLOT_BAG_0;
- }
- else
- {
- for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;i++)
- {
- pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0,i);
- if( pBag )
- {
- if( bagguid == pBag->GetGUID() )
- {
- bag = i;
- break;
- }
- }
- }
- }
- }
-
- if( IsInventoryPos( bag, slot ) || (bagguid == NULL_BAG && slot == NULL_SLOT) )
- {
- ItemPosCountVec dest;
- uint8 msg = CanStoreNewItem( bag, slot, dest, item, pProto->BuyCount * count );
- if( msg != EQUIP_ERR_OK )
- {
- SendEquipError( msg, NULL, NULL );
- return false;
- }
-
- ModifyMoney( -(int32)price );
- if(crItem->ExtendedCost) // case for new honor system
- {
- ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
- if(iece->reqhonorpoints)
- ModifyHonorPoints( - int32(iece->reqhonorpoints * count));
- if(iece->reqarenapoints)
- ModifyArenaPoints( - int32(iece->reqarenapoints * count));
- for (uint8 i = 0; i < 5; ++i)
- {
- if(iece->reqitem[i])
- DestroyItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count), true);
- }
- }
-
- if(Item *it = StoreNewItem( dest, item, true ))
- {
- uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count);
-
- WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
- data << pCreature->GetGUID();
- data << (uint32)(vendor_slot+1); // numbered from 1 at client
- data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
- data << (uint32)count;
- GetSession()->SendPacket(&data);
-
- SendNewItem(it, pProto->BuyCount*count, true, false, false);
- }
- }
- else if( IsEquipmentPos( bag, slot ) )
- {
- uint16 dest;
- uint8 msg = CanEquipNewItem( slot, dest, item, pProto->BuyCount * count, false );
- if( msg != EQUIP_ERR_OK )
- {
- SendEquipError( msg, NULL, NULL );
- return false;
- }
-
- ModifyMoney( -(int32)price );
- if(crItem->ExtendedCost) // case for new honor system
- {
- ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
- if(iece->reqhonorpoints)
- ModifyHonorPoints( - int32(iece->reqhonorpoints));
- if(iece->reqarenapoints)
- ModifyArenaPoints( - int32(iece->reqarenapoints));
- for (uint8 i = 0; i < 5; ++i)
- {
- if(iece->reqitem[i])
- DestroyItemCount(iece->reqitem[i], iece->reqitemcount[i], true);
- }
- }
-
- if(Item *it = EquipNewItem( dest, item, pProto->BuyCount * count, true ))
- {
- uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count);
-
- WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
- data << pCreature->GetGUID();
- data << (uint32)(vendor_slot+1); // numbered from 1 at client
- data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
- data << (uint32)count;
- GetSession()->SendPacket(&data);
-
- SendNewItem(it, pProto->BuyCount*count, true, false, false);
-
- AutoUnequipOffhandIfNeed();
- }
- }
- else
- {
- SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
- return false;
- }
-
- return crItem->maxcount!=0;
-}
-
-uint32 Player::GetMaxPersonalArenaRatingRequirement()
-{
- // returns the maximal personal arena rating that can be used to purchase items requiring this condition
- // the personal rating of the arena team must match the required limit as well
- // so return max[in arenateams](min(personalrating[teamtype], teamrating[teamtype]))
- uint32 max_personal_rating = 0;
- for(int i = 0; i < MAX_ARENA_SLOT; ++i)
- {
- if(ArenaTeam * at = objmgr.GetArenaTeamById(GetArenaTeamId(i)))
- {
- uint32 p_rating = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (i * 6) + 5);
- uint32 t_rating = at->GetRating();
- p_rating = p_rating<t_rating? p_rating : t_rating;
- if(max_personal_rating < p_rating)
- max_personal_rating = p_rating;
- }
- }
- return max_personal_rating;
-}
-
-void Player::UpdateHomebindTime(uint32 time)
-{
- // GMs never get homebind timer online
- if (m_InstanceValid || isGameMaster())
- {
- if(m_HomebindTimer) // instance valid, but timer not reset
- {
- // hide reminder
- WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
- data << uint32(0);
- data << uint32(0);
- GetSession()->SendPacket(&data);
- }
- // instance is valid, reset homebind timer
- m_HomebindTimer = 0;
- }
- else if (m_HomebindTimer > 0)
- {
- if (time >= m_HomebindTimer)
- {
- // teleport to homebind location
- TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation());
- }
- else
- m_HomebindTimer -= time;
- }
- else
- {
- // instance is invalid, start homebind timer
- m_HomebindTimer = 60000;
- // send message to player
- WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
- data << m_HomebindTimer;
- data << uint32(1);
- GetSession()->SendPacket(&data);
- sLog.outDebug("PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName(),GetGUIDLow());
- }
-}
-
-void Player::UpdatePvP(bool state, bool ovrride)
-{
- if(!state || ovrride)
- {
- SetPvP(state);
- if(Pet* pet = GetPet())
- pet->SetPvP(state);
- if(Unit* charmed = GetCharm())
- charmed->SetPvP(state);
-
- pvpInfo.endTimer = 0;
- }
- else
- {
- if(pvpInfo.endTimer != 0)
- pvpInfo.endTimer = time(NULL);
- else
- {
- SetPvP(state);
-
- if(Pet* pet = GetPet())
- pet->SetPvP(state);
- if(Unit* charmed = GetCharm())
- charmed->SetPvP(state);
- }
- }
-}
-
-void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
-{
- SpellCooldown sc;
- sc.end = end_time;
- sc.itemid = itemid;
- m_spellCooldowns[spellid] = sc;
-}
-
-void Player::SendCooldownEvent(SpellEntry const *spellInfo)
-{
- if ( !(spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) )
- return;
-
- // Get spell cooldwn
- int32 cooldown = GetSpellRecoveryTime(spellInfo);
- // Apply spellmods
- ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown);
- if (cooldown < 0)
- cooldown = 0;
- // Add cooldown
- AddSpellCooldown(spellInfo->Id, 0, time(NULL) + cooldown / 1000);
- // Send activate
- WorldPacket data(SMSG_COOLDOWN_EVENT, (4+8));
- data << spellInfo->Id;
- data << GetGUID();
- SendDirectMessage(&data);
-}
- //slot to be excluded while counting
-bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot)
-{
- if(!enchantmentcondition)
- return true;
-
- SpellItemEnchantmentConditionEntry const *Condition = sSpellItemEnchantmentConditionStore.LookupEntry(enchantmentcondition);
-
- if(!Condition)
- return true;
-
- uint8 curcount[4] = {0, 0, 0, 0};
-
- //counting current equipped gem colors
- for(uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
- {
- if(i == slot)
- continue;
- Item *pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
- if(pItem2 && pItem2->GetProto()->Socket[0].Color)
- {
- for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
- {
- uint32 enchant_id = pItem2->GetEnchantmentId(EnchantmentSlot(enchant_slot));
- if(!enchant_id)
- continue;
-
- SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!enchantEntry)
- continue;
-
- uint32 gemid = enchantEntry->GemID;
- if(!gemid)
- continue;
-
- ItemPrototype const* gemProto = sItemStorage.LookupEntry<ItemPrototype>(gemid);
- if(!gemProto)
- continue;
-
- GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties);
- if(!gemProperty)
- continue;
-
- uint8 GemColor = gemProperty->color;
-
- for(uint8 b = 0, tmpcolormask = 1; b < 4; b++, tmpcolormask <<= 1)
- {
- if(tmpcolormask & GemColor)
- ++curcount[b];
- }
- }
- }
- }
-
- bool activate = true;
-
- for(int i = 0; i < 5; i++)
- {
- if(!Condition->Color[i])
- continue;
-
- uint32 _cur_gem = curcount[Condition->Color[i] - 1];
-
- // if have <CompareColor> use them as count, else use <value> from Condition
- uint32 _cmp_gem = Condition->CompareColor[i] ? curcount[Condition->CompareColor[i] - 1]: Condition->Value[i];
-
- switch(Condition->Comparator[i])
- {
- case 2: // requires less <color> than (<value> || <comparecolor>) gems
- activate &= (_cur_gem < _cmp_gem) ? true : false;
- break;
- case 3: // requires more <color> than (<value> || <comparecolor>) gems
- activate &= (_cur_gem > _cmp_gem) ? true : false;
- break;
- case 5: // requires at least <color> than (<value> || <comparecolor>) gems
- activate &= (_cur_gem >= _cmp_gem) ? true : false;
- break;
- }
- }
-
- sLog.outDebug("Checking Condition %u, there are %u Meta Gems, %u Red Gems, %u Yellow Gems and %u Blue Gems, Activate:%s", enchantmentcondition, curcount[0], curcount[1], curcount[2], curcount[3], activate ? "yes" : "no");
-
- return activate;
-}
-
-void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply)
-{
- //cycle all equipped items
- for(uint32 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
- {
- //enchants for the slot being socketed are handled by Player::ApplyItemMods
- if(slot == exceptslot)
- continue;
-
- Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot );
-
- if(!pItem || !pItem->GetProto()->Socket[0].Color)
- continue;
-
- for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
- {
- uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
- if(!enchant_id)
- continue;
-
- SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!enchantEntry)
- continue;
-
- uint32 condition = enchantEntry->EnchantmentCondition;
- if(condition)
- {
- //was enchant active with/without item?
- bool wasactive = EnchantmentFitsRequirements(condition, apply ? exceptslot : -1);
- //should it now be?
- if(wasactive ^ EnchantmentFitsRequirements(condition, apply ? -1 : exceptslot))
- {
- // ignore item gem conditions
- //if state changed, (dis)apply enchant
- ApplyEnchantment(pItem,EnchantmentSlot(enchant_slot),!wasactive,true,true);
- }
- }
- }
- }
-}
-
- //if false -> then toggled off if was on| if true -> toggled on if was off AND meets requirements
-void Player::ToggleMetaGemsActive(uint8 exceptslot, bool apply)
-{
- //cycle all equipped items
- for(int slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
- {
- //enchants for the slot being socketed are handled by WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
- if(slot == exceptslot)
- continue;
-
- Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot );
-
- if(!pItem || !pItem->GetProto()->Socket[0].Color) //if item has no sockets or no item is equipped go to next item
- continue;
-
- //cycle all (gem)enchants
- for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
- {
- uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
- if(!enchant_id) //if no enchant go to next enchant(slot)
- continue;
-
- SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!enchantEntry)
- continue;
-
- //only metagems to be (de)activated, so only enchants with condition
- uint32 condition = enchantEntry->EnchantmentCondition;
- if(condition)
- ApplyEnchantment(pItem,EnchantmentSlot(enchant_slot), apply);
- }
- }
-}
-
-void Player::LeaveBattleground(bool teleportToEntryPoint)
-{
- if(BattleGround *bg = GetBattleGround())
- {
- bool need_debuf = bg->isBattleGround() && (bg->GetStatus() == STATUS_IN_PROGRESS) && sWorld.getConfig(CONFIG_BATTLEGROUND_CAST_DESERTER);
-
- bg->RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true);
-
- // call after remove to be sure that player resurrected for correct cast
- if(need_debuf)
- CastSpell(this, 26013, true); // Deserter
- }
-}
-
-bool Player::CanJoinToBattleground() const
-{
- // check Deserter debuff
- if(GetDummyAura(26013))
- return false;
-
- return true;
-}
-
-bool Player::CanReportAfkDueToLimit()
-{
- // a player can complain about 15 people per 5 minutes
- if(m_bgAfkReportedCount >= 15)
- return false;
- ++m_bgAfkReportedCount;
- return true;
-}
-
-///This player has been blamed to be inactive in a battleground
-void Player::ReportedAfkBy(Player* reporter)
-{
- BattleGround *bg = GetBattleGround();
- if(!bg || bg != reporter->GetBattleGround() || GetTeam() != reporter->GetTeam())
- return;
-
- // check if player has 'Idle' or 'Inactive' debuff
- if(m_bgAfkReporter.find(reporter->GetGUIDLow())==m_bgAfkReporter.end() && !HasAura(43680,0) && !HasAura(43681,0) && reporter->CanReportAfkDueToLimit())
- {
- m_bgAfkReporter.insert(reporter->GetGUIDLow());
- // 3 players have to complain to apply debuff
- if(m_bgAfkReporter.size() >= 3)
- {
- // cast 'Idle' spell
- CastSpell(this, 43680, true);
- m_bgAfkReporter.clear();
- }
- }
-}
-
-bool Player::IsVisibleInGridForPlayer( Player* pl ) const
-{
- // gamemaster in GM mode see all, including ghosts
- if(pl->isGameMaster() && GetSession()->GetSecurity() <= pl->GetSession()->GetSecurity())
- return true;
-
- // It seems in battleground everyone sees everyone, except the enemy-faction ghosts
- if (InBattleGround())
- {
- if (!(isAlive() || m_deathTimer > 0) && !IsFriendlyTo(pl) )
- return false;
- return true;
- }
-
- // Live player see live player or dead player with not realized corpse
- if(pl->isAlive() || pl->m_deathTimer > 0)
- {
- return isAlive() || m_deathTimer > 0;
- }
-
- // Ghost see other friendly ghosts, that's for sure
- if(!(isAlive() || m_deathTimer > 0) && IsFriendlyTo(pl))
- return true;
-
- // Dead player see live players near own corpse
- if(isAlive())
- {
- Corpse *corpse = pl->GetCorpse();
- if(corpse)
- {
- // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level
- if(corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO)))
- return true;
- }
- }
-
- // and not see any other
- return false;
-}
-
-bool Player::IsVisibleGloballyFor( Player* u ) const
-{
- if(!u)
- return false;
-
- // Always can see self
- if (u==this)
- return true;
-
- // Visible units, always are visible for all players
- if (GetVisibility() == VISIBILITY_ON)
- return true;
-
- // GMs are visible for higher gms (or players are visible for gms)
- if (u->GetSession()->GetSecurity() > SEC_PLAYER)
- return GetSession()->GetSecurity() <= u->GetSession()->GetSecurity();
-
- // non faction visibility non-breakable for non-GMs
- if (GetVisibility() == VISIBILITY_OFF)
- return false;
-
- // non-gm stealth/invisibility not hide from global player lists
- return true;
-}
-
-void Player::UpdateVisibilityOf(WorldObject* target)
-{
- if(HaveAtClient(target))
- {
- if(!target->isVisibleForInState(this,true))
- {
- target->DestroyForPlayer(this);
- m_clientGUIDs.erase(target->GetGUID());
-
- #ifdef MANGOS_DEBUG
- if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
- sLog.outDebug("Object %u (Type: %u) out of range for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),GetGUIDLow(),GetDistance(target));
- #endif
- }
- }
- else
- {
- if(target->isVisibleForInState(this,false))
- {
- target->SendUpdateToPlayer(this);
- if(target->GetTypeId()!=TYPEID_GAMEOBJECT||!((GameObject*)target)->IsTransport())
- m_clientGUIDs.insert(target->GetGUID());
-
- #ifdef MANGOS_DEBUG
- if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
- sLog.outDebug("Object %u (Type: %u) is visible now for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),GetGUIDLow(),GetDistance(target));
- #endif
-
- // target aura duration for caster show only if target exist at caster client
- // send data at target visibility change (adding to client)
- if(target!=this && target->isType(TYPEMASK_UNIT))
- SendAuraDurationsForTarget((Unit*)target);
-
- if(target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->isAlive())
- ((Creature*)target)->SendMonsterMoveWithSpeedToCurrentDestination(this);
- }
- }
-}
-
-template<class T>
-inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, T* target)
-{
- s64.insert(target->GetGUID());
-}
-
-template<>
-inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, GameObject* target)
-{
- if(!target->IsTransport())
- s64.insert(target->GetGUID());
-}
-
-template<class T>
-void Player::UpdateVisibilityOf(T* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow)
-{
- if(HaveAtClient(target))
- {
- if(!target->isVisibleForInState(this,true))
- {
- target->BuildOutOfRangeUpdateBlock(&data);
- m_clientGUIDs.erase(target->GetGUID());
-
- #ifdef MANGOS_DEBUG
- if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
- sLog.outDebug("Object %u (Type: %u, Entry: %u) is out of range for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),target->GetEntry(),GetGUIDLow(),GetDistance(target));
- #endif
- }
- }
- else
- {
- if(target->isVisibleForInState(this,false))
- {
- visibleNow.insert(target);
- target->BuildUpdate(data_updates);
- target->BuildCreateUpdateBlockForPlayer(&data, this);
- UpdateVisibilityOf_helper(m_clientGUIDs,target);
-
- #ifdef MANGOS_DEBUG
- if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
- sLog.outDebug("Object %u (Type: %u, Entry: %u) is visible now for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),target->GetEntry(),GetGUIDLow(),GetDistance(target));
- #endif
- }
- }
-}
-
-template void Player::UpdateVisibilityOf(Player* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
-template void Player::UpdateVisibilityOf(Creature* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
-template void Player::UpdateVisibilityOf(Corpse* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
-template void Player::UpdateVisibilityOf(GameObject* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
-template void Player::UpdateVisibilityOf(DynamicObject* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
-
-void Player::InitPrimaryProffesions()
-{
- SetFreePrimaryProffesions(sWorld.getConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL));
-}
-
-void Player::SendComboPoints()
-{
- Unit *combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget);
- if (combotarget)
- {
- WorldPacket data(SMSG_UPDATE_COMBO_POINTS, combotarget->GetPackGUID().size()+1);
- data.append(combotarget->GetPackGUID());
- data << uint8(m_comboPoints);
- GetSession()->SendPacket(&data);
- }
-}
-
-void Player::AddComboPoints(Unit* target, int8 count)
-{
- if(!count)
- return;
-
- // without combo points lost (duration checked in aura)
- RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS);
-
- if(target->GetGUID() == m_comboTarget)
- {
- m_comboPoints += count;
- }
- else
- {
- if(m_comboTarget)
- if(Unit* target = ObjectAccessor::GetUnit(*this,m_comboTarget))
- target->RemoveComboPointHolder(GetGUIDLow());
-
- m_comboTarget = target->GetGUID();
- m_comboPoints = count;
-
- target->AddComboPointHolder(GetGUIDLow());
- }
-
- if (m_comboPoints > 5) m_comboPoints = 5;
- if (m_comboPoints < 0) m_comboPoints = 0;
-
- SendComboPoints();
-}
-
-void Player::ClearComboPoints()
-{
- if(!m_comboTarget)
- return;
-
- // without combopoints lost (duration checked in aura)
- RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS);
-
- m_comboPoints = 0;
-
- SendComboPoints();
-
- if(Unit* target = ObjectAccessor::GetUnit(*this,m_comboTarget))
- target->RemoveComboPointHolder(GetGUIDLow());
-
- m_comboTarget = 0;
-}
-
-void Player::SetGroup(Group *group, int8 subgroup)
-{
- if(group == NULL) m_group.unlink();
- else
- {
- // never use SetGroup without a subgroup unless you specify NULL for group
- assert(subgroup >= 0);
- m_group.link(group, this);
- m_group.setSubGroup((uint8)subgroup);
- }
-}
-
-void Player::SendInitialPacketsBeforeAddToMap()
-{
- WorldPacket data(SMSG_SET_REST_START, 4);
- data << uint32(0); // unknown, may be rest state time or expirience
- GetSession()->SendPacket(&data);
-
- // Homebind
- data.Initialize(SMSG_BINDPOINTUPDATE, 5*4);
- data << m_homebindX << m_homebindY << m_homebindZ;
- data << (uint32) m_homebindMapId;
- data << (uint32) m_homebindZoneId;
- GetSession()->SendPacket(&data);
-
- // SMSG_SET_PROFICIENCY
- // SMSG_UPDATE_AURA_DURATION
-
- // tutorial stuff
- data.Initialize(SMSG_TUTORIAL_FLAGS, 8*4);
- for (int i = 0; i < 8; ++i)
- data << uint32( GetTutorialInt(i) );
- GetSession()->SendPacket(&data);
-
- SendInitialSpells();
-
- data.Initialize(SMSG_SEND_UNLEARN_SPELLS, 4);
- data << uint32(0); // count, for(count) uint32;
- GetSession()->SendPacket(&data);
-
- SendInitialActionButtons();
- SendInitialReputations();
- UpdateZone(GetZoneId());
- SendInitWorldStates();
-
- // SMSG_SET_AURA_SINGLE
-
- data.Initialize(SMSG_LOGIN_SETTIMESPEED, 8);
- data << uint32(secsToTimeBitFields(sWorld.GetGameTime()));
- data << (float)0.01666667f; // game speed
- GetSession()->SendPacket( &data );
-
- // set fly flag if in fly form or taxi flight to prevent visually drop at ground in showup moment
- if(HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) || isInFlight())
- AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
-}
-
-void Player::SendInitialPacketsAfterAddToMap()
-{
- CastSpell(this, 836, true); // LOGINEFFECT
-
- // set some aura effects that send packet to player client after add player to map
- // SendMessageToSet not send it to player not it map, only for aura that not changed anything at re-apply
- // same auras state lost at far teleport, send it one more time in this case also
- static const AuraType auratypes[] =
- {
- SPELL_AURA_MOD_FEAR, SPELL_AURA_TRANSFORM, SPELL_AURA_WATER_WALK,
- SPELL_AURA_FEATHER_FALL, SPELL_AURA_HOVER, SPELL_AURA_SAFE_FALL,
- SPELL_AURA_FLY, SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED, SPELL_AURA_NONE
- };
- for(AuraType const* itr = &auratypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr)
- {
- Unit::AuraList const& auraList = GetAurasByType(*itr);
- if(!auraList.empty())
- auraList.front()->ApplyModifier(true,true);
- }
-
- if(HasAuraType(SPELL_AURA_MOD_STUN))
- SetMovement(MOVE_ROOT);
-
- // manual send package (have code in ApplyModifier(true,true); that don't must be re-applied.
- if(HasAuraType(SPELL_AURA_MOD_ROOT))
- {
- WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10);
- data.append(GetPackGUID());
- data << (uint32)2;
- SendMessageToSet(&data,true);
- }
-
- SendEnchantmentDurations(); // must be after add to map
- SendItemDurations(); // must be after add to map
-}
-
-void Player::SendUpdateToOutOfRangeGroupMembers()
-{
- if (m_groupUpdateMask == GROUP_UPDATE_FLAG_NONE)
- return;
- if(Group* group = GetGroup())
- group->UpdatePlayerOutOfRange(this);
-
- m_groupUpdateMask = GROUP_UPDATE_FLAG_NONE;
- m_auraUpdateMask = 0;
- if(Pet *pet = GetPet())
- pet->ResetAuraUpdateMask();
-}
-
-void Player::SendTransferAborted(uint32 mapid, uint16 reason)
-{
- WorldPacket data(SMSG_TRANSFER_ABORTED, 4+2);
- data << uint32(mapid);
- data << uint16(reason); // transfer abort reason
- GetSession()->SendPacket(&data);
-}
-
-void Player::SendInstanceResetWarning(uint32 mapid, uint32 time)
-{
- // type of warning, based on the time remaining until reset
- uint32 type;
- if(time > 3600)
- type = RAID_INSTANCE_WELCOME;
- else if(time > 900 && time <= 3600)
- type = RAID_INSTANCE_WARNING_HOURS;
- else if(time > 300 && time <= 900)
- type = RAID_INSTANCE_WARNING_MIN;
- else
- type = RAID_INSTANCE_WARNING_MIN_SOON;
- WorldPacket data(SMSG_RAID_INSTANCE_MESSAGE, 4+4+4);
- data << uint32(type);
- data << uint32(mapid);
- data << uint32(time);
- GetSession()->SendPacket(&data);
-}
-
-void Player::ApplyEquipCooldown( Item * pItem )
-{
- for(int i = 0; i <5; ++i)
- {
- _Spell const& spellData = pItem->GetProto()->Spells[i];
-
- // no spell
- if( !spellData.SpellId )
- continue;
-
- // wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown)
- if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE )
- continue;
-
- AddSpellCooldown(spellData.SpellId, pItem->GetEntry(), time(NULL) + 30);
-
- WorldPacket data(SMSG_ITEM_COOLDOWN, 12);
- data << pItem->GetGUID();
- data << uint32(spellData.SpellId);
- GetSession()->SendPacket(&data);
- }
-}
-
-void Player::resetSpells()
-{
- // not need after this call
- if(HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
- {
- m_atLoginFlags = m_atLoginFlags & ~AT_LOGIN_RESET_SPELLS;
- CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login & ~ %u WHERE guid ='%u'", uint32(AT_LOGIN_RESET_SPELLS), GetGUIDLow());
- }
-
- // make full copy of map (spells removed and marked as deleted at another spell remove
- // and we can't use original map for safe iterative with visit each spell at loop end
- PlayerSpellMap smap = GetSpellMap();
-
- for(PlayerSpellMap::const_iterator iter = smap.begin();iter != smap.end(); ++iter)
- removeSpell(iter->first); // only iter->first can be accessed, object by iter->second can be deleted already
-
- learnDefaultSpells();
- learnQuestRewardedSpells();
-}
-
-void Player::learnDefaultSpells(bool loading)
-{
- // learn default race/class spells
- PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(),getClass());
- std::list<CreateSpellPair>::const_iterator spell_itr;
- for (spell_itr = info->spell.begin(); spell_itr!=info->spell.end(); ++spell_itr)
- {
- uint16 tspell = spell_itr->first;
- if (tspell)
- {
- sLog.outDebug("PLAYER: Adding initial spell, id = %u",tspell);
- if(loading || !spell_itr->second) // not care about passive spells or loading case
- addSpell(tspell,spell_itr->second);
- else // but send in normal spell in game learn case
- learnSpell(tspell);
- }
- }
-}
-
-void Player::learnQuestRewardedSpells(Quest const* quest)
-{
- uint32 spell_id = quest->GetRewSpellCast();
-
- // skip quests without rewarded spell
- if( !spell_id )
- return;
-
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
- if(!spellInfo)
- return;
-
- // check learned spells state
- bool found = false;
- for(int i=0; i < 3; ++i)
- {
- if(spellInfo->Effect[i] == SPELL_EFFECT_LEARN_SPELL && !HasSpell(spellInfo->EffectTriggerSpell[i]))
- {
- found = true;
- break;
- }
- }
-
- // skip quests with not teaching spell or already known spell
- if(!found)
- return;
-
- // prevent learn non first rank unknown profession and second specialization for same profession)
- uint32 learned_0 = spellInfo->EffectTriggerSpell[0];
- if( spellmgr.GetSpellRank(learned_0) > 1 && !HasSpell(learned_0) )
- {
- // not have first rank learned (unlearned prof?)
- uint32 first_spell = spellmgr.GetFirstSpellInChain(learned_0);
- if( !HasSpell(first_spell) )
- return;
-
- SpellEntry const *learnedInfo = sSpellStore.LookupEntry(learned_0);
- if(!learnedInfo)
- return;
-
- // specialization
- if(learnedInfo->Effect[0]==SPELL_EFFECT_TRADE_SKILL && learnedInfo->Effect[1]==0)
- {
- // search other specialization for same prof
- for(PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- {
- if(itr->second->state == PLAYERSPELL_REMOVED || itr->first==learned_0)
- continue;
-
- SpellEntry const *itrInfo = sSpellStore.LookupEntry(itr->first);
- if(!itrInfo)
- return;
-
- // compare only specializations
- if(itrInfo->Effect[0]!=SPELL_EFFECT_TRADE_SKILL || itrInfo->Effect[1]!=0)
- continue;
-
- // compare same chain spells
- if(spellmgr.GetFirstSpellInChain(itr->first) != first_spell)
- continue;
-
- // now we have 2 specialization, learn possible only if found is lesser specialization rank
- if(!spellmgr.IsHighRankOfSpell(learned_0,itr->first))
- return;
- }
- }
- }
-
- CastSpell( this, spell_id, true);
-}
-
-void Player::learnQuestRewardedSpells()
-{
- // learn spells received from quest completing
- for(QuestStatusMap::const_iterator itr = mQuestStatus.begin(); itr != mQuestStatus.end(); ++itr)
- {
- // skip no rewarded quests
- if(!itr->second.m_rewarded)
- continue;
-
- Quest const* quest = objmgr.GetQuestTemplate(itr->first);
- if( !quest )
- continue;
-
- learnQuestRewardedSpells(quest);
- }
-}
-
-void Player::learnSkillRewardedSpells(uint32 skill_id )
-{
- uint32 raceMask = getRaceMask();
- uint32 classMask = getClassMask();
- for (uint32 j=0; j<sSkillLineAbilityStore.GetNumRows(); ++j)
- {
- SkillLineAbilityEntry const *pAbility = sSkillLineAbilityStore.LookupEntry(j);
- if (!pAbility || pAbility->skillId!=skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
- continue;
- // Check race if set
- if (pAbility->racemask && !(pAbility->racemask & raceMask))
- continue;
- // Check class if set
- if (pAbility->classmask && !(pAbility->classmask & classMask))
- continue;
-
- if (SpellEntry const* spellentry = sSpellStore.LookupEntry(pAbility->spellId))
- {
- // Ok need learn spell
- learnSpell(pAbility->spellId);
- }
- }
-}
-
-void Player::learnSkillRewardedSpells()
-{
- for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
- {
- if(!GetUInt32Value(PLAYER_SKILL_INDEX(i)))
- continue;
-
- uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
-
- learnSkillRewardedSpells(pskill);
- }
-}
-
-void Player::SendAuraDurationsForTarget(Unit* target)
-{
- for(Unit::AuraMap::const_iterator itr = target->GetAuras().begin(); itr != target->GetAuras().end(); ++itr)
- {
- Aura* aura = itr->second;
- if(aura->GetAuraSlot() >= MAX_AURAS || aura->IsPassive() || aura->GetCasterGUID()!=GetGUID())
- continue;
-
- aura->SendAuraDurationForCaster(this);
- }
-}
-
-void Player::SetDailyQuestStatus( uint32 quest_id )
-{
- for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
- {
- if(!GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
- {
- SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,quest_id);
- m_lastDailyQuestTime = time(NULL); // last daily quest time
- m_DailyQuestChanged = true;
- break;
- }
- }
-}
-
-void Player::ResetDailyQuestStatus()
-{
- for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
- SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,0);
-
- // DB data deleted in caller
- m_DailyQuestChanged = false;
- m_lastDailyQuestTime = 0;
-}
-
-BattleGround* Player::GetBattleGround() const
-{
- if(GetBattleGroundId()==0)
- return NULL;
-
- return sBattleGroundMgr.GetBattleGround(GetBattleGroundId());
-}
-
-bool Player::InArena() const
-{
- BattleGround *bg = GetBattleGround();
- if(!bg || !bg->isArena())
- return false;
-
- return true;
-}
-
-bool Player::GetBGAccessByLevel(uint32 bgTypeId) const
-{
- // get a template bg instead of running one
- BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
- if(!bg)
- return false;
-
- if(getLevel() < bg->GetMinLevel() || getLevel() > bg->GetMaxLevel())
- return false;
-
- return true;
-}
-
-uint32 Player::GetMinLevelForBattleGroundQueueId(uint32 queue_id)
-{
- if(queue_id < 1)
- return 0;
-
- if(queue_id >=6)
- queue_id = 6;
-
- return 10*(queue_id+1);
-}
-
-uint32 Player::GetMaxLevelForBattleGroundQueueId(uint32 queue_id)
-{
- if(queue_id >=6)
- return 255; // hardcoded max level
-
- return 10*(queue_id+2)-1;
-}
-
-uint32 Player::GetBattleGroundQueueIdFromLevel() const
-{
- uint32 level = getLevel();
- if(level <= 19)
- return 0;
- else if (level > 69)
- return 6;
- else
- return level/10 - 1; // 20..29 -> 1, 30-39 -> 2, ...
-}
-
-float Player::GetReputationPriceDiscount( Creature const* pCreature ) const
-{
- FactionTemplateEntry const* vendor_faction = pCreature->getFactionTemplateEntry();
- if(!vendor_faction)
- return 1.0f;
-
- ReputationRank rank = GetReputationRank(vendor_faction->faction);
- if(rank <= REP_NEUTRAL)
- return 1.0f;
-
- return 1.0f - 0.05f* (rank - REP_NEUTRAL);
-}
-
-bool Player::IsSpellFitByClassAndRace( uint32 spell_id ) const
-{
- uint32 racemask = getRaceMask();
- uint32 classmask = getClassMask();
-
- SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id);
- SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id);
-
- for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
- {
- // skip wrong race skills
- if( _spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0)
- return false;
-
- // skip wrong class skills
- if( _spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0)
- return false;
- }
- return true;
-}
-
-bool Player::HasQuestForGO(int32 GOId)
-{
- for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
- {
- QuestStatusData qs=i->second;
- if (qs.m_status == QUEST_STATUS_INCOMPLETE)
- {
- Quest const* qinfo = objmgr.GetQuestTemplate(i->first);
- if(!qinfo)
- continue;
-
- if(GetGroup() && GetGroup()->isRaidGroup() && qinfo->GetType() != QUEST_TYPE_RAID)
- continue;
-
- for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
- {
- if (qinfo->ReqCreatureOrGOId[j]>=0) //skip non GO case
- continue;
-
- if((-1)*GOId == qinfo->ReqCreatureOrGOId[j] && qs.m_creatureOrGOcount[j] < qinfo->ReqCreatureOrGOCount[j])
- return true;
- }
- }
- }
- return false;
-}
-
-void Player::UpdateForQuestsGO()
-{
- if(m_clientGUIDs.empty())
- return;
-
- UpdateData udata;
- WorldPacket packet;
- for(ClientGUIDs::iterator itr=m_clientGUIDs.begin(); itr!=m_clientGUIDs.end(); ++itr)
- {
- if(IS_GAMEOBJECT_GUID(*itr))
- {
- GameObject *obj = HashMapHolder<GameObject>::Find(*itr);
- if(obj)
- obj->BuildValuesUpdateBlockForPlayer(&udata,this);
- }
- }
- udata.BuildPacket(&packet);
- GetSession()->SendPacket(&packet);
-}
-
-void Player::SummonIfPossible(bool agree)
-{
- if(!agree)
- {
- m_summon_expire = 0;
- return;
- }
-
- // expire and auto declined
- if(m_summon_expire < time(NULL))
- return;
-
- // stop taxi flight at summon
- if(isInFlight())
- {
- GetMotionMaster()->MovementExpired();
- m_taxi.ClearTaxiDestinations();
- }
-
- // drop flag at summon
- if(BattleGround *bg = GetBattleGround())
- bg->EventPlayerDroppedFlag(this);
-
- m_summon_expire = 0;
-
- TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z,GetOrientation());
-}
-
-void Player::RemoveItemDurations( Item *item )
-{
- for(ItemDurationList::iterator itr = m_itemDuration.begin();itr != m_itemDuration.end(); ++itr)
- {
- if(*itr==item)
- {
- m_itemDuration.erase(itr);
- break;
- }
- }
-}
-
-void Player::AddItemDurations( Item *item )
-{
- if(item->GetUInt32Value(ITEM_FIELD_DURATION))
- {
- m_itemDuration.push_back(item);
- item->SendTimeUpdate(this);
- }
-}
-
-void Player::AutoUnequipOffhandIfNeed()
-{
- Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND );
- if(!offItem)
- return;
-
- Item *mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND );
-
- if(!mainItem || mainItem->GetProto()->InventoryType != INVTYPE_2HWEAPON)
- return;
-
- ItemPosCountVec off_dest;
- uint8 off_msg = CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false );
- if( off_msg == EQUIP_ERR_OK )
- {
- RemoveItem(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
- StoreItem( off_dest, offItem, true );
- }
- else
- {
- sLog.outError("Player::EquipItem: Can's store offhand item at 2hand item equip for player (GUID: %u).",GetGUIDLow());
- }
-}
-
-bool Player::HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem)
-{
- if(spellInfo->EquippedItemClass < 0)
- return true;
-
- // scan other equipped items for same requirements (mostly 2 daggers/etc)
- // for optimize check 2 used cases only
- switch(spellInfo->EquippedItemClass)
- {
- case ITEM_CLASS_WEAPON:
- {
- for(int i= EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i)
- if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
- return true;
- break;
- }
- case ITEM_CLASS_ARMOR:
- {
- // tabard not have dependent spells
- for(int i= EQUIPMENT_SLOT_START; i< EQUIPMENT_SLOT_MAINHAND; ++i)
- if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
- if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
- return true;
-
- // shields can be equipped to offhand slot
- if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
- if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
- return true;
-
- // ranged slot can have some armor subclasses
- if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
- if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
- return true;
-
- break;
- }
- default:
- sLog.outError("HasItemFitToSpellReqirements: Not handeled spell reqirement for item class %u",spellInfo->EquippedItemClass);
- break;
- }
-
- return false;
-}
-
-void Player::RemoveItemDependentAurasAndCasts( Item * pItem )
-{
- AuraMap& auras = GetAuras();
- for(AuraMap::iterator itr = auras.begin(); itr != auras.end(); )
- {
- Aura* aura = itr->second;
-
- // skip passive (passive item dependent spells work in another way) and not self applied auras
- SpellEntry const* spellInfo = aura->GetSpellProto();
- if(aura->IsPassive() || aura->GetCasterGUID()!=GetGUID())
- {
- ++itr;
- continue;
- }
-
- // skip if not item dependent or have alternative item
- if(HasItemFitToSpellReqirements(spellInfo,pItem))
- {
- ++itr;
- continue;
- }
-
- // no alt item, remove aura, restart check
- RemoveAurasDueToSpell(aura->GetId());
- itr = auras.begin();
- }
-
- // currently casted spells can be dependent from item
- for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
- {
- if( m_currentSpells[i] && m_currentSpells[i]->getState()!=SPELL_STATE_DELAYED &&
- !HasItemFitToSpellReqirements(m_currentSpells[i]->m_spellInfo,pItem) )
- InterruptSpell(i);
- }
-}
-
-uint32 Player::GetResurrectionSpellId()
-{
- // search priceless resurrection possabilities
- uint32 prio = 0;
- uint32 spell_id = 0;
- AuraList const& dummyAuras = GetAurasByType(SPELL_AURA_DUMMY);
- for(AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
- {
- // Soulstone Resurrection // prio: 3 (max, non death persistent)
- if( prio < 2 && (*itr)->GetSpellProto()->SpellVisual == 99 && (*itr)->GetSpellProto()->SpellIconID == 92 )
- {
- switch((*itr)->GetId())
- {
- case 20707: spell_id = 3026; break; // rank 1
- case 20762: spell_id = 20758; break; // rank 2
- case 20763: spell_id = 20759; break; // rank 3
- case 20764: spell_id = 20760; break; // rank 4
- case 20765: spell_id = 20761; break; // rank 5
- case 27239: spell_id = 27240; break; // rank 6
- default:
- sLog.outError("Unhandled spell %%u: S.Resurrection",(*itr)->GetId());
- continue;
- }
-
- prio = 3;
- }
- // Twisting Nether // prio: 2 (max)
- else if((*itr)->GetId()==23701 && roll_chance_i(10))
- {
- prio = 2;
- spell_id = 23700;
- }
- }
-
- // Reincarnation (passive spell) // prio: 1
- if(prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169) && HasItemCount(17030,1))
- spell_id = 21169;
-
- return spell_id;
-}
-
-bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim)
-{
- bool PvP = pVictim->isCharmedOwnedByPlayerOrPlayer();
-
- // prepare data for near group iteration (PvP and !PvP cases)
- uint32 xp = 0;
- bool honored_kill = false;
-
- if(Group *pGroup = GetGroup())
- {
- uint32 count = 0;
- uint32 sum_level = 0;
- Player* member_with_max_level = NULL;
-
- pGroup->GetDataForXPAtKill(pVictim,count,sum_level,member_with_max_level);
-
- if(member_with_max_level)
- {
- xp = PvP ? 0 : MaNGOS::XP::Gain(member_with_max_level, pVictim);
-
- // skip in check PvP case (for speed, not used)
- bool is_raid = PvP ? false : sMapStore.LookupEntry(GetMapId())->IsRaid() && pGroup->isRaidGroup();
- bool is_dungeon = PvP ? false : sMapStore.LookupEntry(GetMapId())->IsDungeon();
- float group_rate = MaNGOS::XP::xp_in_group_rate(count,is_raid);
-
- for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- Player* pGroupGuy = itr->getSource();
- if(!pGroupGuy)
- continue;
-
- if(!pGroupGuy->IsAtGroupRewardDistance(pVictim))
- continue; // member (alive or dead) or his corpse at req. distance
-
- // honor can be in PvP and !PvP (racial leader) cases (for alive)
- if(pGroupGuy->isAlive() && pGroupGuy->RewardHonor(pVictim,count, -1, true) && pGroupGuy==this)
- honored_kill = true;
-
- // xp and reputation only in !PvP case
- if(!PvP)
- {
- float rate = group_rate * float(pGroupGuy->getLevel()) / sum_level;
-
- // if is in dungeon then all receive full reputation at kill
- // rewarded any alive/dead/near_corpse group member
- pGroupGuy->RewardReputation(pVictim,is_dungeon ? 1.0f : rate);
-
- // XP updated only for alive group member
- if(pGroupGuy->isAlive())
- {
- uint32 itr_xp = uint32(xp*rate);
-
- pGroupGuy->GiveXP(itr_xp, pVictim);
- if(Pet* pet = pGroupGuy->GetPet())
- pet->GivePetXP(itr_xp/2);
- }
-
- // quest objectives updated only for alive group member or dead but with not released body
- if(pGroupGuy->isAlive()|| !pGroupGuy->GetCorpse())
- {
- // normal creature (not pet/etc) can be only in !PvP case
- if(pVictim->GetTypeId()==TYPEID_UNIT)
- pGroupGuy->KilledMonster(pVictim->GetEntry(), pVictim->GetGUID());
- }
- }
- }
- }
- }
- else // if (!pGroup)
- {
- xp = PvP ? 0 : MaNGOS::XP::Gain(this, pVictim);
-
- // honor can be in PvP and !PvP (racial leader) cases
- if(RewardHonor(pVictim,1, -1, true))
- honored_kill = true;
-
- // xp and reputation only in !PvP case
- if(!PvP)
- {
- RewardReputation(pVictim,1);
- GiveXP(xp, pVictim);
-
- if(Pet* pet = GetPet())
- pet->GivePetXP(xp);
-
- // normal creature (not pet/etc) can be only in !PvP case
- if(pVictim->GetTypeId()==TYPEID_UNIT)
- KilledMonster(pVictim->GetEntry(),pVictim->GetGUID());
- }
- }
- return xp || honored_kill;
-}
-
-bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const
-{
- if(pRewardSource->GetDistance(this) <= sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
- return true;
-
- if(isAlive())
- return false;
-
- Corpse* corpse = GetCorpse();
- if(!corpse)
- return false;
-
- return pRewardSource->GetDistance(corpse) <= sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE);
-}
-
-uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
-{
- Item* item = GetWeaponForAttack(attType,true);
-
- // unarmmed only with base attack
- if(attType != BASE_ATTACK && !item)
- return 0;
-
- // weapon skill or (unarmed for base attack)
- uint32 skill = item ? item->GetSkill() : SKILL_UNARMED;
- return GetBaseSkillValue(skill);
-}
-
-void Player::ResurectUsingRequestData()
-{
- ResurrectPlayer(0.0f,false);
-
- if(GetMaxHealth() > m_resurrectHealth)
- SetHealth( m_resurrectHealth );
- else
- SetHealth( GetMaxHealth() );
-
- if(GetMaxPower(POWER_MANA) > m_resurrectMana)
- SetPower(POWER_MANA, m_resurrectMana );
- else
- SetPower(POWER_MANA, GetMaxPower(POWER_MANA) );
-
- SetPower(POWER_RAGE, 0 );
-
- SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY) );
-
- SpawnCorpseBones();
-
- TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation());
-}
-
-void Player::SetClientControl(Unit* target, uint8 allowMove)
-{
- WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size()+1);
- data.append(target->GetPackGUID());
- data << uint8(allowMove);
- GetSession()->SendPacket(&data);
-}
-
-void Player::UpdateZoneDependentAuras( uint32 newZone )
-{
- // remove new continent flight forms
- if( !isGameMaster() &&
- GetVirtualMapForMapAndZone(GetMapId(),newZone) != 530)
- {
- RemoveSpellsCausingAura(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED);
- RemoveSpellsCausingAura(SPELL_AURA_FLY);
- }
-
- // Some spells applied at enter into zone (with subzones)
- // Human Illusion
- // NOTE: these are removed by RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP);
- if ( newZone == 2367 ) // Old Hillsbrad Foothills
- {
- uint32 spellid = 0;
- // all horde races
- if( GetTeam() == HORDE )
- spellid = getGender() == GENDER_FEMALE ? 35481 : 35480;
- // and some alliance races
- else if( getRace() == RACE_NIGHTELF || getRace() == RACE_DRAENEI )
- spellid = getGender() == GENDER_FEMALE ? 35483 : 35482;
-
- if(spellid && !HasAura(spellid,0) )
- CastSpell(this,spellid,true);
- }
-}
-
-void Player::UpdateAreaDependentAuras( uint32 newArea )
-{
- // remove auras from spells with area limitations
- for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
- {
- // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date
- if(!IsSpellAllowedInLocation(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea))
- RemoveAura(iter);
- else
- ++iter;
- }
-
- // unmount if enter in this subzone
- if( newArea == 35)
- RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
- // Dragonmaw Illusion
- else if( newArea == 3759 || newArea == 3966 || newArea == 3939 )
- {
- if( GetDummyAura(40214) )
- {
- if( !HasAura(40216,0) )
- CastSpell(this,40216,true);
- if( !HasAura(42016,0) )
- CastSpell(this,42016,true);
- }
- }
-}
-
-uint32 Player::GetCorpseReclaimDelay(bool pvp) const
-{
- if( pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
- !pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
- {
- return copseReclaimDelay[0];
- }
-
- time_t now = time(NULL);
- // 0..2 full period
- uint32 count = (now < m_deathExpireTime) ? (m_deathExpireTime - now)/DEATH_EXPIRE_STEP : 0;
- return copseReclaimDelay[count];
-}
-
-void Player::UpdateCorpseReclaimDelay()
-{
- bool pvp = m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH;
-
- if( pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
- !pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
- return;
-
- time_t now = time(NULL);
- if(now < m_deathExpireTime)
- {
- // full and partly periods 1..3
- uint32 count = (m_deathExpireTime - now)/DEATH_EXPIRE_STEP +1;
- if(count < MAX_DEATH_COUNT)
- m_deathExpireTime = now+(count+1)*DEATH_EXPIRE_STEP;
- else
- m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP;
- }
- else
- m_deathExpireTime = now+DEATH_EXPIRE_STEP;
-}
-
-void Player::SendCorpseReclaimDelay(bool load)
-{
- Corpse* corpse = GetCorpse();
- if(!corpse)
- return;
-
- uint32 delay;
- if(load)
- {
- if(corpse->GetGhostTime() > m_deathExpireTime)
- return;
-
- bool pvp = corpse->GetType()==CORPSE_RESURRECTABLE_PVP;
-
- uint32 count;
- if( pvp && sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
- !pvp && sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
- {
- count = (m_deathExpireTime-corpse->GetGhostTime())/DEATH_EXPIRE_STEP;
- if(count>=MAX_DEATH_COUNT)
- count = MAX_DEATH_COUNT-1;
- }
- else
- count=0;
-
- time_t expected_time = corpse->GetGhostTime()+copseReclaimDelay[count];
-
- time_t now = time(NULL);
- if(now >= expected_time)
- return;
-
- delay = expected_time-now;
- }
- else
- delay = GetCorpseReclaimDelay(corpse->GetType()==CORPSE_RESURRECTABLE_PVP);
-
- //! corpse reclaim delay 30 * 1000ms or longer at often deaths
- WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4);
- data << uint32(delay*1000);
- GetSession()->SendPacket( &data );
-}
-
-Player* Player::GetNextRandomRaidMember(float radius)
-{
- Group *pGroup = GetGroup();
- if(!pGroup)
- return NULL;
-
- std::vector<Player*> nearMembers;
- nearMembers.reserve(pGroup->GetMembersCount());
-
- for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- Player* Target = itr->getSource();
-
- // IsHostileTo check duel and controlled by enemy
- if( Target && Target != this && IsWithinDistInMap(Target, radius) &&
- !Target->HasInvisibilityAura() && !IsHostileTo(Target) )
- nearMembers.push_back(Target);
- }
-
- if (nearMembers.empty())
- return NULL;
-
- uint32 randTarget = urand(0,nearMembers.size()-1);
- return nearMembers[randTarget];
-}
-
-void Player::UpdateUnderwaterState( Map* m, float x, float y, float z )
-{
- float water_z = m->GetWaterLevel(x,y);
- float height_z = m->GetHeight(x,y,z, false); // use .map base surface height
- uint8 flag1 = m->GetTerrainType(x,y);
-
- //!Underwater check, not in water if underground or above water level
- if (height_z <= INVALID_HEIGHT || z < (height_z-2) || z > (water_z - 2) )
- m_isunderwater &= 0x7A;
- else if ((z < (water_z - 2)) && (flag1 & 0x01))
- m_isunderwater |= 0x01;
-
- //!in lava check, anywhere under lava level
- if ((height_z <= INVALID_HEIGHT || z < (height_z - 0)) && (flag1 == 0x00) && IsInWater())
- m_isunderwater |= 0x80;
-}
-
-void Player::SetCanParry( bool value )
-{
- if(m_canParry==value)
- return;
-
- m_canParry = value;
- UpdateParryPercentage();
-}
-
-void Player::SetCanBlock( bool value )
-{
- if(m_canBlock==value)
- return;
-
- m_canBlock = value;
- UpdateBlockPercentage();
-}
-
-bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const
-{
- for(ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr)
- if(itr->pos == this->pos)
- return true;
-
- return false;
-}
-
-bool Player::isAllowUseBattleGroundObject()
-{
- return ( //InBattleGround() && // in battleground - not need, check in other cases
- !IsMounted() && // not mounted
- !HasStealthAura() && // not stealthed
- !HasInvisibilityAura() && // not invisible
- !HasAura(SPELL_RECENTLY_DROPPED_FLAG, 0) && // can't pickup
- isAlive() // live 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 "Language.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "World.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "UpdateMask.h"
+#include "Player.h"
+#include "SkillDiscovery.h"
+#include "QuestDef.h"
+#include "GossipDef.h"
+#include "UpdateData.h"
+#include "Channel.h"
+#include "ChannelMgr.h"
+#include "MapManager.h"
+#include "MapInstanced.h"
+#include "InstanceSaveMgr.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "ObjectMgr.h"
+#include "ObjectAccessor.h"
+#include "CreatureAI.h"
+#include "Formulas.h"
+#include "Group.h"
+#include "Guild.h"
+#include "Pet.h"
+#include "SpellAuras.h"
+#include "Util.h"
+#include "Transports.h"
+#include "Weather.h"
+#include "BattleGround.h"
+#include "BattleGroundMgr.h"
+#include "ArenaTeam.h"
+#include "Chat.h"
+#include "Database/DatabaseImpl.h"
+#include "Spell.h"
+#include "SocialMgr.h"
+
+#include <cmath>
+
+#define ZONE_UPDATE_INTERVAL 1000
+
+#define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
+#define PLAYER_SKILL_VALUE_INDEX(x) (PLAYER_SKILL_INDEX(x)+1)
+#define PLAYER_SKILL_BONUS_INDEX(x) (PLAYER_SKILL_INDEX(x)+2)
+
+#define SKILL_VALUE(x) PAIR32_LOPART(x)
+#define SKILL_MAX(x) PAIR32_HIPART(x)
+#define MAKE_SKILL_VALUE(v, m) MAKE_PAIR32(v,m)
+
+#define SKILL_TEMP_BONUS(x) int16(PAIR32_LOPART(x))
+#define SKILL_PERM_BONUS(x) int16(PAIR32_HIPART(x))
+#define MAKE_SKILL_BONUS(t, p) MAKE_PAIR32(t,p)
+
+enum CharacterFlags
+{
+ CHARACTER_FLAG_NONE = 0x00000000,
+ CHARACTER_FLAG_UNK1 = 0x00000001,
+ CHARACTER_FLAG_UNK2 = 0x00000002,
+ CHARACTER_LOCKED_FOR_TRANSFER = 0x00000004,
+ CHARACTER_FLAG_UNK4 = 0x00000008,
+ CHARACTER_FLAG_UNK5 = 0x00000010,
+ CHARACTER_FLAG_UNK6 = 0x00000020,
+ CHARACTER_FLAG_UNK7 = 0x00000040,
+ CHARACTER_FLAG_UNK8 = 0x00000080,
+ CHARACTER_FLAG_UNK9 = 0x00000100,
+ CHARACTER_FLAG_UNK10 = 0x00000200,
+ CHARACTER_FLAG_HIDE_HELM = 0x00000400,
+ CHARACTER_FLAG_HIDE_CLOAK = 0x00000800,
+ CHARACTER_FLAG_UNK13 = 0x00001000,
+ CHARACTER_FLAG_GHOST = 0x00002000,
+ CHARACTER_FLAG_RENAME = 0x00004000,
+ CHARACTER_FLAG_UNK16 = 0x00008000,
+ CHARACTER_FLAG_UNK17 = 0x00010000,
+ CHARACTER_FLAG_UNK18 = 0x00020000,
+ CHARACTER_FLAG_UNK19 = 0x00040000,
+ CHARACTER_FLAG_UNK20 = 0x00080000,
+ CHARACTER_FLAG_UNK21 = 0x00100000,
+ CHARACTER_FLAG_UNK22 = 0x00200000,
+ CHARACTER_FLAG_UNK23 = 0x00400000,
+ CHARACTER_FLAG_UNK24 = 0x00800000,
+ CHARACTER_FLAG_LOCKED_BY_BILLING = 0x01000000,
+ CHARACTER_FLAG_DECLINED = 0x02000000,
+ CHARACTER_FLAG_UNK27 = 0x04000000,
+ CHARACTER_FLAG_UNK28 = 0x08000000,
+ CHARACTER_FLAG_UNK29 = 0x10000000,
+ CHARACTER_FLAG_UNK30 = 0x20000000,
+ CHARACTER_FLAG_UNK31 = 0x40000000,
+ CHARACTER_FLAG_UNK32 = 0x80000000
+};
+
+// corpse reclaim times
+#define DEATH_EXPIRE_STEP (5*MINUTE)
+#define MAX_DEATH_COUNT 3
+
+static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
+
+//== PlayerTaxi ================================================
+
+PlayerTaxi::PlayerTaxi()
+{
+ // Taxi nodes
+ memset(m_taximask, 0, sizeof(m_taximask));
+}
+
+void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 level)
+{
+ // capital and taxi hub masks
+ switch(race)
+ {
+ case RACE_HUMAN: SetTaximaskNode(2); break; // Human
+ case RACE_ORC: SetTaximaskNode(23); break; // Orc
+ case RACE_DWARF: SetTaximaskNode(6); break; // Dwarf
+ case RACE_NIGHTELF: SetTaximaskNode(26);
+ SetTaximaskNode(27); break; // Night Elf
+ case RACE_UNDEAD_PLAYER: SetTaximaskNode(11); break;// Undead
+ case RACE_TAUREN: SetTaximaskNode(22); break; // Tauren
+ case RACE_GNOME: SetTaximaskNode(6); break; // Gnome
+ case RACE_TROLL: SetTaximaskNode(23); break; // Troll
+ case RACE_BLOODELF: SetTaximaskNode(82); break; // Blood Elf
+ case RACE_DRAENEI: SetTaximaskNode(94); break; // Draenei
+ }
+ // new continent starting masks (It will be accessible only at new map)
+ switch(Player::TeamForRace(race))
+ {
+ case ALLIANCE: SetTaximaskNode(100); break;
+ case HORDE: SetTaximaskNode(99); break;
+ }
+ // level dependent taxi hubs
+ if(level>=68)
+ SetTaximaskNode(213); //Shattered Sun Staging Area
+}
+
+void PlayerTaxi::LoadTaxiMask(const char* data)
+{
+ Tokens tokens = StrSplit(data, " ");
+
+ int index;
+ Tokens::iterator iter;
+ for (iter = tokens.begin(), index = 0;
+ (index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index)
+ {
+ // load and set bits only for existed taxi nodes
+ m_taximask[index] = sTaxiNodesMask[index] & uint32(atol((*iter).c_str()));
+ }
+}
+
+void PlayerTaxi::AppendTaximaskTo( ByteBuffer& data, bool all )
+{
+ if(all)
+ {
+ for (uint8 i=0; i<TaxiMaskSize; i++)
+ data << sTaxiNodesMask[i]; // all existed nodes
+ }
+ else
+ {
+ for (uint8 i=0; i<TaxiMaskSize; i++)
+ data << uint32(m_taximask[i]); // known nodes
+ }
+}
+
+bool PlayerTaxi::LoadTaxiDestinationsFromString( std::string values )
+{
+ ClearTaxiDestinations();
+
+ Tokens tokens = StrSplit(values," ");
+
+ for(Tokens::iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
+ {
+ uint32 node = uint32(atol(iter->c_str()));
+ AddTaxiDestination(node);
+ }
+
+ if(m_TaxiDestinations.empty())
+ return true;
+
+ // Check integrity
+ if(m_TaxiDestinations.size() < 2)
+ return false;
+
+ for(size_t i = 1; i < m_TaxiDestinations.size(); ++i)
+ {
+ uint32 cost;
+ uint32 path;
+ objmgr.GetTaxiPath(m_TaxiDestinations[i-1],m_TaxiDestinations[i],path,cost);
+ if(!path)
+ return false;
+ }
+
+ return true;
+}
+
+std::string PlayerTaxi::SaveTaxiDestinationsToString()
+{
+ if(m_TaxiDestinations.empty())
+ return "";
+
+ std::ostringstream ss;
+
+ for(size_t i=0; i < m_TaxiDestinations.size(); ++i)
+ ss << m_TaxiDestinations[i] << " ";
+
+ return ss.str();
+}
+
+uint32 PlayerTaxi::GetCurrentTaxiPath() const
+{
+ if(m_TaxiDestinations.size() < 2)
+ return 0;
+
+ uint32 path;
+ uint32 cost;
+
+ objmgr.GetTaxiPath(m_TaxiDestinations[0],m_TaxiDestinations[1],path,cost);
+
+ return path;
+}
+
+//== Player ====================================================
+
+const int32 Player::ReputationRank_Length[MAX_REPUTATION_RANK] = {36000, 3000, 3000, 3000, 6000, 12000, 21000, 1000};
+
+UpdateMask Player::updateVisualBits;
+
+Player::Player (WorldSession *session): Unit()
+{
+ m_transport = 0;
+
+ m_speakTime = 0;
+ m_speakCount = 0;
+
+ m_objectType |= TYPEMASK_PLAYER;
+ m_objectTypeId = TYPEID_PLAYER;
+
+ m_valuesCount = PLAYER_END;
+
+ m_session = session;
+
+ m_divider = 0;
+
+ m_ExtraFlags = 0;
+ if(GetSession()->GetSecurity() >= SEC_GAMEMASTER)
+ SetAcceptTicket(true);
+
+ // players always accept
+ if(GetSession()->GetSecurity() == SEC_PLAYER)
+ SetAcceptWhispers(true);
+
+ m_curSelection = 0;
+ m_lootGuid = 0;
+
+ m_comboTarget = 0;
+ m_comboPoints = 0;
+
+ m_usedTalentCount = 0;
+
+ m_regenTimer = 0;
+ m_weaponChangeTimer = 0;
+
+ m_zoneUpdateId = 0;
+ m_zoneUpdateTimer = 0;
+
+ m_areaUpdateId = 0;
+
+ m_nextSave = sWorld.getConfig(CONFIG_INTERVAL_SAVE);
+
+ // randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE]
+ // this must help in case next save after mass player load after server startup
+ m_nextSave = urand(m_nextSave/2,m_nextSave*3/2);
+
+ clearResurrectRequestData();
+
+ m_SpellModRemoveCount = 0;
+
+ memset(m_items, 0, sizeof(Item*)*PLAYER_SLOTS_COUNT);
+
+ m_social = NULL;
+
+ // group is initialized in the reference constructor
+ SetGroupInvite(NULL);
+ m_groupUpdateMask = 0;
+ m_auraUpdateMask = 0;
+
+ duel = NULL;
+
+ m_GuildIdInvited = 0;
+ m_ArenaTeamIdInvited = 0;
+
+ m_atLoginFlags = AT_LOGIN_NONE;
+
+ m_dontMove = false;
+
+ pTrader = 0;
+ ClearTrade();
+
+ m_cinematic = 0;
+
+ PlayerTalkClass = new PlayerMenu( GetSession() );
+ m_currentBuybackSlot = BUYBACK_SLOT_START;
+
+ for ( int aX = 0 ; aX < 8 ; aX++ )
+ m_Tutorials[ aX ] = 0x00;
+ m_TutorialsChanged = false;
+
+ m_DailyQuestChanged = false;
+ m_lastDailyQuestTime = 0;
+
+ m_regenTimer = 0;
+ m_weaponChangeTimer = 0;
+ m_breathTimer = 0;
+ m_isunderwater = 0;
+ m_isInWater = false;
+ m_drunkTimer = 0;
+ m_drunk = 0;
+ m_restTime = 0;
+ m_deathTimer = 0;
+ m_deathExpireTime = 0;
+
+ m_swingErrorMsg = 0;
+
+ m_DetectInvTimer = 1000;
+
+ m_bgBattleGroundID = 0;
+ for (int j=0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; j++)
+ {
+ m_bgBattleGroundQueueID[j].bgQueueType = 0;
+ m_bgBattleGroundQueueID[j].invitedToInstance = 0;
+ }
+ m_bgTeam = 0;
+
+ m_logintime = time(NULL);
+ m_Last_tick = m_logintime;
+ m_WeaponProficiency = 0;
+ m_ArmorProficiency = 0;
+ m_canParry = false;
+ m_canBlock = false;
+ m_canDualWield = false;
+ m_ammoDPS = 0.0f;
+
+ m_temporaryUnsummonedPetNumber = 0;
+ //cache for UNIT_CREATED_BY_SPELL to allow
+ //returning reagests for temporarily removed pets
+ //when dying/logging out
+ m_oldpetspell = 0;
+
+ ////////////////////Rest System/////////////////////
+ time_inn_enter=0;
+ inn_pos_mapid=0;
+ inn_pos_x=0;
+ inn_pos_y=0;
+ inn_pos_z=0;
+ m_rest_bonus=0;
+ rest_type=REST_TYPE_NO;
+ ////////////////////Rest System/////////////////////
+
+ m_mailsLoaded = false;
+ m_mailsUpdated = false;
+ unReadMails = 0;
+ m_nextMailDelivereTime = 0;
+
+ m_resetTalentsCost = 0;
+ m_resetTalentsTime = 0;
+ m_itemUpdateQueueBlocked = false;
+
+ for (int i = 0; i < MAX_MOVE_TYPE; ++i)
+ m_forced_speed_changes[i] = 0;
+
+ m_stableSlots = 0;
+
+ /////////////////// Instance System /////////////////////
+
+ m_HomebindTimer = 0;
+ m_InstanceValid = true;
+ m_dungeonDifficulty = DIFFICULTY_NORMAL;
+
+ for (int i = 0; i < BASEMOD_END; i++)
+ {
+ m_auraBaseMod[i][FLAT_MOD] = 0.0f;
+ m_auraBaseMod[i][PCT_MOD] = 1.0f;
+ }
+
+ // Honor System
+ m_lastHonorUpdateTime = time(NULL);
+
+ // Player summoning
+ m_summon_expire = 0;
+ m_summon_mapid = 0;
+ m_summon_x = 0.0f;
+ m_summon_y = 0.0f;
+ m_summon_z = 0.0f;
+
+ //Default movement to run mode
+ m_unit_movement_flags = 0;
+
+ m_miniPet = 0;
+ m_bgAfkReportedTimer = 0;
+ m_contestedPvPTimer = 0;
+
+ m_declinedname = NULL;
+}
+
+Player::~Player ()
+{
+ CleanupsBeforeDelete();
+
+ if(m_uint32Values) // only for fully created Object
+ {
+ sSocialMgr.RemovePlayerSocial(GetGUIDLow());
+ }
+
+ // Note: buy back item already deleted from DB when player was saved
+ for(int i = 0; i < PLAYER_SLOTS_COUNT; ++i)
+ {
+ if(m_items[i])
+ delete m_items[i];
+ }
+ CleanupChannels();
+
+ for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ delete itr->second;
+
+ //all mailed items should be deleted, also all mail should be deallocated
+ for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();++itr)
+ delete *itr;
+
+ for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
+ delete iter->second; //if item is duplicated... then server may crash ... but that item should be deallocated
+
+ delete PlayerTalkClass;
+
+ if (m_transport)
+ {
+ m_transport->RemovePassenger(this);
+ }
+
+ for(size_t x = 0; x < ItemSetEff.size(); x++)
+ if(ItemSetEff[x])
+ delete ItemSetEff[x];
+
+ // clean up player-instance binds, 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->RemovePlayer(this);
+
+ delete m_declinedname;
+}
+
+void Player::CleanupsBeforeDelete()
+{
+ if(m_uint32Values) // only for fully created Object
+ {
+ TradeCancel(false);
+ DuelComplete(DUEL_INTERUPTED);
+ }
+ Unit::CleanupsBeforeDelete();
+}
+
+bool Player::Create( uint32 guidlow, std::string name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId )
+{
+ Object::_Create(guidlow, 0, HIGHGUID_PLAYER);
+
+ m_name = name;
+
+ PlayerInfo const* info = objmgr.GetPlayerInfo(race, class_);
+ if(!info)
+ {
+ sLog.outError("Player have incorrect race/class pair. Can't be loaded.");
+ return false;
+ }
+
+ for (int i = 0; i < PLAYER_SLOTS_COUNT; i++)
+ m_items[i] = NULL;
+
+ //for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++)
+ //{
+ // SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+j*2,0);
+ // SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+j,0);
+ // SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+j,0);
+ //}
+
+ m_race = race;
+ m_class = class_;
+
+ SetMapId(info->mapId);
+ Relocate(info->positionX,info->positionY,info->positionZ);
+
+ ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(class_);
+ if(!cEntry)
+ {
+ sLog.outError("Class %u not found in DBC (Wrong DBC files?)",class_);
+ return false;
+ }
+
+ uint8 powertype = cEntry->powerType;
+
+ uint32 unitfield;
+
+ switch(powertype)
+ {
+ case POWER_ENERGY:
+ case POWER_MANA:
+ unitfield = 0x00000000;
+ break;
+ case POWER_RAGE:
+ unitfield = 0x00110000;
+ break;
+ default:
+ sLog.outError("Invalid default powertype %u for player (class %u)",powertype,class_);
+ return false;
+ }
+
+ SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE );
+ SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f );
+
+ switch(gender)
+ {
+ case GENDER_FEMALE:
+ SetDisplayId(info->displayId_f );
+ SetNativeDisplayId(info->displayId_f );
+ break;
+ case GENDER_MALE:
+ SetDisplayId(info->displayId_m );
+ SetNativeDisplayId(info->displayId_m );
+ break;
+ default:
+ sLog.outError("Invalid gender %u for player",gender);
+ return false;
+ break;
+ }
+
+ setFactionForRace(m_race);
+
+ SetUInt32Value(UNIT_FIELD_BYTES_0, ( ( race ) | ( class_ << 8 ) | ( gender << 16 ) | ( powertype << 24 ) ) );
+ SetUInt32Value(UNIT_FIELD_BYTES_1, unitfield);
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_UNK5 );
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE );
+ SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client
+
+ //-1 is default value
+ SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1));
+
+ SetUInt32Value(PLAYER_BYTES, (skin | (face << 8) | (hairStyle << 16) | (hairColor << 24)));
+ SetUInt32Value(PLAYER_BYTES_2, (facialHair | (0x00 << 8) | (0x00 << 16) | (0x02 << 24)));
+ SetByteValue(PLAYER_BYTES_3, 0, gender);
+
+ SetUInt32Value( PLAYER_GUILDID, 0 );
+ SetUInt32Value( PLAYER_GUILDRANK, 0 );
+ SetUInt32Value( PLAYER_GUILD_TIMESTAMP, 0 );
+
+ SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES, 0 ); // 0=disabled
+ SetUInt32Value( PLAYER_CHOSEN_TITLE, 0 );
+ SetUInt32Value( PLAYER_FIELD_KILLS, 0 );
+ SetUInt32Value( PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 0 );
+ SetUInt32Value( PLAYER_FIELD_TODAY_CONTRIBUTION, 0 );
+ SetUInt32Value( PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0 );
+
+ // set starting level
+ if(GetSession()->GetSecurity() >= SEC_MODERATOR)
+ SetUInt32Value( UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_GM_START_LEVEL) ); //ImpConfig
+ else
+ SetUInt32Value( UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_START_PLAYER_LEVEL) );
+ // set starting gold
+ SetUInt32Value( PLAYER_FIELD_COINAGE, sWorld.PlayerStartGold()*10000 );
+
+ // set starting honor
+ SetUInt32Value( PLAYER_FIELD_HONOR_CURRENCY, sWorld.getConfig(CONFIG_PLAYER_START_HONOR) );
+
+ // set starting arena pts
+ SetUInt32Value( PLAYER_FIELD_ARENA_CURRENCY, sWorld.getConfig(CONFIG_PLAYER_START_ARENAPTS) );
+
+ // start with every map explored
+ if(sWorld.getConfig(CONFIG_START_ALL_EXPLORED))
+ {
+ for (uint8 i=0; i<64; i++)
+ SetFlag(PLAYER_EXPLORED_ZONES_1+i,0xFFFFFFFF);
+ }
+
+ // Played time
+ m_Last_tick = time(NULL);
+ m_Played_time[0] = 0;
+ m_Played_time[1] = 0;
+
+ // base stats and related field values
+ InitStatsForLevel();
+ InitTaxiNodesForLevel();
+ InitTalentForLevel();
+ InitPrimaryProffesions(); // to max set before any spell added
+
+ // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
+ UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
+ SetHealth(GetMaxHealth());
+ if (getPowerType()==POWER_MANA)
+ {
+ UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intelect)
+ SetPower(POWER_MANA,GetMaxPower(POWER_MANA));
+ }
+
+ learnDefaultSpells(true);
+
+ std::list<uint16>::const_iterator action_itr[4];
+ for(int i=0; i<4; i++)
+ action_itr[i] = info->action[i].begin();
+
+ for (; action_itr[0]!=info->action[0].end() && action_itr[1]!=info->action[1].end();)
+ {
+ uint16 taction[4];
+ for(int i=0; i<4 ;i++)
+ taction[i] = (*action_itr[i]);
+
+ addActionButton((uint8)taction[0], taction[1], (uint8)taction[2], (uint8)taction[3]);
+
+ for(int i=0; i<4 ;i++)
+ ++action_itr[i];
+ }
+
+ for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr!=info->item.end(); ++item_id_itr++)
+ {
+ uint32 titem_id = item_id_itr->item_id;
+ uint32 titem_amount = item_id_itr->item_amount;
+
+ sLog.outDebug("STORAGE: Creating initial item, itemId = %u, count = %u",titem_id, titem_amount);
+
+ // attempt equip
+ uint16 eDest;
+ uint8 msg = CanEquipNewItem( NULL_SLOT, eDest, titem_id, titem_amount, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ EquipNewItem( eDest, titem_id, titem_amount, true);
+ AutoUnequipOffhandIfNeed();
+ continue; // equipped, to next
+ }
+
+ // attempt store
+ ItemPosCountVec sDest;
+ // store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
+ msg = CanStoreNewItem( INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, titem_id, titem_amount );
+ if( msg == EQUIP_ERR_OK )
+ {
+ StoreNewItem( sDest, titem_id, true, Item::GenerateItemRandomPropertyId(titem_id) );
+ continue; // stored, to next
+ }
+
+ // item can't be added
+ sLog.outError("STORAGE: Can't equip or store initial item %u for race %u class %u , error msg = %u",titem_id,race,class_,msg);
+ }
+
+ // bags and main-hand weapon must equipped at this moment
+ // now second pass for not equipped (offhand weapon/shield if it attempt equipped before main-hand weapon)
+ // or ammo not equipped in special bag
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ if(Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ {
+ uint16 eDest;
+ // equip offhand weapon/shield if it attempt equipped before main-hand weapon
+ uint8 msg = CanEquipItem( NULL_SLOT, eDest, pItem, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ RemoveItem(INVENTORY_SLOT_BAG_0, i,true);
+ EquipItem( eDest, pItem, true);
+ }
+ // move other items to more appropriate slots (ammo not equipped in special bag)
+ else
+ {
+ ItemPosCountVec sDest;
+ msg = CanStoreItem( NULL_BAG, NULL_SLOT, sDest, pItem, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ RemoveItem(INVENTORY_SLOT_BAG_0, i,true);
+ pItem = StoreItem( sDest, pItem, true);
+ }
+
+ // if this is ammo then use it
+ uint8 msg = CanUseAmmo( pItem->GetProto()->ItemId );
+ if( msg == EQUIP_ERR_OK )
+ SetAmmo( pItem->GetProto()->ItemId );
+ }
+ }
+ }
+ // all item positions resolved
+
+ return true;
+}
+
+void Player::StartMirrorTimer(MirrorTimerType Type, uint32 MaxValue)
+{
+ uint32 BreathRegen = (uint32)-1;
+
+ WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
+ data << (uint32)Type;
+ data << MaxValue;
+ data << MaxValue;
+ data << BreathRegen;
+ data << (uint8)0;
+ data << (uint32)0; // spell id
+ GetSession()->SendPacket(&data);
+}
+
+void Player::ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, uint32 Regen)
+{
+ if(Type==BREATH_TIMER)
+ m_breathTimer = ((MaxValue + 1000) - CurrentValue) / Regen;
+
+ WorldPacket data(SMSG_START_MIRROR_TIMER, (21));
+ data << (uint32)Type;
+ data << CurrentValue;
+ data << MaxValue;
+ data << Regen;
+ data << (uint8)0;
+ data << (uint32)0; // spell id
+ GetSession()->SendPacket( &data );
+}
+
+void Player::StopMirrorTimer(MirrorTimerType Type)
+{
+ if(Type==BREATH_TIMER)
+ m_breathTimer = 0;
+
+ WorldPacket data(SMSG_STOP_MIRROR_TIMER, 4);
+ data << (uint32)Type;
+ GetSession()->SendPacket( &data );
+}
+
+void Player::EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 damage)
+{
+ WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21));
+ data << (uint64)guid;
+ data << (uint8)(type!=DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL);
+ data << (uint32)damage;
+ data << (uint32)0;
+ data << (uint32)0;
+ //m_session->SendPacket(&data);
+ //Let other players see that you get damage
+ SendMessageToSet(&data, true);
+ DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+
+ if(type==DAMAGE_FALL && !isAlive()) // DealDamage not apply item durability loss at self damage
+ {
+ DEBUG_LOG("We are fall to death, loosing 10 percents durability");
+ DurabilityLossAll(0.10f,false);
+ // durability lost message
+ WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0);
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::HandleDrowning()
+{
+ if(!m_isunderwater)
+ return;
+
+ //if players is GM, have waterbreath, dead or breathing is disabled
+ if(sWorld.getConfig(CONFIG_DISABLE_BREATHING) || waterbreath || isGameMaster() || !isAlive())
+ {
+ StopMirrorTimer(BREATH_TIMER);
+ m_isunderwater = 0;
+ return;
+ }
+
+ uint32 UnderWaterTime = 1*MINUTE*1000; // default leangthL 1 min
+
+ AuraList const& mModWaterBreathing = GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING);
+ for(AuraList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i)
+ UnderWaterTime = uint32(UnderWaterTime * (100.0f + (*i)->GetModifier()->m_amount) / 100.0f);
+
+ if ((m_isunderwater & 0x01) && !(m_isunderwater & 0x80) && isAlive())
+ {
+ //single trigger timer
+ if (!(m_isunderwater & 0x02))
+ {
+ m_isunderwater|= 0x02;
+ m_breathTimer = UnderWaterTime + 1000;
+ }
+ //single trigger "Breathbar"
+ if ( m_breathTimer <= UnderWaterTime && !(m_isunderwater & 0x04))
+ {
+ m_isunderwater|= 0x04;
+ StartMirrorTimer(BREATH_TIMER, UnderWaterTime);
+ }
+ //continius trigger drowning "Damage"
+ if ((m_breathTimer == 0) && (m_isunderwater & 0x01))
+ {
+ //TODO: Check this formula
+ uint64 guid = GetGUID();
+ uint32 damage = GetMaxHealth() / 5 + urand(0, getLevel()-1);
+
+ EnvironmentalDamage(guid, DAMAGE_DROWNING,damage);
+ m_breathTimer = 2000;
+ }
+ }
+ //single trigger retract bar
+ else if (!(m_isunderwater & 0x01) && !(m_isunderwater & 0x08) && (m_isunderwater & 0x02) && (m_breathTimer > 0) && isAlive())
+ {
+ m_isunderwater = 0x08;
+
+ uint32 BreathRegen = 10;
+ ModifyMirrorTimer(BREATH_TIMER, UnderWaterTime, m_breathTimer,BreathRegen);
+ m_isunderwater = 0x10;
+ }
+ //remove bar
+ else if ((m_breathTimer < 50) && !(m_isunderwater & 0x01) && (m_isunderwater == 0x10))
+ {
+ StopMirrorTimer(BREATH_TIMER);
+ m_isunderwater = 0;
+ }
+}
+
+void Player::HandleLava()
+{
+ bool ValidArea = false;
+
+ if ((m_isunderwater & 0x80) && isAlive())
+ {
+ //Single trigger Set BreathTimer
+ if (!(m_isunderwater & 0x80))
+ {
+ m_isunderwater|= 0x04;
+ m_breathTimer = 1000;
+ }
+ //Reset BreathTimer and still in the lava
+ if (!m_breathTimer)
+ {
+ uint64 guid = GetGUID();
+ uint32 damage = urand(600, 700); // TODO: Get more detailed information about lava damage
+ uint32 dmgZone = GetZoneId(); // TODO: Find correct "lava dealing zone" flag in Area Table
+
+ // Deal lava damage only in lava zones.
+ switch(dmgZone)
+ {
+ case 0x8D:
+ ValidArea = false;
+ break;
+ case 0x94:
+ ValidArea = false;
+ break;
+ case 0x2CE:
+ ValidArea = false;
+ break;
+ case 0x2CF:
+ ValidArea = false;
+ break;
+ default:
+ if (dmgZone / 5 & 0x408)
+ ValidArea = true;
+ }
+
+ // if is valid area and is not gamemaster then deal damage
+ if ( ValidArea && !isGameMaster() )
+ EnvironmentalDamage(guid, DAMAGE_LAVA, damage);
+
+ m_breathTimer = 1000;
+ }
+
+ }
+ //Death timer disabled and WaterFlags reset
+ else if (m_deathState == DEAD)
+ {
+ m_breathTimer = 0;
+ m_isunderwater = 0;
+ }
+}
+
+///The player sobers by 256 every 10 seconds
+void Player::HandleSobering()
+{
+ m_drunkTimer = 0;
+
+ uint32 drunk = (m_drunk <= 256) ? 0 : (m_drunk - 256);
+ SetDrunkValue(drunk);
+}
+
+DrunkenState Player::GetDrunkenstateByValue(uint16 value)
+{
+ if(value >= 23000)
+ return DRUNKEN_SMASHED;
+ if(value >= 12800)
+ return DRUNKEN_DRUNK;
+ if(value & 0xFFFE)
+ return DRUNKEN_TIPSY;
+ return DRUNKEN_SOBER;
+}
+
+void Player::SetDrunkValue(uint16 newDrunkenValue, uint32 itemId)
+{
+ uint32 oldDrunkenState = Player::GetDrunkenstateByValue(m_drunk);
+
+ m_drunk = newDrunkenValue;
+ SetUInt32Value(PLAYER_BYTES_3,(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFF0001) | (m_drunk & 0xFFFE));
+
+ uint32 newDrunkenState = Player::GetDrunkenstateByValue(m_drunk);
+
+ // special drunk invisibility detection
+ if(newDrunkenState >= DRUNKEN_DRUNK)
+ m_detectInvisibilityMask |= (1<<6);
+ else
+ m_detectInvisibilityMask &= ~(1<<6);
+
+ if(newDrunkenState == oldDrunkenState)
+ return;
+
+ WorldPacket data(SMSG_CROSSED_INEBRIATION_THRESHOLD, (8+4+4));
+ data << GetGUID();
+ data << uint32(newDrunkenState);
+ data << uint32(itemId);
+
+ SendMessageToSet(&data, true);
+}
+
+void Player::Update( uint32 p_time )
+{
+ if(!IsInWorld())
+ return;
+
+ // undelivered mail
+ if(m_nextMailDelivereTime && m_nextMailDelivereTime <= time(NULL))
+ {
+ SendNewMail();
+ ++unReadMails;
+
+ // It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated)
+ m_nextMailDelivereTime = 0;
+ }
+
+ Unit::Update( p_time );
+
+ // update player only attacks
+ if(uint32 ranged_att = getAttackTimer(RANGED_ATTACK))
+ {
+ setAttackTimer(RANGED_ATTACK, (p_time >= ranged_att ? 0 : ranged_att - p_time) );
+ }
+
+ if(uint32 off_att = getAttackTimer(OFF_ATTACK))
+ {
+ setAttackTimer(OFF_ATTACK, (p_time >= off_att ? 0 : off_att - p_time) );
+ }
+
+ time_t now = time (NULL);
+
+ UpdatePvPFlag(now);
+
+ UpdateContestedPvP(p_time);
+
+ UpdateDuelFlag(now);
+
+ CheckDuelDistance(now);
+
+ UpdateAfkReport(now);
+
+ CheckExploreSystem();
+
+ // Update items that have just a limited lifetime
+ if (now>m_Last_tick)
+ UpdateItemDuration(uint32(now- m_Last_tick));
+
+ if (!m_timedquests.empty())
+ {
+ std::set<uint32>::iterator iter = m_timedquests.begin();
+ while (iter != m_timedquests.end())
+ {
+ QuestStatusData& q_status = mQuestStatus[*iter];
+ if( q_status.m_timer <= p_time )
+ {
+ uint32 quest_id = *iter;
+ ++iter; // current iter will be removed in FailTimedQuest
+ FailTimedQuest( quest_id );
+ }
+ else
+ {
+ q_status.m_timer -= p_time;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+ ++iter;
+ }
+ }
+ }
+
+ if (hasUnitState(UNIT_STAT_MELEE_ATTACKING))
+ {
+ Unit *pVictim = getVictim();
+ if( !IsNonMeleeSpellCasted(false) && pVictim)
+ {
+ // default combat reach 10
+ // TODO add weapon,skill check
+
+ float pldistance = ATTACK_DISTANCE;
+
+ if (isAttackReady(BASE_ATTACK))
+ {
+ if(!IsWithinDistInMap(pVictim, pldistance))
+ {
+ setAttackTimer(BASE_ATTACK,100);
+ if(m_swingErrorMsg != 1) // send single time (client auto repeat)
+ {
+ SendAttackSwingNotInRange();
+ m_swingErrorMsg = 1;
+ }
+ }
+ //120 degrees of radiant range
+ else if( !HasInArc( 2*M_PI/3, pVictim ))
+ {
+ setAttackTimer(BASE_ATTACK,100);
+ if(m_swingErrorMsg != 2) // send single time (client auto repeat)
+ {
+ SendAttackSwingBadFacingAttack();
+ m_swingErrorMsg = 2;
+ }
+ }
+ else
+ {
+ m_swingErrorMsg = 0; // reset swing error state
+
+ // prevent base and off attack in same time, delay attack at 0.2 sec
+ if(haveOffhandWeapon())
+ {
+ uint32 off_att = getAttackTimer(OFF_ATTACK);
+ if(off_att < ATTACK_DISPLAY_DELAY)
+ setAttackTimer(OFF_ATTACK,ATTACK_DISPLAY_DELAY);
+ }
+ AttackerStateUpdate(pVictim, BASE_ATTACK);
+ resetAttackTimer(BASE_ATTACK);
+ }
+ }
+
+ if ( haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
+ {
+ if(!IsWithinDistInMap(pVictim, pldistance))
+ {
+ setAttackTimer(OFF_ATTACK,100);
+ }
+ else if( !HasInArc( 2*M_PI/3, pVictim ))
+ {
+ setAttackTimer(OFF_ATTACK,100);
+ }
+ else
+ {
+ // prevent base and off attack in same time, delay attack at 0.2 sec
+ uint32 base_att = getAttackTimer(BASE_ATTACK);
+ if(base_att < ATTACK_DISPLAY_DELAY)
+ setAttackTimer(BASE_ATTACK,ATTACK_DISPLAY_DELAY);
+ // do attack
+ AttackerStateUpdate(pVictim, OFF_ATTACK);
+ resetAttackTimer(OFF_ATTACK);
+ }
+ }
+
+ Unit *owner = pVictim->GetOwner();
+ Unit *u = owner ? owner : pVictim;
+ if(u->IsPvP() && (!duel || duel->opponent != u))
+ {
+ UpdatePvP(true);
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
+ }
+ }
+ }
+
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
+ {
+ if(roll_chance_i(3) && GetTimeInnEnter() > 0) //freeze update
+ {
+ int time_inn = time(NULL)-GetTimeInnEnter();
+ if (time_inn >= 10) //freeze update
+ {
+ float bubble = 0.125*sWorld.getRate(RATE_REST_INGAME);
+ //speed collect rest bonus (section/in hour)
+ SetRestBonus( GetRestBonus()+ time_inn*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble );
+ UpdateInnerTime(time(NULL));
+ }
+ }
+ }
+
+ if(m_regenTimer > 0)
+ {
+ if(p_time >= m_regenTimer)
+ m_regenTimer = 0;
+ else
+ m_regenTimer -= p_time;
+ }
+
+ if (m_weaponChangeTimer > 0)
+ {
+ if(p_time >= m_weaponChangeTimer)
+ m_weaponChangeTimer = 0;
+ else
+ m_weaponChangeTimer -= p_time;
+ }
+
+ if (m_zoneUpdateTimer > 0)
+ {
+ if(p_time >= m_zoneUpdateTimer)
+ {
+ uint32 newzone = GetZoneId();
+ if( m_zoneUpdateId != newzone )
+ UpdateZone(newzone); // also update area
+ else
+ {
+ // use area updates as well
+ // needed for free far all arenas for example
+ uint32 newarea = GetAreaId();
+ if( m_areaUpdateId != newarea )
+ UpdateArea(newarea);
+
+ m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
+ }
+ }
+ else
+ m_zoneUpdateTimer -= p_time;
+ }
+
+ if (isAlive())
+ {
+ RegenerateAll();
+ }
+
+ if (m_deathState == JUST_DIED)
+ {
+ KillPlayer();
+ }
+
+ if(m_nextSave > 0)
+ {
+ if(p_time >= m_nextSave)
+ {
+ // m_nextSave reseted in SaveToDB call
+ SaveToDB();
+ sLog.outDetail("Player '%s' (GUID: %u) saved", GetName(), GetGUIDLow());
+ }
+ else
+ {
+ m_nextSave -= p_time;
+ }
+ }
+
+ //Breathtimer
+ if(m_breathTimer > 0)
+ {
+ if(p_time >= m_breathTimer)
+ m_breathTimer = 0;
+ else
+ m_breathTimer -= p_time;
+
+ }
+
+ //Handle Water/drowning
+ HandleDrowning();
+
+ //Handle lava
+ HandleLava();
+
+ //Handle detect stealth players
+ if (m_DetectInvTimer > 0)
+ {
+ if (p_time >= m_DetectInvTimer)
+ {
+ m_DetectInvTimer = 3000;
+ HandleStealthedUnitsDetection();
+ }
+ else
+ m_DetectInvTimer -= p_time;
+ }
+
+ // Played time
+ if (now > m_Last_tick)
+ {
+ uint32 elapsed = uint32(now - m_Last_tick);
+ m_Played_time[0] += elapsed; // Total played time
+ m_Played_time[1] += elapsed; // Level played time
+ m_Last_tick = now;
+ }
+
+ if (m_drunk)
+ {
+ m_drunkTimer += p_time;
+
+ if (m_drunkTimer > 10000)
+ HandleSobering();
+ }
+
+ // not auto-free ghost from body in instances
+ if(m_deathTimer > 0 && !GetBaseMap()->Instanceable())
+ {
+ if(p_time >= m_deathTimer)
+ {
+ m_deathTimer = 0;
+ BuildPlayerRepop();
+ RepopAtGraveyard();
+ }
+ else
+ m_deathTimer -= p_time;
+ }
+
+ UpdateEnchantTime(p_time);
+ UpdateHomebindTime(p_time);
+
+ // group update
+ SendUpdateToOutOfRangeGroupMembers();
+
+ Pet* pet = GetPet();
+ if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE))
+ {
+ RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
+ return;
+ }
+}
+
+void Player::setDeathState(DeathState s)
+{
+ uint32 ressSpellId = 0;
+
+ bool cur = isAlive();
+
+ if(s == JUST_DIED && cur)
+ {
+ // drunken state is cleared on death
+ SetDrunkValue(0);
+ // lost combo points at any target (targeted combo points clear in Unit::setDeathState)
+ ClearComboPoints();
+
+ clearResurrectRequestData();
+
+ // remove form before other mods to prevent incorrect stats calculation
+ RemoveAurasDueToSpell(m_ShapeShiftFormSpellId);
+
+ //FIXME: is pet dismissed at dying or releasing spirit? if second, add setDeathState(DEAD) to HandleRepopRequestOpcode and define pet unsummon here with (s == DEAD)
+ RemovePet(NULL, PET_SAVE_NOT_IN_SLOT, true);
+
+ // remove uncontrolled pets
+ RemoveMiniPet();
+ RemoveGuardians();
+
+ // save value before aura remove in Unit::setDeathState
+ ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL);
+
+ // passive spell
+ if(!ressSpellId)
+ ressSpellId = GetResurrectionSpellId();
+ }
+ Unit::setDeathState(s);
+
+ // restore resurrection spell id for player after aura remove
+ if(s == JUST_DIED && cur && ressSpellId)
+ SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId);
+
+ if(isAlive() && !cur)
+ {
+ //clear aura case after resurrection by another way (spells will be applied before next death)
+ SetUInt32Value(PLAYER_SELF_RES_SPELL, 0);
+
+ // restore default warrior stance
+ if(getClass()== CLASS_WARRIOR)
+ CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true);
+ }
+}
+
+void Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
+{
+ *p_data << GetGUID();
+ *p_data << m_name;
+
+ *p_data << getRace();
+ uint8 pClass = getClass();
+ *p_data << pClass;
+ *p_data << getGender();
+
+ uint32 bytes = GetUInt32Value(PLAYER_BYTES);
+ *p_data << uint8(bytes);
+ *p_data << uint8(bytes >> 8);
+ *p_data << uint8(bytes >> 16);
+ *p_data << uint8(bytes >> 24);
+
+ bytes = GetUInt32Value(PLAYER_BYTES_2);
+ *p_data << uint8(bytes);
+
+ *p_data << uint8(getLevel()); // player level
+ // do not use GetMap! it will spawn a new instance since the bound instances are not loaded
+ uint32 zoneId = MapManager::Instance().GetZoneId(GetMapId(), GetPositionX(),GetPositionY());
+
+ *p_data << zoneId;
+ *p_data << GetMapId();
+
+ *p_data << GetPositionX();
+ *p_data << GetPositionY();
+ *p_data << GetPositionZ();
+
+ *p_data << GetUInt32Value(PLAYER_GUILDID); // guild id
+
+ uint32 char_flags = 0;
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
+ char_flags |= CHARACTER_FLAG_HIDE_HELM;
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
+ char_flags |= CHARACTER_FLAG_HIDE_CLOAK;
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
+ char_flags |= CHARACTER_FLAG_GHOST;
+ if(HasAtLoginFlag(AT_LOGIN_RENAME))
+ char_flags |= CHARACTER_FLAG_RENAME;
+ // always send the flag if declined names aren't used
+ // to let the client select a default method of declining the name
+ if(!sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) || (result && result->Fetch()[12].GetCppString() != ""))
+ char_flags |= CHARACTER_FLAG_DECLINED;
+
+ *p_data << (uint32)char_flags; // character flags
+
+ *p_data << (uint8)1; // unknown
+
+ // Pets info
+ {
+ uint32 petDisplayId = 0;
+ uint32 petLevel = 0;
+ uint32 petFamily = 0;
+
+ // show pet at selection character in character list only for non-ghost character
+ if(result && isAlive() && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER))
+ {
+ Field* fields = result->Fetch();
+
+ uint32 entry = fields[9].GetUInt32();
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(entry);
+ if(cInfo)
+ {
+ petDisplayId = fields[10].GetUInt32();
+ petLevel = fields[11].GetUInt32();
+ petFamily = cInfo->family;
+ }
+ }
+
+ *p_data << (uint32)petDisplayId;
+ *p_data << (uint32)petLevel;
+ *p_data << (uint32)petFamily;
+ }
+
+ /*ItemPrototype const *items[EQUIPMENT_SLOT_END];
+ for (int i = 0; i < EQUIPMENT_SLOT_END; i++)
+ items[i] = NULL;
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT slot,item_template FROM character_inventory WHERE guid = '%u' AND bag = 0",GetGUIDLow());
+ if (result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint8 slot = fields[0].GetUInt8() & 255;
+ uint32 item_id = fields[1].GetUInt32();
+ if( slot >= EQUIPMENT_SLOT_END )
+ continue;
+
+ items[slot] = objmgr.GetItemPrototype(item_id);
+ if(!items[slot])
+ {
+ sLog.outError( "Player::BuildEnumData: Player %s have unknown item (id: #%u) in inventory, skipped.", GetName(),item_id );
+ continue;
+ }
+ } while (result->NextRow());
+ delete result;
+ }*/
+
+ for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++)
+ {
+ uint32 visualbase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET);
+ uint32 item_id = GetUInt32Value(visualbase);
+ const ItemPrototype * proto = objmgr.GetItemPrototype(item_id);
+ SpellItemEnchantmentEntry const *enchant = NULL;
+
+ for(uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot<=TEMP_ENCHANTMENT_SLOT; enchantSlot++)
+ {
+ uint32 enchantId = GetUInt32Value(visualbase+1+enchantSlot);
+ if(enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId))
+ break;
+ }
+
+ if (proto != NULL)
+ {
+ *p_data << (uint32)proto->DisplayInfoID;
+ *p_data << (uint8)proto->InventoryType;
+ *p_data << (uint32)(enchant?enchant->aura_id:0);
+ }
+ else
+ {
+ *p_data << (uint32)0;
+ *p_data << (uint8)0;
+ *p_data << (uint32)0; // enchant?
+ }
+ }
+ *p_data << (uint32)0; // first bag display id
+ *p_data << (uint8)0; // first bag inventory type
+ *p_data << (uint32)0; // enchant?
+}
+
+bool Player::ToggleAFK()
+{
+ ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);
+
+ bool state = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK);
+
+ // afk player not allowed in battleground
+ if(state && InBattleGround())
+ LeaveBattleground();
+
+ return state;
+}
+
+bool Player::ToggleDND()
+{
+ ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);
+
+ return HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_DND);
+}
+
+uint8 Player::chatTag() const
+{
+ // it's bitmask
+ // 0x8 - ??
+ // 0x4 - gm
+ // 0x2 - dnd
+ // 0x1 - afk
+ if(isGMChat())
+ return 4;
+ else if(isDND())
+ return 3;
+ if(isAFK())
+ return 1;
+ else
+ return 0;
+}
+
+bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options)
+{
+ if(!MapManager::IsValidMapCoord(mapid, x, y, z, orientation))
+ {
+ sLog.outError("TeleportTo: invalid map %d or absent instance template.", mapid);
+ return false;
+ }
+
+ // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
+ Pet* pet = GetPet();
+
+ MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
+
+ // don't let enter battlegrounds without assigned battleground id (for example through areatrigger)...
+ // don't let gm level > 1 either
+ if(!InBattleGround() && mEntry->IsBattleGroundOrArena())
+ return false;
+
+ bool tbc = GetSession()->IsTBC() && sWorld.getConfig(CONFIG_EXPANSION) > 0;
+
+ // normal client and TBC map
+ if(!tbc && mEntry->IsExpansionMap())
+ {
+ sLog.outDebug("Player %s using Normal client and tried teleport to non existing map %u", GetName(), mapid);
+
+ if(GetTransport())
+ RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :)
+
+ SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL1);
+
+ return false; // normal client can't teleport to this map...
+ }
+ else if(tbc) // can teleport to any existing map
+ {
+ sLog.outDebug("Player %s have TBC client and will teleported to map %u", GetName(), mapid);
+ }
+ else
+ {
+ sLog.outDebug("Player %s have normal client and will teleported to standard map %u", GetName(), mapid);
+ }
+ /*
+ only TBC (no 0x80000 and 0x10 flags...)
+ 3604590=0x37006E=0x200000 + 0x100000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x8 + 0x4 + 0x2
+
+ Kharazan (normal/TBC??), but not have 0x10 flag (accessible by normal client?)
+ 4128878=0x3F006E=0x200000 + 0x100000 + 0x80000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x8 + 0x4 + 0x2
+
+ normal+TBC maps
+ 4128894=0x3F007E=0x200000 + 0x100000 + 0x80000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x10 + 0x8 + 0x4 + 0x2
+
+ normal+TBC maps
+ 8323198=0x7F007E=0x400000 + 0x200000 + 0x100000 + 0x80000 + 0x40000 + 0x20000 + 0x10000 + 0x40 + 0x20 + 0x10 + 0x8 + 0x4 + 0x2
+ */
+
+ // if we were on a transport, leave
+ if (!(options & TELE_TO_NOT_LEAVE_TRANSPORT) && m_transport)
+ {
+ m_transport->RemovePassenger(this);
+ m_transport = NULL;
+ m_movementInfo.t_x = 0.0f;
+ m_movementInfo.t_y = 0.0f;
+ m_movementInfo.t_z = 0.0f;
+ m_movementInfo.t_o = 0.0f;
+ m_movementInfo.t_time = 0;
+ }
+
+ SetSemaphoreTeleport(true);
+
+ // The player was ported to another map and looses the duel immediatly.
+ // We have to perform this check before the teleport, otherwise the
+ // ObjectAccessor won't find the flag.
+ if (duel && this->GetMapId()!=mapid)
+ {
+ GameObject* obj = ObjectAccessor::GetGameObject(*this, GetUInt64Value(PLAYER_DUEL_ARBITER));
+ if (obj)
+ DuelComplete(DUEL_FLED);
+ }
+
+ // reset movement flags at teleport, because player will continue move with these flags after teleport
+ SetUnitMovementFlags(0);
+
+ if ((this->GetMapId() == mapid) && (!m_transport))
+ {
+ // prepare zone change detect
+ uint32 old_zone = GetZoneId();
+
+ // near teleport
+ if(!GetSession()->PlayerLogout())
+ {
+ WorldPacket data;
+ BuildTeleportAckMsg(&data, x, y, z, orientation);
+ GetSession()->SendPacket(&data);
+ SetPosition( x, y, z, orientation, true);
+ }
+ else
+ // this will be used instead of the current location in SaveToDB
+ m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
+
+ //BuildHeartBeatMsg(&data);
+ //SendMessageToSet(&data, true);
+ if (!(options & TELE_TO_NOT_UNSUMMON_PET))
+ {
+ //same map, only remove pet if out of range
+ if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE))
+ {
+ if(pet->isControlled() && !pet->isTemporarySummoned() )
+ m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
+ else
+ m_temporaryUnsummonedPetNumber = 0;
+
+ RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
+ }
+ }
+
+ if(!(options & TELE_TO_NOT_LEAVE_COMBAT))
+ CombatStop();
+
+ if (!(options & TELE_TO_NOT_UNSUMMON_PET))
+ {
+ // resummon pet
+ if(pet && m_temporaryUnsummonedPetNumber)
+ {
+ Pet* NewPet = new Pet;
+ if(!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
+ delete NewPet;
+
+ m_temporaryUnsummonedPetNumber = 0;
+ }
+ }
+
+ 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())
+ {
+ // honorless target
+ if(pvpInfo.inHostileArea)
+ CastSpell(this, 2479, true);
+ }
+ }
+ else
+ {
+ // far teleport to another map
+ Map* oldmap = IsInWorld() ? MapManager::Instance().GetMap(GetMapId(), this) : NULL;
+ // check if we can enter before stopping combat / removing pet / totems / interrupting spells
+
+ // Check enter rights before map getting to avoid creating instance copy for player
+ // this check not dependent from map instance copy and same for all instance copies of selected map
+ if (!MapManager::Instance().CanPlayerEnter(mapid, this))
+ {
+ SetSemaphoreTeleport(false);
+ return false;
+ }
+
+ // If the map is not created, assume it is possible to enter it.
+ // It will be created in the WorldPortAck.
+ Map *map = MapManager::Instance().FindMap(mapid);
+ if (!map || map->CanEnter(this))
+ {
+ SetSelection(0);
+
+ CombatStop();
+
+ ResetContestedPvP();
+
+ // remove player from battleground on far teleport (when changing maps)
+ if(BattleGround const* bg = GetBattleGround())
+ {
+ // Note: at battleground join battleground id set before teleport
+ // and we already will found "current" battleground
+ // just need check that this is targeted map or leave
+ if(bg->GetMapId() != mapid)
+ LeaveBattleground(false); // don't teleport to entry point
+ }
+
+ // remove pet on map change
+ if (pet)
+ {
+ //leaving map -> delete pet right away (doing this later will cause problems)
+ if(pet->isControlled() && !pet->isTemporarySummoned())
+ m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
+ else
+ m_temporaryUnsummonedPetNumber = 0;
+
+ RemovePet(pet, PET_SAVE_NOT_IN_SLOT);
+ }
+
+ // remove all dyn objects
+ RemoveAllDynObjects();
+
+ // stop spellcasting
+ // not attempt interrupt teleportation spell at caster teleport
+ if(!(options & TELE_TO_SPELL))
+ if(IsNonMeleeSpellCasted(true))
+ InterruptNonMeleeSpells(true);
+
+ if(!GetSession()->PlayerLogout())
+ {
+ // send transfer packets
+ WorldPacket data(SMSG_TRANSFER_PENDING, (4+4+4));
+ data << uint32(mapid);
+ if (m_transport)
+ {
+ data << m_transport->GetEntry() << GetMapId();
+ }
+ GetSession()->SendPacket(&data);
+
+ data.Initialize(SMSG_NEW_WORLD, (20));
+ if (m_transport)
+ {
+ data << (uint32)mapid << m_movementInfo.t_x << m_movementInfo.t_y << m_movementInfo.t_z << m_movementInfo.t_o;
+ }
+ else
+ {
+ data << (uint32)mapid << (float)x << (float)y << (float)z << (float)orientation;
+ }
+ GetSession()->SendPacket( &data );
+ SendSavedInstances();
+
+ // remove from old map now
+ if(oldmap) oldmap->Remove(this, false);
+ }
+
+ // new final coordinates
+ float final_x = x;
+ float final_y = y;
+ float final_z = z;
+ float final_o = orientation;
+
+ if(m_transport)
+ {
+ final_x += m_movementInfo.t_x;
+ final_y += m_movementInfo.t_y;
+ final_z += m_movementInfo.t_z;
+ final_o += m_movementInfo.t_o;
+ }
+
+ m_teleport_dest = WorldLocation(mapid, final_x, final_y, final_z, final_o);
+ // if the player is saved before worldportack (at logout for example)
+ // this will be used instead of the current location in SaveToDB
+
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP);
+
+ // move packet sent by client always after far teleport
+ // SetPosition(final_x, final_y, final_z, final_o, true);
+ SetDontMove(true);
+
+ // code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
+ }
+ else
+ return false;
+ }
+ return true;
+}
+
+void Player::AddToWorld()
+{
+ ///- Do not add/remove the player from the object storage
+ ///- It will crash when updating the ObjectAccessor
+ ///- The player should only be added when logging in
+ Unit::AddToWorld();
+
+ for(int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++)
+ {
+ if(m_items[i])
+ m_items[i]->AddToWorld();
+ }
+}
+
+void Player::RemoveFromWorld()
+{
+ // cleanup
+ if(IsInWorld())
+ {
+ ///- Release charmed creatures, unsummon totems and remove pets/guardians
+ Uncharm();
+ UnsummonAllTotems();
+ RemoveMiniPet();
+ RemoveGuardians();
+ }
+
+ for(int i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++)
+ {
+ if(m_items[i])
+ m_items[i]->RemoveFromWorld();
+ }
+
+ ///- Do not add/remove the player from the object storage
+ ///- It will crash when updating the ObjectAccessor
+ ///- The player should only be removed when logging out
+ Unit::RemoveFromWorld();
+}
+
+void Player::RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker )
+{
+ float addRage;
+
+ float rageconversion = ((0.0091107836 * getLevel()*getLevel())+3.225598133*getLevel())+4.2652911;
+
+ if(attacker)
+ {
+ addRage = ((damage/rageconversion*7.5 + weaponSpeedHitFactor)/2);
+
+ // talent who gave more rage on attack
+ addRage *= 1.0f + GetTotalAuraModifier(SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT) / 100.0f;
+ }
+ else
+ {
+ addRage = damage/rageconversion*2.5;
+
+ // Berserker Rage effect
+ if(HasAura(18499,0))
+ addRage *= 1.3;
+ }
+
+ addRage *= sWorld.getRate(RATE_POWER_RAGE_INCOME);
+
+ ModifyPower(POWER_RAGE, uint32(addRage*10));
+}
+
+void Player::RegenerateAll()
+{
+ if (m_regenTimer != 0)
+ return;
+ uint32 regenDelay = 2000;
+
+ // Not in combat or they have regeneration
+ if( !isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) ||
+ HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT) || IsPolymorphed() )
+ {
+ RegenerateHealth();
+ if (!isInCombat() && !HasAuraType(SPELL_AURA_INTERRUPT_REGEN))
+ Regenerate(POWER_RAGE);
+ }
+
+ Regenerate( POWER_ENERGY );
+
+ Regenerate( POWER_MANA );
+
+ m_regenTimer = regenDelay;
+}
+
+void Player::Regenerate(Powers power)
+{
+ uint32 curValue = GetPower(power);
+ uint32 maxValue = GetMaxPower(power);
+
+ float addvalue = 0.0f;
+
+ switch (power)
+ {
+ case POWER_MANA:
+ {
+ bool recentCast = IsUnderLastManaUseEffect();
+ float ManaIncreaseRate = sWorld.getRate(RATE_POWER_MANA);
+ if (recentCast)
+ {
+ // Mangos Updates Mana in intervals of 2s, which is correct
+ addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT) * ManaIncreaseRate * 2.00f;
+ }
+ else
+ {
+ addvalue = GetFloatValue(PLAYER_FIELD_MOD_MANA_REGEN) * ManaIncreaseRate * 2.00f;
+ }
+ } break;
+ case POWER_RAGE: // Regenerate rage
+ {
+ float RageDecreaseRate = sWorld.getRate(RATE_POWER_RAGE_LOSS);
+ addvalue = 30 * RageDecreaseRate; // 3 rage by tick
+ } break;
+ case POWER_ENERGY: // Regenerate energy (rogue)
+ addvalue = 20;
+ break;
+ case POWER_FOCUS:
+ case POWER_HAPPINESS:
+ break;
+ }
+
+ // Mana regen calculated in Player::UpdateManaRegen()
+ // Exist only for POWER_MANA, POWER_ENERGY, POWER_FOCUS auras
+ if(power != POWER_MANA)
+ {
+ AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
+ for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue == power)
+ addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f;
+ }
+
+ if (power != POWER_RAGE)
+ {
+ curValue += uint32(addvalue);
+ if (curValue > maxValue)
+ curValue = maxValue;
+ }
+ else
+ {
+ if(curValue <= uint32(addvalue))
+ curValue = 0;
+ else
+ curValue -= uint32(addvalue);
+ }
+ SetPower(power, curValue);
+}
+
+void Player::RegenerateHealth()
+{
+ uint32 curValue = GetHealth();
+ uint32 maxValue = GetMaxHealth();
+
+ if (curValue >= maxValue) return;
+
+ float HealthIncreaseRate = sWorld.getRate(RATE_HEALTH);
+
+ float addvalue = 0.0f;
+
+ // polymorphed case
+ if ( IsPolymorphed() )
+ addvalue = GetMaxHealth()/3;
+ // normal regen case (maybe partly in combat case)
+ else if (!isInCombat() || HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) )
+ {
+ addvalue = OCTRegenHPPerSpirit()* HealthIncreaseRate;
+ if (!isInCombat())
+ {
+ AuraList const& mModHealthRegenPct = GetAurasByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
+ for(AuraList::const_iterator i = mModHealthRegenPct.begin(); i != mModHealthRegenPct.end(); ++i)
+ addvalue *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f;
+ }
+ else if(HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT))
+ addvalue *= GetTotalAuraModifier(SPELL_AURA_MOD_REGEN_DURING_COMBAT) / 100.0f;
+
+ if(!IsStandState())
+ addvalue *= 1.5;
+ }
+
+ // always regeneration bonus (including combat)
+ addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT);
+
+ if(addvalue < 0)
+ addvalue = 0;
+
+ ModifyHealth(int32(addvalue));
+}
+
+bool Player::CanInteractWithNPCs(bool alive) const
+{
+ if(alive && !isAlive())
+ return false;
+ if(isInFlight())
+ return false;
+
+ return true;
+}
+
+bool Player::IsUnderWater() const
+{
+ return IsInWater() &&
+ GetPositionZ() < (MapManager::Instance().GetBaseMap(GetMapId())->GetWaterLevel(GetPositionX(),GetPositionY())-2);
+}
+
+void Player::SetInWater(bool apply)
+{
+ if(m_isInWater==apply)
+ return;
+
+ //define player in water by opcodes
+ //move player's guid into HateOfflineList of those mobs
+ //which can't swim and move guid back into ThreatList when
+ //on surface.
+ //TODO: exist also swimming mobs, and function must be symmetric to enter/leave water
+ m_isInWater = apply;
+
+ // remove auras that need water/land
+ RemoveAurasWithInterruptFlags(apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER);
+
+ getHostilRefManager().updateThreatTables();
+}
+
+void Player::SetGameMaster(bool on)
+{
+ if(on)
+ {
+ m_ExtraFlags |= PLAYER_EXTRA_GM_ON;
+ setFaction(35);
+ SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
+
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
+ ResetContestedPvP();
+
+ getHostilRefManager().setOnlineOfflineState(false);
+ CombatStop();
+ }
+ else
+ {
+ m_ExtraFlags &= ~ PLAYER_EXTRA_GM_ON;
+ setFactionForRace(getRace());
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
+
+ // restore FFA PvP Server state
+ if(sWorld.IsFFAPvPRealm())
+ SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+
+ // restore FFA PvP area state, remove not allowed for GM mounts
+ UpdateArea(m_areaUpdateId);
+
+ getHostilRefManager().setOnlineOfflineState(true);
+ }
+
+ ObjectAccessor::UpdateVisibilityForPlayer(this);
+}
+
+void Player::SetGMVisible(bool on)
+{
+ if(on)
+ {
+ m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; //remove flag
+
+ // Reapply stealth/invisibility if active or show if not any
+ if(HasAuraType(SPELL_AURA_MOD_STEALTH))
+ SetVisibility(VISIBILITY_GROUP_STEALTH);
+ else if(HasAuraType(SPELL_AURA_MOD_INVISIBILITY))
+ SetVisibility(VISIBILITY_GROUP_INVISIBILITY);
+ else
+ SetVisibility(VISIBILITY_ON);
+ }
+ else
+ {
+ m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE; //add flag
+
+ SetAcceptWhispers(false);
+ SetGameMaster(true);
+
+ SetVisibility(VISIBILITY_OFF);
+ }
+}
+
+bool Player::IsGroupVisibleFor(Player* p) const
+{
+ switch(sWorld.getConfig(CONFIG_GROUP_VISIBILITY))
+ {
+ default: return IsInSameGroupWith(p);
+ case 1: return IsInSameRaidWith(p);
+ case 2: return GetTeam()==p->GetTeam();
+ }
+}
+
+bool Player::IsInSameGroupWith(Player const* p) const
+{
+ return p==this || GetGroup() != NULL &&
+ GetGroup() == p->GetGroup() &&
+ GetGroup()->SameSubGroup((Player*)this, (Player*)p);
+}
+
+///- If the player is invited, remove him. If the group if then only 1 person, disband the group.
+/// \todo Shouldn't we also check if there is no other invitees before disbanding the group?
+void Player::UninviteFromGroup()
+{
+ if(GetGroupInvite()) // uninvited invitee
+ {
+ Group* group = GetGroupInvite();
+ group->RemoveInvite(this);
+
+ if(group->GetMembersCount() <= 1) // group has just 1 member => disband
+ {
+ if(group->IsCreated())
+ {
+ group->Disband(true);
+ objmgr.RemoveGroup(group);
+ }
+ else
+ group->RemoveAllInvites();
+
+ delete group;
+ }
+ }
+}
+
+void Player::RemoveFromGroup(Group* group, uint64 guid)
+{
+ if(group)
+ {
+ if (group->RemoveMember(guid, 0) <= 1)
+ {
+ // group->Disband(); already disbanded in RemoveMember
+ objmgr.RemoveGroup(group);
+ delete group;
+ // removemember sets the player's group pointer to NULL
+ }
+ }
+}
+
+void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 RestXP)
+{
+ WorldPacket data(SMSG_LOG_XPGAIN, 21);
+ data << uint64(victim ? victim->GetGUID() : 0); // guid
+ data << uint32(GivenXP+RestXP); // given experience
+ data << uint8(victim ? 0 : 1); // 00-kill_xp type, 01-non_kill_xp type
+ if(victim)
+ {
+ data << uint32(GivenXP); // experience without rested bonus
+ data << float(1); // 1 - none 0 - 100% group bonus output
+ }
+ data << uint8(0); // new 2.4.0
+ GetSession()->SendPacket(&data);
+}
+
+void Player::GiveXP(uint32 xp, Unit* victim)
+{
+ if ( xp < 1 )
+ return;
+
+ if(!isAlive())
+ return;
+
+ uint32 level = getLevel();
+
+ // XP to money conversion processed in Player::RewardQuest
+ if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ return;
+
+ // handle SPELL_AURA_MOD_XP_PCT auras
+ Unit::AuraList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_XP_PCT);
+ for(Unit::AuraList::const_iterator i = ModXPPctAuras.begin();i != ModXPPctAuras.end(); ++i)
+ xp = uint32(xp*(1.0f + (*i)->GetModifier()->m_amount / 100.0f));
+
+ // XP resting bonus for kill
+ uint32 rested_bonus_xp = victim ? GetXPRestBonus(xp) : 0;
+
+ SendLogXPGain(xp,victim,rested_bonus_xp);
+
+ uint32 curXP = GetUInt32Value(PLAYER_XP);
+ uint32 nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
+ uint32 newXP = curXP + xp + rested_bonus_xp;
+
+ while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
+ {
+ newXP -= nextLvlXP;
+
+ if ( level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
+ GiveLevel(level + 1);
+
+ level = getLevel();
+ nextLvlXP = GetUInt32Value(PLAYER_NEXT_LEVEL_XP);
+ }
+
+ SetUInt32Value(PLAYER_XP, newXP);
+}
+
+// Update player to next level
+// Current player experience not update (must be update by caller)
+void Player::GiveLevel(uint32 level)
+{
+ if ( level == getLevel() )
+ return;
+
+ PlayerLevelInfo info;
+ objmgr.GetPlayerLevelInfo(getRace(),getClass(),level,&info);
+
+ PlayerClassLevelInfo classInfo;
+ objmgr.GetPlayerClassLevelInfo(getClass(),level,&classInfo);
+
+ // send levelup info to client
+ WorldPacket data(SMSG_LEVELUP_INFO, (4+4+MAX_POWERS*4+MAX_STATS*4));
+ data << uint32(level);
+ data << uint32(int32(classInfo.basehealth) - int32(GetCreateHealth()));
+ // for(int i = 0; i < MAX_POWERS; ++i) // Powers loop (0-6)
+ data << uint32(int32(classInfo.basemana) - int32(GetCreateMana()));
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ // end for
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) // Stats loop (0-4)
+ data << uint32(int32(info.stats[i]) - GetCreateStat(Stats(i)));
+
+ GetSession()->SendPacket(&data);
+
+ SetUInt32Value(PLAYER_NEXT_LEVEL_XP, MaNGOS::XP::xp_to_level(level));
+
+ //update level, max level of skills
+ if(getLevel()!= level)
+ m_Played_time[1] = 0; // Level Played Time reset
+ SetLevel(level);
+ UpdateMaxSkills();
+
+ // save base values (bonuses already included in stored stats
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
+ SetCreateStat(Stats(i), info.stats[i]);
+
+ SetCreateHealth(classInfo.basehealth);
+ SetCreateMana(classInfo.basemana);
+
+ InitTalentForLevel();
+ InitTaxiNodesForLevel();
+
+ UpdateAllStats();
+
+ if(sWorld.getConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up
+ UpdateSkillsToMaxSkillsForLevel();
+
+ // set current level health and mana/energy to maximum after applying all mods.
+ SetHealth(GetMaxHealth());
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
+ SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
+ if(GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
+ SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
+ SetPower(POWER_FOCUS, 0);
+ SetPower(POWER_HAPPINESS, 0);
+
+ // give level to summoned pet
+ Pet* pet = GetPet();
+ if(pet && pet->getPetType()==SUMMON_PET)
+ pet->GivePetLevel(level);
+}
+
+void Player::InitTalentForLevel()
+{
+ uint32 level = getLevel();
+ // talents base at level diff ( talents = level - 9 but some can be used already)
+ if(level < 10)
+ {
+ // Remove all talent points
+ if(m_usedTalentCount > 0) // Free any used talents
+ {
+ resetTalents(true);
+ SetFreeTalentPoints(0);
+ }
+ }
+ else
+ {
+ uint32 talentPointsForLevel = uint32((level-9)*sWorld.getRate(RATE_TALENT));
+ // if used more that have then reset
+ if(m_usedTalentCount > talentPointsForLevel)
+ {
+ if (GetSession()->GetSecurity() < SEC_ADMINISTRATOR)
+ resetTalents(true);
+ else
+ SetFreeTalentPoints(0);
+ }
+ // else update amount of free points
+ else
+ SetFreeTalentPoints(talentPointsForLevel-m_usedTalentCount);
+ }
+}
+
+void Player::InitStatsForLevel(bool reapplyMods)
+{
+ if(reapplyMods) //reapply stats values only on .reset stats (level) command
+ _RemoveAllStatBonuses();
+
+ PlayerClassLevelInfo classInfo;
+ objmgr.GetPlayerClassLevelInfo(getClass(),getLevel(),&classInfo);
+
+ PlayerLevelInfo info;
+ objmgr.GetPlayerLevelInfo(getRace(),getClass(),getLevel(),&info);
+
+ SetUInt32Value(PLAYER_FIELD_MAX_LEVEL, sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) );
+ SetUInt32Value(PLAYER_NEXT_LEVEL_XP, MaNGOS::XP::xp_to_level(getLevel()));
+
+ UpdateMaxSkills ();
+
+ // set default cast time multiplier
+ SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
+
+ // reset size before reapply auras
+ SetFloatValue(OBJECT_FIELD_SCALE_X,1.0f);
+
+ // save base values (bonuses already included in stored stats
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
+ SetCreateStat(Stats(i), info.stats[i]);
+
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i)
+ SetStat(Stats(i), info.stats[i]);
+
+ SetCreateHealth(classInfo.basehealth);
+
+ //set create powers
+ SetCreateMana(classInfo.basemana);
+
+ SetArmor(int32(m_createStats[STAT_AGILITY]*2));
+
+ InitStatBuffMods();
+
+ //reset rating fields values
+ for(uint16 index = PLAYER_FIELD_COMBAT_RATING_1; index < PLAYER_FIELD_COMBAT_RATING_1 + MAX_COMBAT_RATING; ++index)
+ SetUInt32Value(index, 0);
+
+ SetUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS,0);
+ for (int i = 0; i < 7; i++)
+ {
+ SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG+i, 0);
+ SetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, 0);
+ SetFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT+i, 1.00f);
+ }
+
+ //reset attack power, damage and attack speed fields
+ SetFloatValue(UNIT_FIELD_BASEATTACKTIME, 2000.0f );
+ SetFloatValue(UNIT_FIELD_BASEATTACKTIME + 1, 2000.0f ); // offhand attack time
+ SetFloatValue(UNIT_FIELD_RANGEDATTACKTIME, 2000.0f );
+
+ SetFloatValue(UNIT_FIELD_MINDAMAGE, 0.0f );
+ SetFloatValue(UNIT_FIELD_MAXDAMAGE, 0.0f );
+ SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, 0.0f );
+ SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, 0.0f );
+ SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE, 0.0f );
+ SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE, 0.0f );
+
+ SetUInt32Value(UNIT_FIELD_ATTACK_POWER, 0 );
+ SetUInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, 0 );
+ SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER,0.0f);
+ SetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0 );
+ SetUInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MODS,0 );
+ SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER,0.0f);
+
+ // Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
+ SetFloatValue(PLAYER_CRIT_PERCENTAGE,0.0f);
+ SetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE,0.0f);
+ SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE,0.0f);
+
+ // Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
+ for (uint8 i = 0; i < 7; ++i)
+ SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1+i, 0.0f);
+
+ SetFloatValue(PLAYER_PARRY_PERCENTAGE, 0.0f);
+ SetFloatValue(PLAYER_BLOCK_PERCENTAGE, 0.0f);
+ SetUInt32Value(PLAYER_SHIELD_BLOCK, 0);
+
+ // Dodge percentage
+ SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f);
+
+ // set armor (resistance 0) to original value (create_agility*2)
+ SetArmor(int32(m_createStats[STAT_AGILITY]*2));
+ SetResistanceBuffMods(SpellSchools(0), true, 0.0f);
+ SetResistanceBuffMods(SpellSchools(0), false, 0.0f);
+ // set other resistance to original value (0)
+ for (int i = 1; i < MAX_SPELL_SCHOOL; i++)
+ {
+ SetResistance(SpellSchools(i), 0);
+ SetResistanceBuffMods(SpellSchools(i), true, 0.0f);
+ SetResistanceBuffMods(SpellSchools(i), false, 0.0f);
+ }
+
+ SetUInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,0);
+ SetUInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,0);
+ for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ {
+ SetFloatValue(UNIT_FIELD_POWER_COST_MODIFIER+i,0.0f);
+ SetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,0.0f);
+ }
+ // Init data for form but skip reapply item mods for form
+ InitDataForForm(reapplyMods);
+
+ // save new stats
+ for (int i = POWER_MANA; i < MAX_POWERS; i++)
+ SetMaxPower(Powers(i), uint32(GetCreatePowers(Powers(i))));
+
+ SetMaxHealth(classInfo.basehealth); // stamina bonus will applied later
+
+ // cleanup mounted state (it will set correctly at aura loading if player saved at mount.
+ SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
+
+ // cleanup unit flags (will be re-applied if need at aura load).
+ RemoveFlag( UNIT_FIELD_FLAGS,
+ UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 |
+ UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED |
+ UNIT_FLAG_DISABLE_ROTATE | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED |
+ UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_NOT_SELECTABLE |
+ UNIT_FLAG_SKINNABLE | UNIT_FLAG_MOUNT | UNIT_FLAG_TAXI_FLIGHT );
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE ); // must be set
+
+ // cleanup player flags (will be re-applied if need at aura load), to avoid have ghost flag without ghost aura, for example.
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK | PLAYER_FLAGS_DND | PLAYER_FLAGS_GM | PLAYER_FLAGS_GHOST | PLAYER_FLAGS_FFA_PVP);
+
+ SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x00); // one form stealth modified bytes
+
+ // restore if need some important flags
+ SetUInt32Value(PLAYER_FIELD_BYTES2, 0 ); // flags empty by default
+
+ if(reapplyMods) //reapply stats values only on .reset stats (level) command
+ _ApplyAllStatBonuses();
+
+ // set current level health and mana/energy to maximum after applying all mods.
+ SetHealth(GetMaxHealth());
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
+ SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY));
+ if(GetPower(POWER_RAGE) > GetMaxPower(POWER_RAGE))
+ SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
+ SetPower(POWER_FOCUS, 0);
+ SetPower(POWER_HAPPINESS, 0);
+}
+
+void Player::SendInitialSpells()
+{
+ uint16 spellCount = 0;
+
+ WorldPacket data(SMSG_INITIAL_SPELLS, (1+2+4*m_spells.size()+2+m_spellCooldowns.size()*(2+2+2+4+4)));
+ data << uint8(0);
+
+ size_t countPos = data.wpos();
+ data << uint16(spellCount); // spell count placeholder
+
+ for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+
+ if(!itr->second->active || itr->second->disabled)
+ continue;
+
+ data << uint16(itr->first);
+ //data << uint16(itr->second->slotId);
+ data << uint16(0); // it's not slot id
+
+ spellCount +=1;
+ }
+
+ data.put<uint16>(countPos,spellCount); // write real count value
+
+ uint16 spellCooldowns = m_spellCooldowns.size();
+ data << uint16(spellCooldowns);
+ for(SpellCooldowns::const_iterator itr=m_spellCooldowns.begin(); itr!=m_spellCooldowns.end(); itr++)
+ {
+ SpellEntry const *sEntry = sSpellStore.LookupEntry(itr->first);
+ if(!sEntry)
+ continue;
+
+ data << uint16(itr->first);
+
+ time_t cooldown = 0;
+ time_t curTime = time(NULL);
+ if(itr->second.end > curTime)
+ cooldown = (itr->second.end-curTime)*1000;
+
+ data << uint16(itr->second.itemid); // cast item id
+ data << uint16(sEntry->Category); // spell category
+ if(sEntry->Category) // may be wrong, but anyway better than nothing...
+ {
+ data << uint32(0);
+ data << uint32(cooldown);
+ }
+ else
+ {
+ data << uint32(cooldown);
+ data << uint32(0);
+ }
+ }
+
+ GetSession()->SendPacket(&data);
+
+ sLog.outDetail( "CHARACTER: Sent Initial Spells" );
+}
+
+void Player::RemoveMail(uint32 id)
+{
+ for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();++itr)
+ {
+ if ((*itr)->messageID == id)
+ {
+ //do not delete item, because Player::removeMail() is called when returning mail to sender.
+ m_mail.erase(itr);
+ return;
+ }
+ }
+}
+
+void Player::SendMailResult(uint32 mailId, uint32 mailAction, uint32 mailError, uint32 equipError, uint32 item_guid, uint32 item_count)
+{
+ WorldPacket data(SMSG_SEND_MAIL_RESULT, (4+4+4+(mailError == MAIL_ERR_BAG_FULL?4:(mailAction == MAIL_ITEM_TAKEN?4+4:0))));
+ data << (uint32) mailId;
+ data << (uint32) mailAction;
+ data << (uint32) mailError;
+ if ( mailError == MAIL_ERR_BAG_FULL )
+ data << (uint32) equipError;
+ else if( mailAction == MAIL_ITEM_TAKEN )
+ {
+ data << (uint32) item_guid; // item guid low?
+ data << (uint32) item_count; // item count?
+ }
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendNewMail()
+{
+ // deliver undelivered mail
+ WorldPacket data(SMSG_RECEIVED_MAIL, 4);
+ data << (uint32) 0;
+ GetSession()->SendPacket(&data);
+}
+
+void Player::UpdateNextMailTimeAndUnreads()
+{
+ // calculate next delivery time (min. from non-delivered mails
+ // and recalculate unReadMail
+ time_t cTime = time(NULL);
+ m_nextMailDelivereTime = 0;
+ unReadMails = 0;
+ for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
+ {
+ if((*itr)->deliver_time > cTime)
+ {
+ if(!m_nextMailDelivereTime || m_nextMailDelivereTime > (*itr)->deliver_time)
+ m_nextMailDelivereTime = (*itr)->deliver_time;
+ }
+ else if(((*itr)->checked & MAIL_CHECK_MASK_READ) == 0)
+ ++unReadMails;
+ }
+}
+
+void Player::AddNewMailDeliverTime(time_t deliver_time)
+{
+ if(deliver_time <= time(NULL)) // ready now
+ {
+ ++unReadMails;
+ SendNewMail();
+ }
+ else // not ready and no have ready mails
+ {
+ if(!m_nextMailDelivereTime || m_nextMailDelivereTime > deliver_time)
+ m_nextMailDelivereTime = deliver_time;
+ }
+}
+
+bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool loading, uint16 slot_id, bool disabled)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+ if (!spellInfo)
+ {
+ // do character spell book cleanup (all characters)
+ if(loading && !learning) // spell load case
+ {
+ sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.",spell_id);
+ CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id);
+ }
+ else
+ sLog.outError("Player::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
+
+ return false;
+ }
+
+ if(!SpellMgr::IsSpellValid(spellInfo,this,false))
+ {
+ // do character spell book cleanup (all characters)
+ if(loading && !learning) // spell load case
+ {
+ sLog.outError("Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.",spell_id);
+ CharacterDatabase.PExecute("DELETE FROM character_spell WHERE spell = '%u'",spell_id);
+ }
+ else
+ sLog.outError("Player::addSpell: Broken spell #%u learning not allowed.",spell_id);
+
+ return false;
+ }
+
+ PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
+
+ bool disabled_case = false;
+ bool superceded_old = false;
+
+ PlayerSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr != m_spells.end())
+ {
+ // update active state for known spell
+ if(itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled)
+ {
+ itr->second->active = active;
+
+ // loading && !learning == explicitly load from DB and then exist in it already and set correctly
+ if(loading && !learning)
+ itr->second->state = PLAYERSPELL_UNCHANGED;
+ else if(itr->second->state != PLAYERSPELL_NEW)
+ itr->second->state = PLAYERSPELL_CHANGED;
+
+ if(!active)
+ {
+ WorldPacket data(SMSG_REMOVED_SPELL, 4);
+ data << uint16(spell_id);
+ GetSession()->SendPacket(&data);
+ }
+ return active; // learn (show in spell book if active now)
+ }
+
+ if(itr->second->disabled != disabled && itr->second->state != PLAYERSPELL_REMOVED)
+ {
+ if(itr->second->state != PLAYERSPELL_NEW)
+ itr->second->state = PLAYERSPELL_CHANGED;
+ itr->second->disabled = disabled;
+
+ if(disabled)
+ return false;
+
+ disabled_case = true;
+ }
+ else switch(itr->second->state)
+ {
+ case PLAYERSPELL_UNCHANGED: // known saved spell
+ return false;
+ case PLAYERSPELL_REMOVED: // re-learning removed not saved spell
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ state = PLAYERSPELL_CHANGED;
+ break; // need re-add
+ }
+ default: // known not saved yet spell (new or modified)
+ {
+ // can be in case spell loading but learned at some previous spell loading
+ if(loading && !learning)
+ itr->second->state = PLAYERSPELL_UNCHANGED;
+
+ return false;
+ }
+ }
+ }
+
+ if(!disabled_case) // skip new spell adding if spell already known (disabled spells case)
+ {
+ // talent: unlearn all other talent ranks (high and low)
+ if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id))
+ {
+ if(TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentPos->talent_id ))
+ {
+ for(int i=0; i <5; ++i)
+ {
+ // skip learning spell and no rank spell case
+ uint32 rankSpellId = talentInfo->RankID[i];
+ if(!rankSpellId || rankSpellId==spell_id)
+ continue;
+
+ // skip unknown ranks
+ if(!HasSpell(rankSpellId))
+ continue;
+
+ removeSpell(rankSpellId);
+ }
+ }
+ }
+ // non talent spell: learn low ranks (recursive call)
+ else if(uint32 prev_spell = spellmgr.GetPrevSpellInChain(spell_id))
+ {
+ if(loading) // at spells loading, no output, but allow save
+ addSpell(prev_spell,active,true,loading,SPELL_WITHOUT_SLOT_ID,disabled);
+ else // at normal learning
+ learnSpell(prev_spell);
+ }
+
+ PlayerSpell *newspell = new PlayerSpell;
+ newspell->active = active;
+ newspell->state = state;
+ newspell->disabled = disabled;
+
+ // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible
+ if(newspell->active && !newspell->disabled && !SpellMgr::canStackSpellRanks(spellInfo) && spellmgr.GetSpellRank(spellInfo->Id) != 0)
+ {
+ for( PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr )
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED) continue;
+ SpellEntry const *i_spellInfo = sSpellStore.LookupEntry(itr->first);
+ if(!i_spellInfo) continue;
+
+ if( spellmgr.IsRankSpellDueToSpell(spellInfo,itr->first) )
+ {
+ if(itr->second->active)
+ {
+ if(spellmgr.IsHighRankOfSpell(spell_id,itr->first))
+ {
+ if(!loading) // not send spell (re-/over-)learn packets at loading
+ {
+ WorldPacket data(SMSG_SUPERCEDED_SPELL, (4));
+ data << uint16(itr->first);
+ data << uint16(spell_id);
+ GetSession()->SendPacket( &data );
+ }
+
+ // mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
+ itr->second->active = false;
+ itr->second->state = PLAYERSPELL_CHANGED;
+ superceded_old = true; // new spell replace old in action bars and spell book.
+ }
+ else if(spellmgr.IsHighRankOfSpell(itr->first,spell_id))
+ {
+ if(!loading) // not send spell (re-/over-)learn packets at loading
+ {
+ WorldPacket data(SMSG_SUPERCEDED_SPELL, (4));
+ data << uint16(spell_id);
+ data << uint16(itr->first);
+ GetSession()->SendPacket( &data );
+ }
+
+ // mark new spell as disable (not learned yet for client and will not learned)
+ newspell->active = false;
+ if(newspell->state != PLAYERSPELL_NEW)
+ newspell->state = PLAYERSPELL_CHANGED;
+ }
+ }
+ }
+ }
+ }
+
+ uint16 tmpslot=slot_id;
+
+ if (tmpslot == SPELL_WITHOUT_SLOT_ID)
+ {
+ uint16 maxid = 0;
+ PlayerSpellMap::iterator itr;
+ for (itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+ if (itr->second->slotId > maxid)
+ maxid = itr->second->slotId;
+ }
+ tmpslot = maxid + 1;
+ }
+
+ newspell->slotId = tmpslot;
+ m_spells[spell_id] = newspell;
+
+ // return false if spell disabled
+ if (newspell->disabled)
+ return false;
+ }
+
+ uint32 talentCost = GetTalentSpellCost(spell_id);
+
+ // cast talents with SPELL_EFFECT_LEARN_SPELL (other dependent spells will learned later as not auto-learned)
+ // note: all spells with SPELL_EFFECT_LEARN_SPELL isn't passive
+ if( talentCost > 0 && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_LEARN_SPELL) )
+ {
+ // ignore stance requirement for talent learn spell (stance set for spell only for client spell description show)
+ CastSpell(this, spell_id, true);
+ }
+ // also cast passive spells (including all talents without SPELL_EFFECT_LEARN_SPELL) with additional checks
+ else if (IsPassiveSpell(spell_id))
+ {
+ // if spell doesn't require a stance or the player is in the required stance
+ if( ( !spellInfo->Stances &&
+ spell_id != 5420 && spell_id != 5419 && spell_id != 7376 &&
+ spell_id != 7381 && spell_id != 21156 && spell_id != 21009 &&
+ spell_id != 21178 && spell_id != 33948 && spell_id != 40121 ) ||
+ m_form != 0 && (spellInfo->Stances & (1<<(m_form-1))) ||
+ (spell_id == 5420 && m_form == FORM_TREE) ||
+ (spell_id == 5419 && m_form == FORM_TRAVEL) ||
+ (spell_id == 7376 && m_form == FORM_DEFENSIVESTANCE) ||
+ (spell_id == 7381 && m_form == FORM_BERSERKERSTANCE) ||
+ (spell_id == 21156 && m_form == FORM_BATTLESTANCE)||
+ (spell_id == 21178 && (m_form == FORM_BEAR || m_form == FORM_DIREBEAR) ) ||
+ (spell_id == 33948 && m_form == FORM_FLIGHT) ||
+ (spell_id == 40121 && m_form == FORM_FLIGHT_EPIC) )
+ //Check CasterAuraStates
+ if (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState)))
+ CastSpell(this, spell_id, true);
+ }
+ else if( IsSpellHaveEffect(spellInfo,SPELL_EFFECT_SKILL_STEP) )
+ {
+ CastSpell(this, spell_id, true);
+ return false;
+ }
+
+ // update used talent points count
+ m_usedTalentCount += talentCost;
+
+ // update free primary prof.points (if any, can be none in case GM .learn prof. learning)
+ if(uint32 freeProfs = GetFreePrimaryProffesionPoints())
+ {
+ if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell_id))
+ SetFreePrimaryProffesions(freeProfs-1);
+ }
+
+ // add dependent skills
+ uint16 maxskill = GetMaxSkillValueForLevel();
+
+ SpellLearnSkillNode const* spellLearnSkill = spellmgr.GetSpellLearnSkill(spell_id);
+
+ if(spellLearnSkill)
+ {
+ uint32 skill_value = GetPureSkillValue(spellLearnSkill->skill);
+ uint32 skill_max_value = GetPureMaxSkillValue(spellLearnSkill->skill);
+
+ if(skill_value < spellLearnSkill->value)
+ skill_value = spellLearnSkill->value;
+
+ uint32 new_skill_max_value = spellLearnSkill->maxvalue == 0 ? maxskill : spellLearnSkill->maxvalue;
+
+ if(skill_max_value < new_skill_max_value)
+ skill_max_value = new_skill_max_value;
+
+ SetSkill(spellLearnSkill->skill,skill_value,skill_max_value);
+ }
+ else
+ {
+ // not ranked skills
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
+ if(!pSkill)
+ continue;
+
+ if(HasSkill(pSkill->id))
+ continue;
+
+ if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
+ // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
+ pSkill->id==SKILL_POISONS && _spell_idx->second->max_value==0 ||
+ // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
+ pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 )
+ {
+ switch(GetSkillRangeType(pSkill,_spell_idx->second->racemask!=0))
+ {
+ case SKILL_RANGE_LANGUAGE:
+ SetSkill(pSkill->id, 300, 300 );
+ break;
+ case SKILL_RANGE_LEVEL:
+ SetSkill(pSkill->id, 1, GetMaxSkillValueForLevel() );
+ break;
+ case SKILL_RANGE_MONO:
+ SetSkill(pSkill->id, 1, 1 );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // learn dependent spells
+ SpellLearnSpellMap::const_iterator spell_begin = spellmgr.GetBeginSpellLearnSpell(spell_id);
+ SpellLearnSpellMap::const_iterator spell_end = spellmgr.GetEndSpellLearnSpell(spell_id);
+
+ for(SpellLearnSpellMap::const_iterator itr = spell_begin; itr != spell_end; ++itr)
+ {
+ if(!itr->second.autoLearned)
+ {
+ if(loading) // at spells loading, no output, but allow save
+ addSpell(itr->second.spell,true,true,loading);
+ else // at normal learning
+ learnSpell(itr->second.spell);
+ }
+ }
+
+ // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell
+ return active && !disabled && !superceded_old;
+}
+
+void Player::learnSpell(uint32 spell_id)
+{
+ PlayerSpellMap::iterator itr = m_spells.find(spell_id);
+
+ bool disabled = (itr != m_spells.end()) ? itr->second->disabled : false;
+ bool active = disabled ? itr->second->active : true;
+
+ bool learning = addSpell(spell_id,active);
+
+ // learn all disabled higher ranks (recursive)
+ SpellChainMapNext const& nextMap = spellmgr.GetSpellChainNext();
+ for(SpellChainMapNext::const_iterator i = nextMap.lower_bound(spell_id); i != nextMap.upper_bound(spell_id); ++i)
+ {
+ PlayerSpellMap::iterator iter = m_spells.find(i->second);
+ if (disabled && iter != m_spells.end() && iter->second->disabled)
+ learnSpell(i->second);
+ }
+
+ // prevent duplicated entires in spell book
+ if(!learning)
+ return;
+
+ WorldPacket data(SMSG_LEARNED_SPELL, 4);
+ data << uint32(spell_id);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::removeSpell(uint32 spell_id, bool disabled)
+{
+ PlayerSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr == m_spells.end())
+ return;
+
+ if(itr->second->state == PLAYERSPELL_REMOVED || disabled && itr->second->disabled)
+ return;
+
+ // unlearn non talent higher ranks (recursive)
+ SpellChainMapNext const& nextMap = spellmgr.GetSpellChainNext();
+ for(SpellChainMapNext::const_iterator itr2 = nextMap.lower_bound(spell_id); itr2 != nextMap.upper_bound(spell_id); ++itr2)
+ if(HasSpell(itr2->second) && !GetTalentSpellPos(itr2->second))
+ removeSpell(itr2->second,disabled);
+
+ // removing
+ WorldPacket data(SMSG_REMOVED_SPELL, 4);
+ data << uint16(spell_id);
+ GetSession()->SendPacket(&data);
+
+ if (disabled)
+ {
+ itr->second->disabled = disabled;
+ if(itr->second->state != PLAYERSPELL_NEW)
+ itr->second->state = PLAYERSPELL_CHANGED;
+ }
+ else
+ {
+ if(itr->second->state == PLAYERSPELL_NEW)
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ }
+ else
+ itr->second->state = PLAYERSPELL_REMOVED;
+ }
+
+ RemoveAurasDueToSpell(spell_id);
+
+ // remove pet auras
+ if(PetAura const* petSpell = spellmgr.GetPetAura(spell_id))
+ RemovePetAura(petSpell);
+
+ // free talent points
+ uint32 talentCosts = GetTalentSpellCost(spell_id);
+ if(talentCosts > 0)
+ {
+ if(talentCosts < m_usedTalentCount)
+ m_usedTalentCount -= talentCosts;
+ else
+ m_usedTalentCount = 0;
+ }
+
+ // update free primary prof.points (if not overflow setting, can be in case GM use before .learn prof. learning)
+ if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell_id))
+ {
+ uint32 freeProfs = GetFreePrimaryProffesionPoints()+1;
+ if(freeProfs <= sWorld.getConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL))
+ SetFreePrimaryProffesions(freeProfs);
+ }
+
+ // remove dependent skill
+ SpellLearnSkillNode const* spellLearnSkill = spellmgr.GetSpellLearnSkill(spell_id);
+ if(spellLearnSkill)
+ {
+ uint32 prev_spell = spellmgr.GetPrevSpellInChain(spell_id);
+ if(!prev_spell) // first rank, remove skill
+ SetSkill(spellLearnSkill->skill,0,0);
+ else
+ {
+ // search prev. skill setting by spell ranks chain
+ SpellLearnSkillNode const* prevSkill = spellmgr.GetSpellLearnSkill(prev_spell);
+ while(!prevSkill && prev_spell)
+ {
+ prev_spell = spellmgr.GetPrevSpellInChain(prev_spell);
+ prevSkill = spellmgr.GetSpellLearnSkill(spellmgr.GetFirstSpellInChain(prev_spell));
+ }
+
+ if(!prevSkill) // not found prev skill setting, remove skill
+ SetSkill(spellLearnSkill->skill,0,0);
+ else // set to prev. skill setting values
+ {
+ uint32 skill_value = GetPureSkillValue(prevSkill->skill);
+ uint32 skill_max_value = GetPureMaxSkillValue(prevSkill->skill);
+
+ if(skill_value > prevSkill->value)
+ skill_value = prevSkill->value;
+
+ uint32 new_skill_max_value = prevSkill->maxvalue == 0 ? GetMaxSkillValueForLevel() : prevSkill->maxvalue;
+
+ if(skill_max_value > new_skill_max_value)
+ skill_max_value = new_skill_max_value;
+
+ SetSkill(prevSkill->skill,skill_value,skill_max_value);
+ }
+ }
+
+ }
+ else
+ {
+ // not ranked skills
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(_spell_idx->second->skillId);
+ if(!pSkill)
+ continue;
+
+ if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL ||
+ // poison special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
+ pSkill->id==SKILL_POISONS && _spell_idx->second->max_value==0 ||
+ // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
+ pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 )
+ {
+ // not reset skills for professions and racial abilities
+ if( (pSkill->categoryId==SKILL_CATEGORY_SECONDARY || pSkill->categoryId==SKILL_CATEGORY_PROFESSION) &&
+ (IsProfessionSkill(pSkill->id) || _spell_idx->second->racemask!=0) )
+ continue;
+
+ SetSkill(pSkill->id, 0, 0 );
+ }
+ }
+ }
+
+ // remove dependent spells
+ SpellLearnSpellMap::const_iterator spell_begin = spellmgr.GetBeginSpellLearnSpell(spell_id);
+ SpellLearnSpellMap::const_iterator spell_end = spellmgr.GetEndSpellLearnSpell(spell_id);
+
+ for(SpellLearnSpellMap::const_iterator itr2 = spell_begin; itr2 != spell_end; ++itr2)
+ removeSpell(itr2->second.spell, disabled);
+}
+
+void Player::RemoveArenaSpellCooldowns()
+{
+ // remove cooldowns on spells that has < 15 min CD
+ SpellCooldowns::iterator itr, next;
+ // iterate spell cooldowns
+ for(itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+ SpellEntry const * entry = sSpellStore.LookupEntry(itr->first);
+ // check if spellentry is present and if the cooldown is less than 15 mins
+ if( entry &&
+ entry->RecoveryTime <= 15 * MINUTE * 1000 &&
+ entry->CategoryRecoveryTime <= 15 * MINUTE * 1000 )
+ {
+ // notify player
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(itr->first);
+ data << GetGUID();
+ GetSession()->SendPacket(&data);
+ // remove cooldown
+ m_spellCooldowns.erase(itr);
+ }
+ }
+}
+
+void Player::RemoveAllSpellCooldown()
+{
+ if(!m_spellCooldowns.empty())
+ {
+ for(SpellCooldowns::const_iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); ++itr)
+ {
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(itr->first);
+ data << uint64(GetGUID());
+ GetSession()->SendPacket(&data);
+ }
+ m_spellCooldowns.clear();
+ }
+}
+
+void Player::_LoadSpellCooldowns(QueryResult *result)
+{
+ m_spellCooldowns.clear();
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'",GetGUIDLow());
+
+ if(result)
+ {
+ time_t curTime = time(NULL);
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 spell_id = fields[0].GetUInt32();
+ uint32 item_id = fields[1].GetUInt32();
+ time_t db_time = (time_t)fields[2].GetUInt64();
+
+ if(!sSpellStore.LookupEntry(spell_id))
+ {
+ sLog.outError("Player %u have unknown spell %u in `character_spell_cooldown`, skipping.",GetGUIDLow(),spell_id);
+ continue;
+ }
+
+ // skip outdated cooldown
+ if(db_time <= curTime)
+ continue;
+
+ AddSpellCooldown(spell_id, item_id, db_time);
+
+ sLog.outDebug("Player (GUID: %u) spell %u, item %u cooldown loaded (%u secs).", GetGUIDLow(), spell_id, item_id, uint32(db_time-curTime));
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Player::_SaveSpellCooldowns()
+{
+ CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow());
+
+ time_t curTime = time(NULL);
+
+ // remove outdated and save active
+ for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();)
+ {
+ if(itr->second.end <= curTime)
+ m_spellCooldowns.erase(itr++);
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES ('%u', '%u', '%u', '" I64FMTD "')", GetGUIDLow(), itr->first, itr->second.itemid, uint64(itr->second.end));
+ ++itr;
+ }
+ }
+}
+
+uint32 Player::resetTalentsCost() const
+{
+ // The first time reset costs 1 gold
+ if(m_resetTalentsCost < 1*GOLD)
+ return 1*GOLD;
+ // then 5 gold
+ else if(m_resetTalentsCost < 5*GOLD)
+ return 5*GOLD;
+ // After that it increases in increments of 5 gold
+ else if(m_resetTalentsCost < 10*GOLD)
+ return 10*GOLD;
+ else
+ {
+ uint32 months = (sWorld.GetGameTime() - m_resetTalentsTime)/MONTH;
+ if(months > 0)
+ {
+ // This cost will be reduced by a rate of 5 gold per month
+ int32 new_cost = int32(m_resetTalentsCost) - 5*GOLD*months;
+ // to a minimum of 10 gold.
+ return (new_cost < 10*GOLD ? 10*GOLD : new_cost);
+ }
+ else
+ {
+ // After that it increases in increments of 5 gold
+ int32 new_cost = m_resetTalentsCost + 5*GOLD;
+ // until it hits a cap of 50 gold.
+ if(new_cost > 50*GOLD)
+ new_cost = 50*GOLD;
+ return new_cost;
+ }
+ }
+}
+
+bool Player::resetTalents(bool no_cost)
+{
+ // not need after this call
+ if(HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
+ {
+ m_atLoginFlags = m_atLoginFlags & ~AT_LOGIN_RESET_TALENTS;
+ CharacterDatabase.PExecute("UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'", uint32(AT_LOGIN_RESET_TALENTS), GetGUIDLow());
+ }
+
+ uint32 level = getLevel();
+ uint32 talentPointsForLevel = level < 10 ? 0 : uint32((level-9)*sWorld.getRate(RATE_TALENT));
+
+ if (m_usedTalentCount == 0)
+ {
+ SetFreeTalentPoints(talentPointsForLevel);
+ return false;
+ }
+
+ uint32 cost = 0;
+
+ if(!no_cost)
+ {
+ cost = resetTalentsCost();
+
+ if (GetMoney() < cost)
+ {
+ SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
+ return false;
+ }
+ }
+
+ for (unsigned int i = 0; i < sTalentStore.GetNumRows(); i++)
+ {
+ TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
+
+ if (!talentInfo) continue;
+
+ TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
+
+ if(!talentTabInfo)
+ continue;
+
+ // unlearn only talents for character class
+ // some spell learned by one class as normal spells or know at creation but another class learn it as talent,
+ // to prevent unexpected lost normal learned spell skip another class talents
+ if( (getClassMask() & talentTabInfo->ClassMask) == 0 )
+ continue;
+
+ for (int j = 0; j < 5; j++)
+ {
+ for(PlayerSpellMap::iterator itr = GetSpellMap().begin(); itr != GetSpellMap().end();)
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled)
+ {
+ ++itr;
+ continue;
+ }
+
+ // remove learned spells (all ranks)
+ uint32 itrFirstId = spellmgr.GetFirstSpellInChain(itr->first);
+
+ // unlearn if first rank is talent or learned by talent
+ if (itrFirstId == talentInfo->RankID[j] || spellmgr.IsSpellLearnToSpell(talentInfo->RankID[j],itrFirstId))
+ {
+ removeSpell(itr->first,!IsPassiveSpell(itr->first));
+ itr = GetSpellMap().begin();
+ continue;
+ }
+ else
+ ++itr;
+ }
+ }
+ }
+
+ SetFreeTalentPoints(talentPointsForLevel);
+
+ if(!no_cost)
+ {
+ ModifyMoney(-(int32)cost);
+
+ m_resetTalentsCost = cost;
+ m_resetTalentsTime = time(NULL);
+ }
+
+ //FIXME: remove pet before or after unlearn spells? for now after unlearn to allow removing of talent related, pet affecting auras
+ RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
+
+ return true;
+}
+
+bool Player::_removeSpell(uint16 spell_id)
+{
+ PlayerSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr != m_spells.end())
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ return true;
+ }
+ return false;
+}
+
+Mail* Player::GetMail(uint32 id)
+{
+ for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); itr++)
+ {
+ if ((*itr)->messageID == id)
+ {
+ return (*itr);
+ }
+ }
+ return NULL;
+}
+
+void Player::_SetCreateBits(UpdateMask *updateMask, Player *target) const
+{
+ if(target == this)
+ {
+ Object::_SetCreateBits(updateMask, target);
+ }
+ else
+ {
+ for(uint16 index = 0; index < m_valuesCount; index++)
+ {
+ if(GetUInt32Value(index) != 0 && updateVisualBits.GetBit(index))
+ updateMask->SetBit(index);
+ }
+ }
+}
+
+void Player::_SetUpdateBits(UpdateMask *updateMask, Player *target) const
+{
+ if(target == this)
+ {
+ Object::_SetUpdateBits(updateMask, target);
+ }
+ else
+ {
+ Object::_SetUpdateBits(updateMask, target);
+ *updateMask &= updateVisualBits;
+ }
+}
+
+void Player::InitVisibleBits()
+{
+ updateVisualBits.SetCount(PLAYER_END);
+
+ updateVisualBits.SetBit(OBJECT_FIELD_GUID);
+ updateVisualBits.SetBit(OBJECT_FIELD_TYPE);
+ updateVisualBits.SetBit(OBJECT_FIELD_SCALE_X);
+
+ updateVisualBits.SetBit(UNIT_FIELD_CHARM);
+ updateVisualBits.SetBit(UNIT_FIELD_CHARM+1);
+
+ updateVisualBits.SetBit(UNIT_FIELD_SUMMON);
+ updateVisualBits.SetBit(UNIT_FIELD_SUMMON+1);
+
+ updateVisualBits.SetBit(UNIT_FIELD_CHARMEDBY);
+
+ updateVisualBits.SetBit(UNIT_FIELD_TARGET);
+ updateVisualBits.SetBit(UNIT_FIELD_TARGET+1);
+
+ updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT);
+ updateVisualBits.SetBit(UNIT_FIELD_CHANNEL_OBJECT+1);
+
+ updateVisualBits.SetBit(UNIT_FIELD_HEALTH);
+ updateVisualBits.SetBit(UNIT_FIELD_POWER1);
+ updateVisualBits.SetBit(UNIT_FIELD_POWER2);
+ updateVisualBits.SetBit(UNIT_FIELD_POWER3);
+ updateVisualBits.SetBit(UNIT_FIELD_POWER4);
+ updateVisualBits.SetBit(UNIT_FIELD_POWER5);
+
+ updateVisualBits.SetBit(UNIT_FIELD_MAXHEALTH);
+ updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER1);
+ updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER2);
+ updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER3);
+ updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER4);
+ updateVisualBits.SetBit(UNIT_FIELD_MAXPOWER5);
+
+ updateVisualBits.SetBit(UNIT_FIELD_LEVEL);
+ updateVisualBits.SetBit(UNIT_FIELD_FACTIONTEMPLATE);
+ updateVisualBits.SetBit(UNIT_FIELD_BYTES_0);
+ updateVisualBits.SetBit(UNIT_FIELD_FLAGS);
+ updateVisualBits.SetBit(UNIT_FIELD_FLAGS_2);
+ for(uint16 i = UNIT_FIELD_AURA; i < UNIT_FIELD_AURASTATE; ++i)
+ updateVisualBits.SetBit(i);
+ updateVisualBits.SetBit(UNIT_FIELD_AURASTATE);
+ updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME);
+ updateVisualBits.SetBit(UNIT_FIELD_BASEATTACKTIME + 1);
+ updateVisualBits.SetBit(UNIT_FIELD_RANGEDATTACKTIME);
+ updateVisualBits.SetBit(UNIT_FIELD_BOUNDINGRADIUS);
+ updateVisualBits.SetBit(UNIT_FIELD_COMBATREACH);
+ updateVisualBits.SetBit(UNIT_FIELD_DISPLAYID);
+ updateVisualBits.SetBit(UNIT_FIELD_NATIVEDISPLAYID);
+ updateVisualBits.SetBit(UNIT_FIELD_MOUNTDISPLAYID);
+ updateVisualBits.SetBit(UNIT_FIELD_BYTES_1);
+ updateVisualBits.SetBit(UNIT_FIELD_MOUNTDISPLAYID);
+ updateVisualBits.SetBit(UNIT_FIELD_PETNUMBER);
+ updateVisualBits.SetBit(UNIT_FIELD_PET_NAME_TIMESTAMP);
+ updateVisualBits.SetBit(UNIT_DYNAMIC_FLAGS);
+ updateVisualBits.SetBit(UNIT_CHANNEL_SPELL);
+ updateVisualBits.SetBit(UNIT_MOD_CAST_SPEED);
+ updateVisualBits.SetBit(UNIT_FIELD_BYTES_2);
+
+ updateVisualBits.SetBit(PLAYER_FLAGS);
+ updateVisualBits.SetBit(PLAYER_BYTES);
+ updateVisualBits.SetBit(PLAYER_BYTES_2);
+ updateVisualBits.SetBit(PLAYER_BYTES_3);
+ updateVisualBits.SetBit(PLAYER_GUILDID);
+ updateVisualBits.SetBit(PLAYER_GUILDRANK);
+ updateVisualBits.SetBit(PLAYER_GUILD_TIMESTAMP);
+ updateVisualBits.SetBit(PLAYER_DUEL_TEAM);
+ updateVisualBits.SetBit(PLAYER_DUEL_ARBITER);
+ updateVisualBits.SetBit(PLAYER_DUEL_ARBITER+1);
+
+ // PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)...
+ for(uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i+=4)
+ updateVisualBits.SetBit(i);
+
+ //Players visible items are not inventory stuff
+ //431) = 884 (0x374) = main weapon
+ for(uint16 i = 0; i < EQUIPMENT_SLOT_END; i++)
+ {
+ // item creator
+ updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i*MAX_VISIBLE_ITEM_OFFSET) + 0);
+ updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_CREATOR + (i*MAX_VISIBLE_ITEM_OFFSET) + 1);
+
+ uint16 visual_base = PLAYER_VISIBLE_ITEM_1_0 + (i*MAX_VISIBLE_ITEM_OFFSET);
+
+ // item entry
+ updateVisualBits.SetBit(visual_base + 0);
+
+ // item enchantment IDs
+ for(uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j)
+ updateVisualBits.SetBit(visual_base + 1 + j);
+
+ // random properties
+ updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 0 + (i*MAX_VISIBLE_ITEM_OFFSET));
+ updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (i*MAX_VISIBLE_ITEM_OFFSET));
+ }
+
+ updateVisualBits.SetBit(PLAYER_CHOSEN_TITLE);
+
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + 1);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY + 2);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 1);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 2);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 3);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 4);
+ updateVisualBits.SetBit(UNIT_VIRTUAL_ITEM_INFO + 5);
+}
+
+void Player::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const
+{
+ for(int i = 0; i < EQUIPMENT_SLOT_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
+ }
+
+ if(target == this)
+ {
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
+ }
+ }
+
+ Unit::BuildCreateUpdateBlockForPlayer( data, target );
+}
+
+void Player::DestroyForPlayer( Player *target ) const
+{
+ Unit::DestroyForPlayer( target );
+
+ for(int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->DestroyForPlayer( target );
+ }
+
+ if(target == this)
+ {
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->DestroyForPlayer( target );
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ if(m_items[i] == NULL)
+ continue;
+
+ m_items[i]->DestroyForPlayer( target );
+ }
+ }
+}
+
+bool Player::HasSpell(uint32 spell) const
+{
+ PlayerSpellMap::const_iterator itr = m_spells.find((uint16)spell);
+ return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled);
+}
+
+TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell) const
+{
+ if (!trainer_spell)
+ return TRAINER_SPELL_RED;
+
+ if (!trainer_spell->spell)
+ return TRAINER_SPELL_RED;
+
+ // known spell
+ if(HasSpell(trainer_spell->spell))
+ return TRAINER_SPELL_GRAY;
+
+ // check race/class requirement
+ if(!IsSpellFitByClassAndRace(trainer_spell->spell))
+ return TRAINER_SPELL_RED;
+
+ // check level requirement
+ if(getLevel() < trainer_spell->reqlevel)
+ return TRAINER_SPELL_RED;
+
+ if(SpellChainNode const* spell_chain = spellmgr.GetSpellChainNode(trainer_spell->spell))
+ {
+ // check prev.rank requirement
+ if(spell_chain->prev && !HasSpell(spell_chain->prev))
+ return TRAINER_SPELL_RED;
+
+ // check additional spell requirement
+ if(spell_chain->req && !HasSpell(spell_chain->req))
+ return TRAINER_SPELL_RED;
+ }
+
+ // check skill requirement
+ if(trainer_spell->reqskill && GetBaseSkillValue(trainer_spell->reqskill) < trainer_spell->reqskillvalue)
+ return TRAINER_SPELL_RED;
+
+ // exist, already checked at loading
+ SpellEntry const* spell = sSpellStore.LookupEntry(trainer_spell->spell);
+
+ // secondary prof. or not prof. spell
+ uint32 skill = spell->EffectMiscValue[1];
+
+ if(spell->Effect[1] != SPELL_EFFECT_SKILL || !IsPrimaryProfessionSkill(skill))
+ return TRAINER_SPELL_GREEN;
+
+ // check primary prof. limit
+ if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell->Id) && GetFreePrimaryProffesionPoints() == 0)
+ return TRAINER_SPELL_RED;
+
+ return TRAINER_SPELL_GREEN;
+}
+
+void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars)
+{
+ uint32 guid = GUID_LOPART(playerguid);
+
+ // convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry)
+ // bones will be deleted by corpse/bones deleting thread shortly
+ ObjectAccessor::Instance().ConvertCorpseForPlayer(playerguid);
+
+ // remove from guild
+ uint32 guildId = GetGuildIdFromDB(playerguid);
+ if(guildId != 0)
+ {
+ Guild* guild = objmgr.GetGuildById(guildId);
+ if(guild)
+ guild->DelMember(guid);
+ }
+
+ // remove from arena teams
+ uint32 at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_2v2);
+ if(at_id != 0)
+ {
+ ArenaTeam * at = objmgr.GetArenaTeamById(at_id);
+ if(at)
+ at->DelMember(playerguid);
+ }
+ at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_3v3);
+ if(at_id != 0)
+ {
+ ArenaTeam * at = objmgr.GetArenaTeamById(at_id);
+ if(at)
+ at->DelMember(playerguid);
+ }
+ at_id = GetArenaTeamIdFromDB(playerguid,ARENA_TEAM_5v5);
+ if(at_id != 0)
+ {
+ ArenaTeam * at = objmgr.GetArenaTeamById(at_id);
+ if(at)
+ at->DelMember(playerguid);
+ }
+
+ // the player was uninvited already on logout so just remove from group
+ QueryResult *resultGroup = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", guid);
+ if(resultGroup)
+ {
+ uint64 leaderGuid = MAKE_NEW_GUID((*resultGroup)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ delete resultGroup;
+ Group* group = objmgr.GetGroupByLeader(leaderGuid);
+ if(group)
+ {
+ RemoveFromGroup(group, playerguid);
+ }
+ }
+
+ // remove signs from petitions (also remove petitions if owner);
+ RemovePetitionsAndSigns(playerguid, 10);
+
+ // return back all mails with COD and Item 0 1 2 3 4 5 6
+ QueryResult *resultMail = CharacterDatabase.PQuery("SELECT id,mailTemplateId,sender,subject,itemTextId,money,has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0", guid);
+ if(resultMail)
+ {
+ do
+ {
+ Field *fields = resultMail->Fetch();
+
+ uint32 mail_id = fields[0].GetUInt32();
+ uint16 mailTemplateId= fields[1].GetUInt16();
+ uint32 sender = fields[2].GetUInt32();
+ std::string subject = fields[3].GetCppString();
+ uint32 itemTextId = fields[4].GetUInt32();
+ uint32 money = fields[5].GetUInt32();
+ bool has_items = fields[6].GetBool();
+
+ //we can return mail now
+ //so firstly delete the old one
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id);
+
+ MailItemsInfo mi;
+ if(has_items)
+ {
+ QueryResult *resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", mail_id);
+ if(resultItems)
+ {
+ do
+ {
+ Field *fields2 = resultItems->Fetch();
+
+ uint32 item_guidlow = fields2[0].GetUInt32();
+ uint32 item_template = fields2[1].GetUInt32();
+
+ ItemPrototype const* itemProto = objmgr.GetItemPrototype(item_template);
+ if(!itemProto)
+ {
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guidlow);
+ continue;
+ }
+
+ Item *pItem = NewItemOrBag(itemProto);
+ if(!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER)))
+ {
+ pItem->FSetState(ITEM_REMOVED);
+ pItem->SaveToDB(); // it also deletes item object !
+ continue;
+ }
+
+ mi.AddItem(item_guidlow, item_template, pItem);
+ }
+ while (resultItems->NextRow());
+
+ delete resultItems;
+ }
+ }
+
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
+
+ uint32 pl_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
+
+ WorldSession::SendReturnToSender(MAIL_NORMAL, pl_account, guid, sender, subject, itemTextId, &mi, money, 0, mailTemplateId);
+ }
+ while (resultMail->NextRow());
+
+ delete resultMail;
+ }
+
+ // unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
+ // Get guids of character's pets, will deleted in transaction
+ QueryResult *resultPets = CharacterDatabase.PQuery("SELECT id FROM character_pet WHERE owner = '%u'",guid);
+
+ // NOW we can finally clear other DB data related to character
+ CharacterDatabase.BeginTransaction();
+ if (resultPets)
+ {
+ do
+ {
+ Field *fields3 = resultPets->Fetch();
+ uint32 petguidlow = fields3[0].GetUInt32();
+ Pet::DeleteFromDB(petguidlow);
+ } while (resultPets->NextRow());
+ delete resultPets;
+ }
+
+ CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_queststatus WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'",guid,guid);
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE receiver = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE receiver = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'",guid);
+ CharacterDatabase.PExecute("DELETE FROM has_logged_in_before WHERE guid = %u",guid);
+ CharacterDatabase.CommitTransaction();
+
+ //loginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID);
+ if(updateRealmChars) sWorld.UpdateRealmCharCount(accountId);
+}
+
+void Player::SetMovement(PlayerMovementType pType)
+{
+ WorldPacket data;
+ switch(pType)
+ {
+ case MOVE_ROOT: data.Initialize(SMSG_FORCE_MOVE_ROOT, GetPackGUID().size()+4); break;
+ case MOVE_UNROOT: data.Initialize(SMSG_FORCE_MOVE_UNROOT, GetPackGUID().size()+4); break;
+ case MOVE_WATER_WALK: data.Initialize(SMSG_MOVE_WATER_WALK, GetPackGUID().size()+4); break;
+ case MOVE_LAND_WALK: data.Initialize(SMSG_MOVE_LAND_WALK, GetPackGUID().size()+4); break;
+ default:
+ sLog.outError("Player::SetMovement: Unsupported move type (%d), data not sent to client.",pType);
+ return;
+ }
+ data.append(GetPackGUID());
+ data << uint32(0);
+ GetSession()->SendPacket( &data );
+}
+
+/* Preconditions:
+ - a resurrectable corpse must not be loaded for the player (only bones)
+ - the player must be in world
+*/
+void Player::BuildPlayerRepop()
+{
+ if(getRace() == RACE_NIGHTELF)
+ CastSpell(this, 20584, true); // 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)
+ CastSpell(this, 8326, true); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
+
+ // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK
+ // there must be SMSG.STOP_MIRROR_TIMER
+ // there we must send 888 opcode
+
+ // the player cannot have a corpse already, only bones which are not returned by GetCorpse
+ if(GetCorpse())
+ {
+ sLog.outError("BuildPlayerRepop: player %s(%d) already has a corpse", GetName(), GetGUIDLow());
+ assert(false);
+ }
+
+ // create a corpse and place it at the player's location
+ CreateCorpse();
+ Corpse *corpse = GetCorpse();
+ if(!corpse)
+ {
+ sLog.outError("Error creating corpse for Player %s [%u]", GetName(), GetGUIDLow());
+ return;
+ }
+ GetMap()->Add(corpse);
+
+ // convert player body to ghost
+ SetHealth( 1 );
+
+ SetMovement(MOVE_WATER_WALK);
+ if(!GetSession()->isLogingOut())
+ SetMovement(MOVE_UNROOT);
+
+ // BG - remove insignia related
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+
+ SendCorpseReclaimDelay();
+
+ // to prevent cheating
+ corpse->ResetGhostTime();
+
+ StopMirrorTimers(); //disable timers(bars)
+
+ SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (float)1.0); //see radius of death player?
+
+ SetByteValue(UNIT_FIELD_BYTES_1, 3, PLAYER_STATE_FLAG_ALWAYS_STAND);
+}
+
+void Player::SendDelayResponse(const uint32 ml_seconds)
+{
+ WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
+ data << (uint32)time(NULL);
+ data << (uint32)0;
+ GetSession()->SendPacket( &data );
+}
+
+void Player::ResurrectPlayer(float restore_percent, bool updateToWorld, bool applySickness)
+{
+ WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // remove spirit healer position
+ data << uint32(-1);
+ data << float(0);
+ data << float(0);
+ data << float(0);
+ GetSession()->SendPacket(&data);
+
+ // speed change, land walk
+
+ // remove death flag + set aura
+ SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00);
+ if(getRace() == RACE_NIGHTELF)
+ RemoveAurasDueToSpell(20584); // speed bonuses
+ RemoveAurasDueToSpell(8326); // SPELL_AURA_GHOST
+
+ setDeathState(ALIVE);
+
+ SetMovement(MOVE_LAND_WALK);
+ SetMovement(MOVE_UNROOT);
+
+ m_deathTimer = 0;
+
+ // set health/powers (0- will be set in caller)
+ if(restore_percent>0.0f)
+ {
+ SetHealth(uint32(GetMaxHealth()*restore_percent));
+ SetPower(POWER_MANA, uint32(GetMaxPower(POWER_MANA)*restore_percent));
+ SetPower(POWER_RAGE, 0);
+ SetPower(POWER_ENERGY, uint32(GetMaxPower(POWER_ENERGY)*restore_percent));
+ }
+
+ // update visbility
+ ObjectAccessor::UpdateVisibilityForPlayer(this);
+
+ // some items limited to specific map
+ DestroyZoneLimitedItem( true, GetZoneId());
+
+ if(sWorld.getConfig(CONFIG_DISABLE_RES_SICKNESS) || !applySickness || getLevel() <= 10)
+ return;
+
+ //Characters from level 1-10 are not affected by resurrection sickness.
+ //Characters from level 11-19 will suffer from one minute of sickness
+ //for each level they are above 10.
+ //Characters level 20 and up suffer from ten minutes of sickness.
+ int32 startLevel = sWorld.getConfig(CONFIG_DEATH_SICKNESS_LEVEL);
+
+ if(int32(getLevel()) >= startLevel)
+ {
+ // set resurrection sickness
+ CastSpell(this,SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,true);
+
+ // not full duration
+ if(int32(getLevel()) < startLevel+9)
+ {
+ int32 delta = (int32(getLevel()) - startLevel + 1)*MINUTE;
+
+ for(int i =0; i < 3; ++i)
+ {
+ if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,i))
+ {
+ Aur->SetAuraDuration(delta*1000);
+ Aur->UpdateAuraDuration();
+ }
+ }
+ }
+ }
+}
+
+void Player::KillPlayer()
+{
+ SetMovement(MOVE_ROOT);
+
+ StopMirrorTimers(); //disable timers(bars)
+
+ setDeathState(CORPSE);
+ //SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_IN_PVP );
+
+ SetFlag(UNIT_DYNAMIC_FLAGS, 0x00);
+ ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(GetMapId())->Instanceable());
+
+ // 6 minutes until repop at graveyard
+ m_deathTimer = 6*MINUTE*1000;
+
+ UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill
+
+ // don't create corpse at this moment, player might be falling
+
+ // update visibility
+ ObjectAccessor::UpdateObjectVisibility(this);
+}
+
+void Player::CreateCorpse()
+{
+ // prevent existence 2 corpse for player
+ SpawnCorpseBones();
+
+ uint32 _uf, _pb, _pb2, _cfb1, _cfb2;
+
+ Corpse *corpse = new Corpse( (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE );
+ SetPvPDeath(false);
+
+ if(!corpse->Create(objmgr.GenerateLowGuid(HIGHGUID_CORPSE), this, GetMapId(), GetPositionX(),
+ GetPositionY(), GetPositionZ(), GetOrientation()))
+ {
+ delete corpse;
+ return;
+ }
+
+ _uf = GetUInt32Value(UNIT_FIELD_BYTES_0);
+ _pb = GetUInt32Value(PLAYER_BYTES);
+ _pb2 = GetUInt32Value(PLAYER_BYTES_2);
+
+ uint8 race = (uint8)(_uf);
+ uint8 skin = (uint8)(_pb);
+ uint8 face = (uint8)(_pb >> 8);
+ uint8 hairstyle = (uint8)(_pb >> 16);
+ uint8 haircolor = (uint8)(_pb >> 24);
+ uint8 facialhair = (uint8)(_pb2);
+
+ _cfb1 = ((0x00) | (race << 8) | (getGender() << 16) | (skin << 24));
+ _cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));
+
+ corpse->SetUInt32Value( CORPSE_FIELD_BYTES_1, _cfb1 );
+ corpse->SetUInt32Value( CORPSE_FIELD_BYTES_2, _cfb2 );
+
+ uint32 flags = CORPSE_FLAG_UNK2;
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM))
+ flags |= CORPSE_FLAG_HIDE_HELM;
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK))
+ flags |= CORPSE_FLAG_HIDE_CLOAK;
+ if(InBattleGround())
+ flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia
+ corpse->SetUInt32Value( CORPSE_FIELD_FLAGS, flags );
+
+ corpse->SetUInt32Value( CORPSE_FIELD_DISPLAY_ID, GetNativeDisplayId() );
+
+ corpse->SetUInt32Value( CORPSE_FIELD_GUILD, GetGuildId() );
+
+ uint32 iDisplayID;
+ uint16 iIventoryType;
+ uint32 _cfi;
+ for (int i = 0; i < EQUIPMENT_SLOT_END; i++)
+ {
+ if(m_items[i])
+ {
+ iDisplayID = m_items[i]->GetProto()->DisplayInfoID;
+ iIventoryType = (uint16)m_items[i]->GetProto()->InventoryType;
+
+ _cfi = (uint16(iDisplayID)) | (iIventoryType)<< 24;
+ corpse->SetUInt32Value(CORPSE_FIELD_ITEM + i,_cfi);
+ }
+ }
+
+ // we don't SaveToDB for players in battlegrounds so don't do it for corpses either
+ const MapEntry *entry = sMapStore.LookupEntry(corpse->GetMapId());
+ assert(entry);
+ if(entry->map_type != MAP_BATTLEGROUND)
+ corpse->SaveToDB();
+
+ // register for player, but not show
+ ObjectAccessor::Instance().AddCorpse(corpse);
+}
+
+void Player::SpawnCorpseBones()
+{
+ if(ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID()))
+ SaveToDB(); // prevent loading as ghost without corpse
+}
+
+Corpse* Player::GetCorpse() const
+{
+ return ObjectAccessor::Instance().GetCorpseForPlayerGUID(GetGUID());
+}
+
+void Player::DurabilityLossAll(double percent, bool inventory)
+{
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ DurabilityLoss(pItem,percent);
+
+ if(inventory)
+ {
+ // bags not have durability
+ // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ DurabilityLoss(pItem,percent);
+
+ // keys not have durability
+ //for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ if(ItemPrototype const *pBagProto = pBag->GetProto())
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ if(Item* pItem = GetItemByPos( i, j ))
+ DurabilityLoss(pItem,percent);
+ }
+}
+
+void Player::DurabilityLoss(Item* item, double percent)
+{
+ if(!item )
+ return;
+
+ uint32 pMaxDurability = item ->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
+
+ if(!pMaxDurability)
+ return;
+
+ uint32 pDurabilityLoss = uint32(pMaxDurability*percent);
+
+ if(pDurabilityLoss < 1 )
+ pDurabilityLoss = 1;
+
+ DurabilityPointsLoss(item,pDurabilityLoss);
+}
+
+void Player::DurabilityPointsLossAll(int32 points, bool inventory)
+{
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ DurabilityPointsLoss(pItem,points);
+
+ if(inventory)
+ {
+ // bags not have durability
+ // for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ DurabilityPointsLoss(pItem,points);
+
+ // keys not have durability
+ //for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ if(ItemPrototype const *pBagProto = pBag->GetProto())
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ if(Item* pItem = GetItemByPos( i, j ))
+ DurabilityPointsLoss(pItem,points);
+ }
+}
+
+void Player::DurabilityPointsLoss(Item* item, int32 points)
+{
+ int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
+ int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
+ int32 pNewDurability = pOldDurability - points;
+
+ if (pNewDurability < 0)
+ pNewDurability = 0;
+ else if (pNewDurability > pMaxDurability)
+ pNewDurability = pMaxDurability;
+
+ if (pOldDurability != pNewDurability)
+ {
+ // modify item stats _before_ Durability set to 0 to pass _ApplyItemMods internal check
+ if ( pNewDurability == 0 && pOldDurability > 0 && item->IsEquipped())
+ _ApplyItemMods(item,item->GetSlot(), false);
+
+ item->SetUInt32Value(ITEM_FIELD_DURABILITY, pNewDurability);
+
+ // modify item stats _after_ restore durability to pass _ApplyItemMods internal check
+ if ( pNewDurability > 0 && pOldDurability == 0 && item->IsEquipped())
+ _ApplyItemMods(item,item->GetSlot(), true);
+
+ item->SetState(ITEM_CHANGED, this);
+ }
+}
+
+void Player::DurabilityPointLossForEquipSlot(EquipmentSlots slot)
+{
+ if(Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot ))
+ DurabilityPointsLoss(pItem,1);
+}
+
+uint32 Player::DurabilityRepairAll(bool cost, float discountMod, bool guildBank)
+{
+ uint32 TotalCost = 0;
+ // equipped, backpack, bags itself
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ TotalCost += DurabilityRepair(( (INVENTORY_SLOT_BAG_0 << 8) | i ),cost,discountMod, guildBank);
+
+ // bank, buyback and keys not repaired
+
+ // items in inventory bags
+ for(int j = INVENTORY_SLOT_BAG_START; j < INVENTORY_SLOT_BAG_END; j++)
+ for(int i = 0; i < MAX_BAG_SIZE; i++)
+ TotalCost += DurabilityRepair(( (j << 8) | i ),cost,discountMod, guildBank);
+ return TotalCost;
+}
+
+uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank)
+{
+ Item* item = GetItemByPos(pos);
+
+ uint32 TotalCost = 0;
+ if(!item)
+ return TotalCost;
+
+ uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
+ if(!maxDurability)
+ return TotalCost;
+
+ uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
+
+ if(cost)
+ {
+ uint32 LostDurability = maxDurability - curDurability;
+ if(LostDurability>0)
+ {
+ ItemPrototype const *ditemProto = sItemStorage.LookupEntry<ItemPrototype>(item->GetEntry());
+ if(!ditemProto)
+ {
+ sLog.outError("ERROR: RepairDurability: Unknown item id %u", ditemProto);
+ return TotalCost;
+ }
+
+ DurabilityCostsEntry const *dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
+ if(!dcost)
+ {
+ sLog.outError("ERROR: RepairDurability: Wrong item lvl %u", dcost);
+ return TotalCost;
+ }
+
+ DurabilityQualityEntry const *dQualitymodEntry = sDurabilityQualityStore.LookupEntry((ditemProto->Quality+1)*2);
+ if(!dQualitymodEntry)
+ {
+ sLog.outError("ERROR: RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntry);
+ return TotalCost;
+ }
+
+ uint32 dmultiplier = dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class,ditemProto->SubClass)];
+ uint32 costs = uint32(LostDurability*dmultiplier*double(dQualitymodEntry->quality_mod));
+
+ costs = uint32(costs * discountMod);
+
+ if (costs==0) //fix for ITEM_QUALITY_ARTIFACT
+ costs = 1;
+
+ if (guildBank)
+ {
+ if (GetGuildId()==0)
+ {
+ DEBUG_LOG("You are not member of a guild");
+ return TotalCost;
+ }
+
+ Guild *pGuild = objmgr.GetGuildById(GetGuildId());
+ if (!pGuild)
+ return TotalCost;
+
+ if (!pGuild->HasRankRight(GetRank(), GR_RIGHT_WITHDRAW_REPAIR))
+ {
+ DEBUG_LOG("You do not have rights to withdraw for repairs");
+ return TotalCost;
+ }
+
+ if (pGuild->GetMemberMoneyWithdrawRem(GetGUIDLow()) < costs)
+ {
+ DEBUG_LOG("You do not have enough money withdraw amount remaining");
+ return TotalCost;
+ }
+
+ if (pGuild->GetGuildBankMoney() < costs)
+ {
+ DEBUG_LOG("There is not enough money in bank");
+ return TotalCost;
+ }
+
+ pGuild->MemberMoneyWithdraw(costs, GetGUIDLow());
+ TotalCost = costs;
+ }
+ else if (GetMoney() < costs)
+ {
+ DEBUG_LOG("You do not have enough money");
+ return TotalCost;
+ }
+ else
+ ModifyMoney( -int32(costs) );
+ }
+ }
+
+ item->SetUInt32Value(ITEM_FIELD_DURABILITY, maxDurability);
+ item->SetState(ITEM_CHANGED, this);
+
+ // reapply mods for total broken and repaired item if equipped
+ if(IsEquipmentPos(pos) && !curDurability)
+ _ApplyItemMods(item,pos & 255, true);
+ return TotalCost;
+}
+
+void Player::RepopAtGraveyard()
+{
+ // note: this can be called also when the player is alive
+ // for example from WorldSession::HandleMovementOpcodes
+
+ AreaTableEntry const *zone = GetAreaEntryByAreaID(GetAreaId());
+
+ // Such zones are considered unreachable as a ghost and the player must be automatically revived
+ if(!isAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY || GetTransport())
+ {
+ ResurrectPlayer(0.5f);
+ SpawnCorpseBones();
+ }
+
+ WorldSafeLocsEntry const *ClosestGrave = NULL;
+
+ // Special handle for battleground maps
+ BattleGround *bg = sBattleGroundMgr.GetBattleGround(GetBattleGroundId());
+
+ if(bg && (bg->GetTypeID() == BATTLEGROUND_AB || bg->GetTypeID() == BATTLEGROUND_EY))
+ ClosestGrave = bg->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetTeam());
+ else
+ ClosestGrave = objmgr.GetClosestGraveYard( GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam() );
+
+ // stop countdown until repop
+ m_deathTimer = 0;
+
+ // if no grave found, stay at the current location
+ // and don't show spirit healer location
+ if(ClosestGrave)
+ {
+ TeleportTo(ClosestGrave->map_id, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, GetOrientation());
+ if(isDead()) // not send if alive, because it used in TeleportTo()
+ {
+ WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // show spirit healer position on minimap
+ data << ClosestGrave->map_id;
+ data << ClosestGrave->x;
+ data << ClosestGrave->y;
+ data << ClosestGrave->z;
+ GetSession()->SendPacket(&data);
+ }
+ }
+}
+
+void Player::JoinedChannel(Channel *c)
+{
+ m_channels.push_back(c);
+}
+
+void Player::LeftChannel(Channel *c)
+{
+ m_channels.remove(c);
+}
+
+void Player::CleanupChannels()
+{
+ while(!m_channels.empty())
+ {
+ Channel* ch = *m_channels.begin();
+ m_channels.erase(m_channels.begin()); // remove from player's channel list
+ ch->Leave(GetGUID(), false); // not send to client, not remove from player's channel list
+ if (ChannelMgr* cMgr = channelMgr(GetTeam()))
+ cMgr->LeftChannel(ch->GetName()); // deleted channel if empty
+
+ }
+ sLog.outDebug("Player: channels cleaned up!");
+}
+
+void Player::UpdateLocalChannels(uint32 newZone )
+{
+ if(m_channels.empty())
+ return;
+
+ AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone);
+ if(!current_zone)
+ return;
+
+ ChannelMgr* cMgr = channelMgr(GetTeam());
+ if(!cMgr)
+ return;
+
+ std::string current_zone_name = current_zone->area_name[GetSession()->GetSessionDbcLocale()];
+
+ for(JoinedChannelsList::iterator i = m_channels.begin(), next; i != m_channels.end(); i = next)
+ {
+ next = i; ++next;
+
+ // skip non built-in channels
+ if(!(*i)->IsConstant())
+ continue;
+
+ ChatChannelsEntry const* ch = GetChannelEntryFor((*i)->GetChannelId());
+ if(!ch)
+ continue;
+
+ if((ch->flags & 4) == 4) // global channel without zone name in pattern
+ continue;
+
+ // new channel
+ char new_channel_name_buf[100];
+ snprintf(new_channel_name_buf,100,ch->pattern[m_session->GetSessionDbcLocale()],current_zone_name.c_str());
+ Channel* new_channel = cMgr->GetJoinChannel(new_channel_name_buf,ch->ChannelID);
+
+ if((*i)!=new_channel)
+ {
+ new_channel->Join(GetGUID(),""); // will output Changed Channel: N. Name
+
+ // leave old channel
+ (*i)->Leave(GetGUID(),false); // not send leave channel, it already replaced at client
+ std::string name = (*i)->GetName(); // stroe name, (*i)erase in LeftChannel
+ LeftChannel(*i); // remove from player's channel list
+ cMgr->LeftChannel(name); // delete if empty
+ }
+ }
+ sLog.outDebug("Player: channels cleaned up!");
+}
+
+void Player::LeaveLFGChannel()
+{
+ for(JoinedChannelsList::iterator i = m_channels.begin(); i != m_channels.end(); ++i )
+ {
+ if((*i)->IsLFG())
+ {
+ (*i)->Leave(GetGUID());
+ break;
+ }
+ }
+}
+
+void Player::UpdateDefense()
+{
+ uint32 defense_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_DEFENSE);
+
+ if(UpdateSkill(SKILL_DEFENSE,defense_skill_gain))
+ {
+ // update dependent from defense skill part
+ UpdateDefenseBonusesMod();
+ }
+}
+
+void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply, bool affectStats)
+{
+ if(modGroup >= BASEMOD_END || modType >= MOD_END)
+ {
+ sLog.outError("ERROR in HandleBaseModValue(): nonexisted BaseModGroup of wrong BaseModType!");
+ return;
+ }
+
+ float val = 1.0f;
+
+ switch(modType)
+ {
+ case FLAT_MOD:
+ m_auraBaseMod[modGroup][modType] += apply ? amount : -amount;
+ break;
+ case PCT_MOD:
+ if(amount <= -100.0f)
+ amount = -200.0f;
+
+ val = (100.0f + amount) / 100.0f;
+ m_auraBaseMod[modGroup][modType] *= apply ? val : (1.0f/val);
+ break;
+ }
+
+ if(!CanModifyStats())
+ return;
+
+ switch(modGroup)
+ {
+ case CRIT_PERCENTAGE: UpdateCritPercentage(BASE_ATTACK); break;
+ case RANGED_CRIT_PERCENTAGE: UpdateCritPercentage(RANGED_ATTACK); break;
+ case OFFHAND_CRIT_PERCENTAGE: UpdateCritPercentage(OFF_ATTACK); break;
+ case SHIELD_BLOCK_VALUE: UpdateShieldBlockValue(); break;
+ default: break;
+ }
+}
+
+float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const
+{
+ if(modGroup >= BASEMOD_END || modType > MOD_END)
+ {
+ sLog.outError("ERROR: trial to access nonexisted BaseModGroup or wrong BaseModType!");
+ return 0.0f;
+ }
+
+ if(modType == PCT_MOD && m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
+ return 0.0f;
+
+ return m_auraBaseMod[modGroup][modType];
+}
+
+float Player::GetTotalBaseModValue(BaseModGroup modGroup) const
+{
+ if(modGroup >= BASEMOD_END)
+ {
+ sLog.outError("ERROR: wrong BaseModGroup in GetTotalBaseModValue()!");
+ return 0.0f;
+ }
+
+ if(m_auraBaseMod[modGroup][PCT_MOD] <= 0.0f)
+ return 0.0f;
+
+ return m_auraBaseMod[modGroup][FLAT_MOD] * m_auraBaseMod[modGroup][PCT_MOD];
+}
+
+uint32 Player::GetShieldBlockValue() const
+{
+ BaseModGroup modGroup = SHIELD_BLOCK_VALUE;
+
+ float value = GetTotalBaseModValue(modGroup) + GetStat(STAT_STRENGTH)/20 - 1;
+
+ value = (value < 0) ? 0 : value;
+
+ return uint32(value);
+}
+
+float Player::GetMeleeCritFromAgility()
+{
+ uint32 level = getLevel();
+ uint32 pclass = getClass();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+ GtChanceToMeleeCritBaseEntry const *critBase = sGtChanceToMeleeCritBaseStore.LookupEntry(pclass-1);
+ GtChanceToMeleeCritEntry const *critRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ if (critBase==NULL || critRatio==NULL)
+ return 0.0f;
+
+ float crit=critBase->base + GetStat(STAT_AGILITY)*critRatio->ratio;
+ return crit*100.0f;
+}
+
+float Player::GetDodgeFromAgility()
+{
+ // Table for base dodge values
+ float dodge_base[MAX_CLASSES] = {
+ 0.0075f, // Warrior
+ 0.00652f, // Paladin
+ -0.0545f, // Hunter
+ -0.0059f, // Rogue
+ 0.03183f, // Priest
+ 0.0114f, // DK
+ 0.0167f, // Shaman
+ 0.034575f, // Mage
+ 0.02011f, // Warlock
+ 0.0f, // ??
+ -0.0187f // Druid
+ };
+ // Crit/agility to dodge/agility coefficient multipliers
+ float crit_to_dodge[MAX_CLASSES] = {
+ 1.1f, // Warrior
+ 1.0f, // Paladin
+ 1.6f, // Hunter
+ 2.0f, // Rogue
+ 1.0f, // Priest
+ 1.0f, // DK?
+ 1.0f, // Shaman
+ 1.0f, // Mage
+ 1.0f, // Warlock
+ 0.0f, // ??
+ 1.7f // Druid
+ };
+
+ uint32 level = getLevel();
+ uint32 pclass = getClass();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+ // Dodge per agility for most classes equal crit per agility (but for some classes need apply some multiplier)
+ GtChanceToMeleeCritEntry const *dodgeRatio = sGtChanceToMeleeCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ if (dodgeRatio==NULL || pclass > MAX_CLASSES)
+ return 0.0f;
+
+ float dodge=dodge_base[pclass-1] + GetStat(STAT_AGILITY) * dodgeRatio->ratio * crit_to_dodge[pclass-1];
+ return dodge*100.0f;
+}
+
+float Player::GetSpellCritFromIntellect()
+{
+ uint32 level = getLevel();
+ uint32 pclass = getClass();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+ GtChanceToSpellCritBaseEntry const *critBase = sGtChanceToSpellCritBaseStore.LookupEntry(pclass-1);
+ GtChanceToSpellCritEntry const *critRatio = sGtChanceToSpellCritStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ if (critBase==NULL || critRatio==NULL)
+ return 0.0f;
+
+ float crit=critBase->base + GetStat(STAT_INTELLECT)*critRatio->ratio;
+ return crit*100.0f;
+}
+
+float Player::GetRatingCoefficient(CombatRating cr) const
+{
+ uint32 level = getLevel();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+ GtCombatRatingsEntry const *Rating = sGtCombatRatingsStore.LookupEntry(cr*GT_MAX_LEVEL+level-1);
+ if (Rating == NULL)
+ return 1.0f; // By default use minimum coefficient (not must be called)
+
+ return Rating->ratio;
+}
+
+float Player::GetRatingBonusValue(CombatRating cr) const
+{
+ return float(GetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr)) / GetRatingCoefficient(cr);
+}
+
+uint32 Player::GetMeleeCritDamageReduction(uint32 damage) const
+{
+ float melee = GetRatingBonusValue(CR_CRIT_TAKEN_MELEE)*2.0f;
+ if (melee>25.0f) melee = 25.0f;
+ return uint32 (melee * damage /100.0f);
+}
+
+uint32 Player::GetRangedCritDamageReduction(uint32 damage) const
+{
+ float ranged = GetRatingBonusValue(CR_CRIT_TAKEN_RANGED)*2.0f;
+ if (ranged>25.0f) ranged=25.0f;
+ return uint32 (ranged * damage /100.0f);
+}
+
+uint32 Player::GetSpellCritDamageReduction(uint32 damage) const
+{
+ float spell = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2.0f;
+ // In wow script resilience limited to 25%
+ if (spell>25.0f)
+ spell = 25.0f;
+ return uint32 (spell * damage / 100.0f);
+}
+
+uint32 Player::GetDotDamageReduction(uint32 damage) const
+{
+ float spellDot = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL);
+ // Dot resilience not limited (limit it by 100%)
+ if (spellDot > 100.0f)
+ spellDot = 100.0f;
+ return uint32 (spellDot * damage / 100.0f);
+}
+
+float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const
+{
+ switch (attType)
+ {
+ case BASE_ATTACK:
+ return GetUInt32Value(PLAYER_EXPERTISE) / 4.0f;
+ case OFF_ATTACK:
+ return GetUInt32Value(PLAYER_OFFHAND_EXPERTISE) / 4.0f;
+ default:
+ break;
+ }
+ return 0.0f;
+}
+
+float Player::OCTRegenHPPerSpirit()
+{
+ uint32 level = getLevel();
+ uint32 pclass = getClass();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+ GtOCTRegenHPEntry const *baseRatio = sGtOCTRegenHPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ GtRegenHPPerSptEntry const *moreRatio = sGtRegenHPPerSptStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ if (baseRatio==NULL || moreRatio==NULL)
+ return 0.0f;
+
+ // Formula from PaperDollFrame script
+ float spirit = GetStat(STAT_SPIRIT);
+ float baseSpirit = spirit;
+ if (baseSpirit>50) baseSpirit = 50;
+ float moreSpirit = spirit - baseSpirit;
+ float regen = baseSpirit * baseRatio->ratio + moreSpirit * moreRatio->ratio;
+ return regen;
+}
+
+float Player::OCTRegenMPPerSpirit()
+{
+ uint32 level = getLevel();
+ uint32 pclass = getClass();
+
+ if (level>GT_MAX_LEVEL) level = GT_MAX_LEVEL;
+
+// GtOCTRegenMPEntry const *baseRatio = sGtOCTRegenMPStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ GtRegenMPPerSptEntry const *moreRatio = sGtRegenMPPerSptStore.LookupEntry((pclass-1)*GT_MAX_LEVEL + level-1);
+ if (moreRatio==NULL)
+ return 0.0f;
+
+ // Formula get from PaperDollFrame script
+ float spirit = GetStat(STAT_SPIRIT);
+ float regen = spirit * moreRatio->ratio;
+ return regen;
+}
+
+void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply)
+{
+ ApplyModUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, value, apply);
+
+ float RatingCoeffecient = GetRatingCoefficient(cr);
+ float RatingChange = 0.0f;
+
+ bool affectStats = CanModifyStats();
+
+ switch (cr)
+ {
+ case CR_WEAPON_SKILL: // Implemented in Unit::RollMeleeOutcomeAgainst
+ case CR_DEFENSE_SKILL:
+ UpdateDefenseBonusesMod();
+ break;
+ case CR_DODGE:
+ UpdateDodgePercentage();
+ break;
+ case CR_PARRY:
+ UpdateParryPercentage();
+ break;
+ case CR_BLOCK:
+ UpdateBlockPercentage();
+ break;
+ case CR_HIT_MELEE:
+ RatingChange = value / RatingCoeffecient;
+ m_modMeleeHitChance += apply ? RatingChange : -RatingChange;
+ break;
+ case CR_HIT_RANGED:
+ RatingChange = value / RatingCoeffecient;
+ m_modRangedHitChance += apply ? RatingChange : -RatingChange;
+ break;
+ case CR_HIT_SPELL:
+ RatingChange = value / RatingCoeffecient;
+ m_modSpellHitChance += apply ? RatingChange : -RatingChange;
+ break;
+ case CR_CRIT_MELEE:
+ if(affectStats)
+ {
+ UpdateCritPercentage(BASE_ATTACK);
+ UpdateCritPercentage(OFF_ATTACK);
+ }
+ break;
+ case CR_CRIT_RANGED:
+ if(affectStats)
+ UpdateCritPercentage(RANGED_ATTACK);
+ break;
+ case CR_CRIT_SPELL:
+ if(affectStats)
+ UpdateAllSpellCritChances();
+ break;
+ case CR_HIT_TAKEN_MELEE: // Implemented in Unit::MeleeMissChanceCalc
+ case CR_HIT_TAKEN_RANGED:
+ break;
+ case CR_HIT_TAKEN_SPELL: // Implemented in Unit::MagicSpellHitResult
+ break;
+ case CR_CRIT_TAKEN_MELEE: // Implemented in Unit::RollMeleeOutcomeAgainst (only for chance to crit)
+ case CR_CRIT_TAKEN_RANGED:
+ break;
+ case CR_CRIT_TAKEN_SPELL: // Implemented in Unit::SpellCriticalBonus (only for chance to crit)
+ break;
+ case CR_HASTE_MELEE:
+ RatingChange = value / RatingCoeffecient;
+ ApplyAttackTimePercentMod(BASE_ATTACK,RatingChange,apply);
+ ApplyAttackTimePercentMod(OFF_ATTACK,RatingChange,apply);
+ break;
+ case CR_HASTE_RANGED:
+ RatingChange = value / RatingCoeffecient;
+ ApplyAttackTimePercentMod(RANGED_ATTACK, RatingChange, apply);
+ break;
+ case CR_HASTE_SPELL:
+ RatingChange = value / RatingCoeffecient;
+ ApplyCastTimePercentMod(RatingChange,apply);
+ break;
+ case CR_WEAPON_SKILL_MAINHAND: // Implemented in Unit::RollMeleeOutcomeAgainst
+ case CR_WEAPON_SKILL_OFFHAND:
+ case CR_WEAPON_SKILL_RANGED:
+ break;
+ case CR_EXPERTISE:
+ if(affectStats)
+ {
+ UpdateExpertise(BASE_ATTACK);
+ UpdateExpertise(OFF_ATTACK);
+ }
+ break;
+ }
+}
+
+void Player::SetRegularAttackTime()
+{
+ for(int i = 0; i < MAX_ATTACK; ++i)
+ {
+ Item *tmpitem = GetWeaponForAttack(WeaponAttackType(i));
+ if(tmpitem && !tmpitem->IsBroken())
+ {
+ ItemPrototype const *proto = tmpitem->GetProto();
+ if(proto->Delay)
+ SetAttackTime(WeaponAttackType(i), proto->Delay);
+ else
+ SetAttackTime(WeaponAttackType(i), BASE_ATTACK_TIME);
+ }
+ }
+}
+
+//skill+step, checking for max value
+bool Player::UpdateSkill(uint32 skill_id, uint32 step)
+{
+ if(!skill_id)
+ return false;
+
+ uint16 i=0;
+ for (; i < PLAYER_MAX_SKILLS; i++)
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill_id)
+ break;
+
+ if(i>=PLAYER_MAX_SKILLS)
+ return false;
+
+ uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
+ uint32 value = SKILL_VALUE(data);
+ uint32 max = SKILL_MAX(data);
+
+ if ((!max) || (!value) || (value >= max))
+ return false;
+
+ if (value*512 < max*urand(0,512))
+ {
+ uint32 new_value = value+step;
+ if(new_value > max)
+ new_value = max;
+
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,max));
+ return true;
+ }
+
+ return false;
+}
+
+inline int SkillGainChance(uint32 SkillValue, uint32 GrayLevel, uint32 GreenLevel, uint32 YellowLevel)
+{
+ if ( SkillValue >= GrayLevel )
+ return sWorld.getConfig(CONFIG_SKILL_CHANCE_GREY)*10;
+ if ( SkillValue >= GreenLevel )
+ return sWorld.getConfig(CONFIG_SKILL_CHANCE_GREEN)*10;
+ if ( SkillValue >= YellowLevel )
+ return sWorld.getConfig(CONFIG_SKILL_CHANCE_YELLOW)*10;
+ return sWorld.getConfig(CONFIG_SKILL_CHANCE_ORANGE)*10;
+}
+
+bool Player::UpdateCraftSkill(uint32 spellid)
+{
+ sLog.outDebug("UpdateCraftSkill spellid %d", spellid);
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ if(_spell_idx->second->skillId)
+ {
+ uint32 SkillValue = GetPureSkillValue(_spell_idx->second->skillId);
+
+ // Alchemy Discoveries here
+ SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellid);
+ if(spellEntry && spellEntry->Mechanic==MECHANIC_DISCOVERY)
+ {
+ if(uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this))
+ learnSpell(discoveredSpell);
+ }
+
+ uint32 craft_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_CRAFTING);
+
+ return UpdateSkillPro(_spell_idx->second->skillId, SkillGainChance(SkillValue,
+ _spell_idx->second->max_value,
+ (_spell_idx->second->max_value + _spell_idx->second->min_value)/2,
+ _spell_idx->second->min_value),
+ craft_skill_gain);
+ }
+ }
+ return false;
+}
+
+bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator )
+{
+ sLog.outDebug("UpdateGatherSkill(SkillId %d SkillLevel %d RedLevel %d)", SkillId, SkillValue, RedLevel);
+
+ uint32 gathering_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_GATHERING);
+
+ // For skinning and Mining chance decrease with level. 1-74 - no decrease, 75-149 - 2 times, 225-299 - 8 times
+ switch (SkillId)
+ {
+ case SKILL_HERBALISM:
+ case SKILL_LOCKPICKING:
+ case SKILL_JEWELCRAFTING:
+ return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
+ case SKILL_SKINNING:
+ if( sWorld.getConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS)==0)
+ return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
+ else
+ return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator) >> (SkillValue/sWorld.getConfig(CONFIG_SKILL_CHANCE_SKINNING_STEPS)), gathering_skill_gain);
+ case SKILL_MINING:
+ if (sWorld.getConfig(CONFIG_SKILL_CHANCE_MINING_STEPS)==0)
+ return UpdateSkillPro(SkillId, SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator,gathering_skill_gain);
+ else
+ return UpdateSkillPro(SkillId, (SkillGainChance(SkillValue, RedLevel+100, RedLevel+50, RedLevel+25)*Multiplicator) >> (SkillValue/sWorld.getConfig(CONFIG_SKILL_CHANCE_MINING_STEPS)),gathering_skill_gain);
+ }
+ return false;
+}
+
+bool Player::UpdateFishingSkill()
+{
+ sLog.outDebug("UpdateFishingSkill");
+
+ uint32 SkillValue = GetPureSkillValue(SKILL_FISHING);
+
+ int32 chance = SkillValue < 75 ? 100 : 2500/(SkillValue-50);
+
+ uint32 gathering_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_GATHERING);
+
+ return UpdateSkillPro(SKILL_FISHING,chance*10,gathering_skill_gain);
+}
+
+bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step)
+{
+ sLog.outDebug("UpdateSkillPro(SkillId %d, Chance %3.1f%%)", SkillId, Chance/10.0);
+ if ( !SkillId )
+ return false;
+
+ if(Chance <= 0) // speedup in 0 chance case
+ {
+ sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance/10.0);
+ return false;
+ }
+
+ uint16 i=0;
+ for (; i < PLAYER_MAX_SKILLS; i++)
+ if ( SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_INDEX(i))) == SkillId ) break;
+ if ( i >= PLAYER_MAX_SKILLS )
+ return false;
+
+ uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
+ uint16 SkillValue = SKILL_VALUE(data);
+ uint16 MaxValue = SKILL_MAX(data);
+
+ if ( !MaxValue || !SkillValue || SkillValue >= MaxValue )
+ return false;
+
+ int32 Roll = irand(1,1000);
+
+ if ( Roll <= Chance )
+ {
+ uint32 new_value = SkillValue+step;
+ if(new_value > MaxValue)
+ new_value = MaxValue;
+
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,MaxValue));
+ sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% taken", Chance/10.0);
+ return true;
+ }
+
+ sLog.outDebug("Player::UpdateSkillPro Chance=%3.1f%% missed", Chance/10.0);
+ return false;
+}
+
+void Player::UpdateWeaponSkill (WeaponAttackType attType)
+{
+ // no skill gain in pvp
+ Unit *pVictim = getVictim();
+ if(pVictim && pVictim->GetTypeId() == TYPEID_PLAYER)
+ return;
+
+ if(IsInFeralForm())
+ return; // always maximized SKILL_FERAL_COMBAT in fact
+
+ if(m_form == FORM_TREE)
+ return; // use weapon but not skill up
+
+ uint32 weapon_skill_gain = sWorld.getConfig(CONFIG_SKILL_GAIN_WEAPON);
+
+ switch(attType)
+ {
+ case BASE_ATTACK:
+ {
+ Item *tmpitem = GetWeaponForAttack(attType,true);
+
+ if (!tmpitem)
+ UpdateSkill(SKILL_UNARMED,weapon_skill_gain);
+ else if(tmpitem->GetProto()->SubClass != ITEM_SUBCLASS_WEAPON_FISHING_POLE)
+ UpdateSkill(tmpitem->GetSkill(),weapon_skill_gain);
+ break;
+ }
+ case OFF_ATTACK:
+ case RANGED_ATTACK:
+ {
+ Item *tmpitem = GetWeaponForAttack(attType,true);
+ if (tmpitem)
+ UpdateSkill(tmpitem->GetSkill(),weapon_skill_gain);
+ break;
+ }
+ }
+ UpdateAllCritPercentages();
+}
+
+void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, MeleeHitOutcome outcome, bool defence)
+{
+ switch(outcome)
+ {
+ case MELEE_HIT_CRIT:
+ case MELEE_HIT_DODGE:
+ case MELEE_HIT_PARRY:
+ case MELEE_HIT_BLOCK:
+ case MELEE_HIT_BLOCK_CRIT:
+ return;
+
+ default:
+ break;
+ }
+
+ uint32 plevel = getLevel(); // if defense than pVictim == attacker
+ uint32 greylevel = MaNGOS::XP::GetGrayLevel(plevel);
+ uint32 moblevel = pVictim->getLevelForTarget(this);
+ if(moblevel < greylevel)
+ return;
+
+ if (moblevel > plevel + 5)
+ moblevel = plevel + 5;
+
+ uint32 lvldif = moblevel - greylevel;
+ if(lvldif < 3)
+ lvldif = 3;
+
+ uint32 skilldif = 5 * plevel - (defence ? GetBaseDefenseSkillValue() : GetBaseWeaponSkillValue(attType));
+ if(skilldif <= 0)
+ return;
+
+ float chance = float(3 * lvldif * skilldif) / plevel;
+ if(!defence)
+ {
+ if(getClass() == CLASS_WARRIOR || getClass() == CLASS_ROGUE)
+ chance *= 0.1f * GetStat(STAT_INTELLECT);
+ }
+
+ chance = chance < 1.0f ? 1.0f : chance; //minimum chance to increase skill is 1%
+
+ if(roll_chance_f(chance))
+ {
+ if(defence)
+ UpdateDefense();
+ else
+ UpdateWeaponSkill(attType);
+ }
+ else
+ return;
+}
+
+void Player::ModifySkillBonus(uint32 skillid,int32 val, bool talent)
+{
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skillid)
+ {
+ uint32 bonus_val = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));
+ int16 temp_bonus = SKILL_TEMP_BONUS(bonus_val);
+ int16 perm_bonus = SKILL_PERM_BONUS(bonus_val);
+
+ if(talent) // permanent bonus stored in high part
+ SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),MAKE_SKILL_BONUS(temp_bonus,perm_bonus+val));
+ else // temporary/item bonus stored in low part
+ SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),MAKE_SKILL_BONUS(temp_bonus+val,perm_bonus));
+ return;
+ }
+}
+
+void Player::UpdateMaxSkills()
+{
+ uint16 maxconfskill = sWorld.GetConfigMaxSkillValue();
+
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ if (GetUInt32Value(PLAYER_SKILL_INDEX(i)))
+ {
+ uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
+
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(pskill);
+ if(!pSkill)
+ continue;
+
+ if(GetSkillRangeType(pSkill,false) != SKILL_RANGE_LEVEL)
+ continue;
+
+ uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
+ uint32 max = SKILL_MAX(data);
+ uint32 val = SKILL_VALUE(data);
+
+ // update only level dependent max skill values
+ if(max!=1 && max != maxconfskill)
+ {
+ uint32 max_Skill = GetMaxSkillValueForLevel();
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(val,max_Skill));
+ }
+ }
+}
+
+void Player::UpdateSkillsToMaxSkillsForLevel()
+{
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ if (GetUInt32Value(PLAYER_SKILL_INDEX(i)))
+ {
+ uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
+ if( IsProfessionSkill(pskill) || pskill == SKILL_RIDING )
+ continue;
+ uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
+
+ uint32 max = SKILL_MAX(data);
+
+ if(max > 1)
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(max,max));
+
+ if(pskill == SKILL_DEFENSE)
+ UpdateDefenseBonusesMod();
+ }
+}
+
+// This functions sets a skill line value (and adds if doesn't exist yet)
+// To "remove" a skill line, set it's values to zero
+void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)
+{
+ if(!id)
+ return;
+
+ uint16 i=0;
+ for (; i < PLAYER_MAX_SKILLS; i++)
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == id) break;
+
+ if(i<PLAYER_MAX_SKILLS) //has skill
+ {
+ if(currVal)
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal));
+ else //remove
+ {
+ // clear skill fields
+ SetUInt32Value(PLAYER_SKILL_INDEX(i),0);
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),0);
+ SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
+
+ // remove spells that depend on this skill when removing the skill
+ for (PlayerSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
+ {
+ ++next;
+ if(itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(itr->first);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ if (_spell_idx->second->skillId == id)
+ {
+ // this may remove more than one spell (dependants)
+ removeSpell(itr->first);
+ next = m_spells.begin();
+ break;
+ }
+ }
+ }
+ }
+ }
+ else if(currVal) //add
+ {
+ for (i=0; i < PLAYER_MAX_SKILLS; i++)
+ if (!GetUInt32Value(PLAYER_SKILL_INDEX(i)))
+ {
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id);
+ if(!pSkill)
+ {
+ sLog.outError("Skill not found in SkillLineStore: skill #%u", id);
+ return;
+ }
+ // enable unlearn button for primary professions only
+ if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
+ SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,1));
+ else
+ SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0));
+ SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal));
+
+ // apply skill bonuses
+ SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
+
+ // temporary bonuses
+ AuraList const& mModSkill = GetAurasByType(SPELL_AURA_MOD_SKILL);
+ for(AuraList::const_iterator i = mModSkill.begin(); i != mModSkill.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue == int32(id))
+ (*i)->ApplyModifier(true);
+
+ // permanent bonuses
+ AuraList const& mModSkillTalent = GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT);
+ for(AuraList::const_iterator i = mModSkillTalent.begin(); i != mModSkillTalent.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue == int32(id))
+ (*i)->ApplyModifier(true);
+
+ // Learn all spells for skill
+ learnSkillRewardedSpells(id);
+ return;
+ }
+ }
+}
+
+bool Player::HasSkill(uint32 skill) const
+{
+ if(!skill)return false;
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+uint16 Player::GetSkillValue(uint32 skill) const
+{
+ if(!skill)
+ return 0;
+
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));
+
+ int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));
+ result += SKILL_TEMP_BONUS(bonus);
+ result += SKILL_PERM_BONUS(bonus);
+ return result < 0 ? 0 : result;
+ }
+ }
+ return 0;
+}
+
+uint16 Player::GetMaxSkillValue(uint32 skill) const
+{
+ if(!skill)return 0;
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ uint32 bonus = GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i));
+
+ int32 result = int32(SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));
+ result += SKILL_TEMP_BONUS(bonus);
+ result += SKILL_PERM_BONUS(bonus);
+ return result < 0 ? 0 : result;
+ }
+ }
+ return 0;
+}
+
+uint16 Player::GetPureMaxSkillValue(uint32 skill) const
+{
+ if(!skill)return 0;
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ return SKILL_MAX(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)));
+ }
+ }
+ return 0;
+}
+
+uint16 Player::GetBaseSkillValue(uint32 skill) const
+{
+ if(!skill)return 0;
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ int32 result = int32(SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))));
+ result += SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));
+ return result < 0 ? 0 : result;
+ }
+ }
+ return 0;
+}
+
+uint16 Player::GetPureSkillValue(uint32 skill) const
+{
+ if(!skill)return 0;
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ return SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)));
+ }
+ }
+ return 0;
+}
+
+int16 Player::GetSkillTempBonusValue(uint32 skill) const
+{
+ if(!skill)
+ return 0;
+
+ for (int i = 0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
+ {
+ return SKILL_TEMP_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));
+ }
+ }
+
+ return 0;
+}
+
+void Player::SendInitialActionButtons()
+{
+ sLog.outDetail( "Initializing Action Buttons for '%u'", GetGUIDLow() );
+
+ WorldPacket data(SMSG_ACTION_BUTTONS, (MAX_ACTION_BUTTONS*4));
+ for(int button = 0; button < MAX_ACTION_BUTTONS; ++button)
+ {
+ ActionButtonList::const_iterator itr = m_actionButtons.find(button);
+ if(itr != m_actionButtons.end() && itr->second.uState != ACTIONBUTTON_DELETED)
+ {
+ data << uint16(itr->second.action);
+ data << uint8(itr->second.misc);
+ data << uint8(itr->second.type);
+ }
+ else
+ {
+ data << uint32(0);
+ }
+ }
+
+ GetSession()->SendPacket( &data );
+ sLog.outDetail( "Action Buttons for '%u' Initialized", GetGUIDLow() );
+}
+
+void Player::addActionButton(const uint8 button, const uint16 action, const uint8 type, const uint8 misc)
+{
+ if(button >= MAX_ACTION_BUTTONS)
+ {
+ sLog.outError( "Action %u not added into button %u for player %s: button must be < 132", action, button, GetName() );
+ return;
+ }
+
+ // check cheating with adding non-known spells to action bar
+ if(type==ACTION_BUTTON_SPELL)
+ {
+ if(!sSpellStore.LookupEntry(action))
+ {
+ sLog.outError( "Action %u not added into button %u for player %s: spell not exist", action, button, GetName() );
+ return;
+ }
+
+ if(!HasSpell(action))
+ {
+ sLog.outError( "Action %u not added into button %u for player %s: player don't known this spell", action, button, GetName() );
+ return;
+ }
+ }
+
+ ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
+
+ if (buttonItr==m_actionButtons.end())
+ { // just add new button
+ m_actionButtons[button] = ActionButton(action,type,misc);
+ }
+ else
+ { // change state of current button
+ ActionButtonUpdateState uState = buttonItr->second.uState;
+ buttonItr->second = ActionButton(action,type,misc);
+ if (uState != ACTIONBUTTON_NEW) buttonItr->second.uState = ACTIONBUTTON_CHANGED;
+ };
+
+ sLog.outDetail( "Player '%u' Added Action '%u' to Button '%u'", GetGUIDLow(), action, button );
+}
+
+void Player::removeActionButton(uint8 button)
+{
+ ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
+ if (buttonItr==m_actionButtons.end())
+ return;
+
+ if(buttonItr->second.uState==ACTIONBUTTON_NEW)
+ m_actionButtons.erase(buttonItr); // new and not saved
+ else
+ buttonItr->second.uState = ACTIONBUTTON_DELETED; // saved, will deleted at next save
+
+ sLog.outDetail( "Action Button '%u' Removed from Player '%u'", button, GetGUIDLow() );
+}
+
+void Player::SetDontMove(bool dontMove)
+{
+ m_dontMove = dontMove;
+}
+
+bool Player::SetPosition(float x, float y, float z, float orientation, bool teleport)
+{
+ // prevent crash when a bad coord is sent by the client
+ if(!MaNGOS::IsValidMapCoord(x,y,z,orientation))
+ {
+ sLog.outDebug("Player::SetPosition(%f, %f, %f, %f, %d) .. bad coordinates for player %d!",x,y,z,orientation,teleport,GetGUIDLow());
+ return false;
+ }
+
+ Map *m = MapManager::Instance().GetMap(GetMapId(), this);
+
+ const float old_x = GetPositionX();
+ const float old_y = GetPositionY();
+ const float old_z = GetPositionZ();
+ const float old_r = GetOrientation();
+
+ if( teleport || old_x != x || old_y != y || old_z != z || old_r != orientation )
+ {
+ if (teleport || old_x != x || old_y != y || old_z != z)
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING);
+ else
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);
+
+ // move and update visible state if need
+ m->PlayerRelocation(this, x, y, z, orientation);
+
+ // reread after Map::Relocation
+ m = MapManager::Instance().GetMap(GetMapId(), this);
+ x = GetPositionX();
+ y = GetPositionY();
+ z = GetPositionZ();
+ }
+
+ // code block for underwater state update
+ UpdateUnderwaterState(m, x, y, z);
+
+
+ CheckExploreSystem();
+
+ // group update
+ if(GetGroup())
+ SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
+
+ return true;
+}
+
+void Player::SaveRecallPosition()
+{
+ m_recallMap = GetMapId();
+ m_recallX = GetPositionX();
+ m_recallY = GetPositionY();
+ m_recallZ = GetPositionZ();
+ m_recallO = GetOrientation();
+}
+
+void Player::SendMessageToSet(WorldPacket *data, bool self)
+{
+ MapManager::Instance().GetMap(GetMapId(), this)->MessageBroadcast(this, data, self);
+}
+
+void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self)
+{
+ MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self);
+}
+
+void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only)
+{
+ MapManager::Instance().GetMap(GetMapId(), this)->MessageDistBroadcast(this, data, dist, self,own_team_only);
+}
+
+void Player::SendDirectMessage(WorldPacket *data)
+{
+ GetSession()->SendPacket(data);
+}
+
+void Player::CheckExploreSystem()
+{
+ if (!isAlive())
+ return;
+
+ if (isInFlight())
+ return;
+
+ uint16 areaFlag=MapManager::Instance().GetBaseMap(GetMapId())->GetAreaFlag(GetPositionX(),GetPositionY());
+ if(areaFlag==0xffff)
+ return;
+ int offset = areaFlag / 32;
+
+ if(offset >= 128)
+ {
+ sLog.outError("ERROR: Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < 64 ).",areaFlag,GetPositionX(),GetPositionY(),offset,offset);
+ return;
+ }
+
+ uint32 val = (uint32)(1 << (areaFlag % 32));
+ uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
+
+ if( !(currFields & val) )
+ {
+ SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val));
+
+ AreaTableEntry const *p = GetAreaEntryByAreaFlagAndMap(areaFlag,GetMapId());
+ if(!p)
+ {
+ sLog.outError("PLAYER: Player %u discovered unknown area (x: %f y: %f map: %u", GetGUIDLow(), GetPositionX(),GetPositionY(),GetMapId());
+ }
+ else if(p->area_level > 0)
+ {
+ uint32 area = p->ID;
+ if (getLevel() >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ {
+ SendExplorationExperience(area,0);
+ }
+ else
+ {
+ int32 diff = int32(getLevel()) - p->area_level;
+ uint32 XP = 0;
+ if (diff < -5)
+ {
+ XP = uint32(objmgr.GetBaseXP(getLevel()+5)*sWorld.getRate(RATE_XP_EXPLORE));
+ }
+ else if (diff > 5)
+ {
+ int32 exploration_percent = (100-((diff-5)*5));
+ if (exploration_percent > 100)
+ exploration_percent = 100;
+ else if (exploration_percent < 0)
+ exploration_percent = 0;
+
+ XP = uint32(objmgr.GetBaseXP(p->area_level)*exploration_percent/100*sWorld.getRate(RATE_XP_EXPLORE));
+ }
+ else
+ {
+ XP = uint32(objmgr.GetBaseXP(p->area_level)*sWorld.getRate(RATE_XP_EXPLORE));
+ }
+
+ GiveXP( XP, NULL );
+ SendExplorationExperience(area,XP);
+ }
+ sLog.outDetail("PLAYER: Player %u discovered a new area: %u", GetGUIDLow(), area);
+ }
+ }
+}
+
+uint32 Player::TeamForRace(uint8 race)
+{
+ ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
+ if(!rEntry)
+ {
+ sLog.outError("Race %u not found in DBC: wrong DBC files?",uint32(race));
+ return ALLIANCE;
+ }
+
+ switch(rEntry->TeamID)
+ {
+ case 7: return ALLIANCE;
+ case 1: return HORDE;
+ }
+
+ sLog.outError("Race %u have wrong team id in DBC: wrong DBC files?",uint32(race),rEntry->TeamID);
+ return ALLIANCE;
+}
+
+uint32 Player::getFactionForRace(uint8 race)
+{
+ ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
+ if(!rEntry)
+ {
+ sLog.outError("Race %u not found in DBC: wrong DBC files?",uint32(race));
+ return 0;
+ }
+
+ return rEntry->FactionID;
+}
+
+void Player::setFactionForRace(uint8 race)
+{
+ m_team = TeamForRace(race);
+ setFaction( getFactionForRace(race) );
+}
+
+void Player::UpdateReputation() const
+{
+ sLog.outDetail( "WORLD: Player::UpdateReputation" );
+
+ for(FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
+ {
+ SendFactionState(&(itr->second));
+ }
+}
+
+void Player::SendFactionState(FactionState const* faction) const
+{
+ if(faction->Flags & FACTION_FLAG_VISIBLE) //If faction is visible then update it
+ {
+ WorldPacket data(SMSG_SET_FACTION_STANDING, (16)); // last check 2.4.0
+ data << (float) 0; // unk 2.4.0
+ data << (uint32) 1; // count
+ // for
+ data << (uint32) faction->ReputationListID;
+ data << (uint32) faction->Standing;
+ // end for
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::SendInitialReputations()
+{
+ WorldPacket data(SMSG_INITIALIZE_FACTIONS, (4+128*5));
+ data << uint32 (0x00000080);
+
+ RepListID a = 0;
+
+ for (FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); itr++)
+ {
+ // fill in absent fields
+ for (; a != itr->first; a++)
+ {
+ data << uint8 (0x00);
+ data << uint32 (0x00000000);
+ }
+
+ // fill in encountered data
+ data << uint8 (itr->second.Flags);
+ data << uint32 (itr->second.Standing);
+
+ ++a;
+ }
+
+ // fill in absent fields
+ for (; a != 128; a++)
+ {
+ data << uint8 (0x00);
+ data << uint32 (0x00000000);
+ }
+
+ GetSession()->SendPacket(&data);
+}
+
+FactionState const* Player::GetFactionState( FactionEntry const* factionEntry) const
+{
+ FactionStateList::const_iterator itr = m_factions.find(factionEntry->reputationListID);
+ if (itr != m_factions.end())
+ return &itr->second;
+
+ return NULL;
+}
+
+void Player::SetFactionAtWar(FactionState* faction, bool atWar)
+{
+ // not allow declare war to own faction
+ if(atWar && (faction->Flags & FACTION_FLAG_PEACE_FORCED) )
+ return;
+
+ // already set
+ if(((faction->Flags & FACTION_FLAG_AT_WAR) != 0) == atWar)
+ return;
+
+ if( atWar )
+ faction->Flags |= FACTION_FLAG_AT_WAR;
+ else
+ faction->Flags &= ~FACTION_FLAG_AT_WAR;
+
+ faction->Changed = true;
+}
+
+void Player::SetFactionInactive(FactionState* faction, bool inactive)
+{
+ // always invisible or hidden faction can't be inactive
+ if(inactive && ((faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN)) || !(faction->Flags & FACTION_FLAG_VISIBLE) ) )
+ return;
+
+ // already set
+ if(((faction->Flags & FACTION_FLAG_INACTIVE) != 0) == inactive)
+ return;
+
+ if(inactive)
+ faction->Flags |= FACTION_FLAG_INACTIVE;
+ else
+ faction->Flags &= ~FACTION_FLAG_INACTIVE;
+
+ faction->Changed = true;
+}
+
+void Player::SetFactionVisibleForFactionTemplateId(uint32 FactionTemplateId)
+{
+ FactionTemplateEntry const*factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId);
+
+ if(!factionTemplateEntry)
+ return;
+
+ SetFactionVisibleForFactionId(factionTemplateEntry->faction);
+}
+
+void Player::SetFactionVisibleForFactionId(uint32 FactionId)
+{
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(FactionId);
+ if(!factionEntry)
+ return;
+
+ if(factionEntry->reputationListID < 0)
+ return;
+
+ FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
+ if (itr == m_factions.end())
+ return;
+
+ SetFactionVisible(&itr->second);
+}
+
+void Player::SetFactionVisible(FactionState* faction)
+{
+ // always invisible or hidden faction can't be make visible
+ if(faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN))
+ return;
+
+ // already set
+ if(faction->Flags & FACTION_FLAG_VISIBLE)
+ return;
+
+ faction->Flags |= FACTION_FLAG_VISIBLE;
+ faction->Changed = true;
+
+ if(!m_session->PlayerLoading())
+ {
+ // make faction visible in reputation list at client
+ WorldPacket data(SMSG_SET_FACTION_VISIBLE, 4);
+ data << faction->ReputationListID;
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::SetInitialFactions()
+{
+ for(unsigned int i = 1; i < sFactionStore.GetNumRows(); i++)
+ {
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(i);
+
+ if( factionEntry && (factionEntry->reputationListID >= 0))
+ {
+ FactionState newFaction;
+ newFaction.ID = factionEntry->ID;
+ newFaction.ReputationListID = factionEntry->reputationListID;
+ newFaction.Standing = 0;
+ newFaction.Flags = GetDefaultReputationFlags(factionEntry);
+ newFaction.Changed = true;
+
+ m_factions[newFaction.ReputationListID] = newFaction;
+ }
+ }
+}
+
+uint32 Player::GetDefaultReputationFlags(const FactionEntry *factionEntry) const
+{
+ if (!factionEntry)
+ return 0;
+
+ uint32 raceMask = getRaceMask();
+ uint32 classMask = getClassMask();
+ for (int i=0; i < 4; i++)
+ {
+ if( (factionEntry->BaseRepRaceMask[i] & raceMask) &&
+ (factionEntry->BaseRepClassMask[i]==0 ||
+ (factionEntry->BaseRepClassMask[i] & classMask) ) )
+ return factionEntry->ReputationFlags[i];
+ }
+ return 0;
+}
+
+int32 Player::GetBaseReputation(const FactionEntry *factionEntry) const
+{
+ if (!factionEntry)
+ return 0;
+
+ uint32 raceMask = getRaceMask();
+ uint32 classMask = getClassMask();
+ for (int i=0; i < 4; i++)
+ {
+ if( (factionEntry->BaseRepRaceMask[i] & raceMask) &&
+ (factionEntry->BaseRepClassMask[i]==0 ||
+ (factionEntry->BaseRepClassMask[i] & classMask) ) )
+ return factionEntry->BaseRepValue[i];
+ }
+
+ // in faction.dbc exist factions with (RepListId >=0, listed in character reputation list) with all BaseRepRaceMask[i]==0
+ return 0;
+}
+
+int32 Player::GetReputation(uint32 faction_id) const
+{
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(faction_id);
+
+ if (!factionEntry)
+ {
+ sLog.outError("Player::GetReputation: Can't get reputation of %s for unknown faction (faction template id) #%u.",GetName(), faction_id);
+ return 0;
+ }
+
+ return GetReputation(factionEntry);
+}
+
+int32 Player::GetReputation(const FactionEntry *factionEntry) const
+{
+ // Faction without recorded reputation. Just ignore.
+ if(!factionEntry)
+ return 0;
+
+ FactionStateList::const_iterator itr = m_factions.find(factionEntry->reputationListID);
+ if (itr != m_factions.end())
+ return GetBaseReputation(factionEntry) + itr->second.Standing;
+
+ return 0;
+}
+
+ReputationRank Player::GetReputationRank(uint32 faction) const
+{
+ FactionEntry const*factionEntry = sFactionStore.LookupEntry(faction);
+ if(!factionEntry)
+ return MIN_REPUTATION_RANK;
+
+ return GetReputationRank(factionEntry);
+}
+
+ReputationRank Player::ReputationToRank(int32 standing) const
+{
+ int32 Limit = Reputation_Cap + 1;
+ for (int i = MAX_REPUTATION_RANK-1; i >= MIN_REPUTATION_RANK; --i)
+ {
+ Limit -= ReputationRank_Length[i];
+ if (standing >= Limit )
+ return ReputationRank(i);
+ }
+ return MIN_REPUTATION_RANK;
+}
+
+ReputationRank Player::GetReputationRank(const FactionEntry *factionEntry) const
+{
+ int32 Reputation = GetReputation(factionEntry);
+ return ReputationToRank(Reputation);
+}
+
+ReputationRank Player::GetBaseReputationRank(const FactionEntry *factionEntry) const
+{
+ int32 Reputation = GetBaseReputation(factionEntry);
+ return ReputationToRank(Reputation);
+}
+
+bool Player::ModifyFactionReputation(uint32 FactionTemplateId, int32 DeltaReputation)
+{
+ FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId);
+
+ if(!factionTemplateEntry)
+ {
+ sLog.outError("Player::ModifyFactionReputation: Can't update reputation of %s for unknown faction (faction template id) #%u.", GetName(), FactionTemplateId);
+ return false;
+ }
+
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction);
+
+ // Faction without recorded reputation. Just ignore.
+ if(!factionEntry)
+ return false;
+
+ return ModifyFactionReputation(factionEntry, DeltaReputation);
+}
+
+bool Player::ModifyFactionReputation(FactionEntry const* factionEntry, int32 standing)
+{
+ SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID);
+ if (flist)
+ {
+ bool res = false;
+ for (SimpleFactionsList::const_iterator itr = flist->begin();itr != flist->end();++itr)
+ {
+ FactionEntry const *factionEntryCalc = sFactionStore.LookupEntry(*itr);
+ if(factionEntryCalc)
+ res = ModifyOneFactionReputation(factionEntryCalc, standing);
+ }
+ return res;
+ }
+ else
+ return ModifyOneFactionReputation(factionEntry, standing);
+}
+
+bool Player::ModifyOneFactionReputation(FactionEntry const* factionEntry, int32 standing)
+{
+ FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
+ if (itr != m_factions.end())
+ {
+ int32 BaseRep = GetBaseReputation(factionEntry);
+ int32 new_rep = BaseRep + itr->second.Standing + standing;
+
+ if (new_rep > Reputation_Cap)
+ new_rep = Reputation_Cap;
+ else
+ if (new_rep < Reputation_Bottom)
+ new_rep = Reputation_Bottom;
+
+ if(ReputationToRank(new_rep) <= REP_HOSTILE)
+ SetFactionAtWar(&itr->second,true);
+
+ itr->second.Standing = new_rep - BaseRep;
+ itr->second.Changed = true;
+
+ SetFactionVisible(&itr->second);
+
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ if(uint32 questid = GetQuestSlotQuestId(i))
+ {
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if( qInfo && qInfo->GetRepObjectiveFaction() == factionEntry->ID )
+ {
+ QuestStatusData& q_status = mQuestStatus[questid];
+ if( q_status.m_status == QUEST_STATUS_INCOMPLETE )
+ {
+ if(GetReputation(factionEntry) >= qInfo->GetRepObjectiveValue())
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+ }
+ else if( q_status.m_status == QUEST_STATUS_COMPLETE )
+ {
+ if(GetReputation(factionEntry) < qInfo->GetRepObjectiveValue())
+ IncompleteQuest( questid );
+ }
+ }
+ }
+ }
+
+ SendFactionState(&(itr->second));
+
+ return true;
+ }
+ return false;
+}
+
+bool Player::SetFactionReputation(uint32 FactionTemplateId, int32 standing)
+{
+ FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(FactionTemplateId);
+
+ if(!factionTemplateEntry)
+ {
+ sLog.outError("Player::SetFactionReputation: Can't set reputation of %s for unknown faction (faction template id) #%u.", GetName(), FactionTemplateId);
+ return false;
+ }
+
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction);
+
+ // Faction without recorded reputation. Just ignore.
+ if(!factionEntry)
+ return false;
+
+ return SetFactionReputation(factionEntry, standing);
+}
+
+bool Player::SetFactionReputation(FactionEntry const* factionEntry, int32 standing)
+{
+ SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID);
+ if (flist)
+ {
+ bool res = false;
+ for (SimpleFactionsList::const_iterator itr = flist->begin();itr != flist->end();++itr)
+ {
+ FactionEntry const *factionEntryCalc = sFactionStore.LookupEntry(*itr);
+ if(factionEntryCalc)
+ res = SetOneFactionReputation(factionEntryCalc, standing);
+ }
+ return res;
+ }
+ else
+ return SetOneFactionReputation(factionEntry, standing);
+}
+
+bool Player::SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing)
+{
+ FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
+ if (itr != m_factions.end())
+ {
+ if (standing > Reputation_Cap)
+ standing = Reputation_Cap;
+ else
+ if (standing < Reputation_Bottom)
+ standing = Reputation_Bottom;
+
+ int32 BaseRep = GetBaseReputation(factionEntry);
+ itr->second.Standing = standing - BaseRep;
+ itr->second.Changed = true;
+
+ SetFactionVisible(&itr->second);
+
+ if(ReputationToRank(standing) <= REP_HOSTILE)
+ SetFactionAtWar(&itr->second,true);
+
+ SendFactionState(&(itr->second));
+ return true;
+ }
+ return false;
+}
+
+//Calculate total reputation percent player gain with quest/creature level
+int32 Player::CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, bool for_quest)
+{
+ // for grey creature kill received 20%, in other case 100.
+ int32 percent = (!for_quest && (creatureOrQuestLevel <= MaNGOS::XP::GetGrayLevel(getLevel()))) ? 20 : 100;
+
+ int32 repMod = GetTotalAuraModifier(SPELL_AURA_MOD_REPUTATION_GAIN);
+
+ percent += rep > 0 ? repMod : -repMod;
+
+ if(percent <=0)
+ return 0;
+
+ return int32(sWorld.getRate(RATE_REPUTATION_GAIN)*rep*percent/100);
+}
+
+//Calculates how many reputation points player gains in victim's enemy factions
+void Player::RewardReputation(Unit *pVictim, float rate)
+{
+ if(!pVictim || pVictim->GetTypeId() == TYPEID_PLAYER)
+ return;
+
+ ReputationOnKillEntry const* Rep = objmgr.GetReputationOnKilEntry(((Creature*)pVictim)->GetCreatureInfo()->Entry);
+
+ if(!Rep)
+ return;
+
+ if(Rep->repfaction1 && (!Rep->team_dependent || GetTeam()==ALLIANCE))
+ {
+ int32 donerep1 = CalculateReputationGain(pVictim->getLevel(),Rep->repvalue1,false);
+ donerep1 = int32(donerep1*rate);
+ FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(Rep->repfaction1);
+ uint32 current_reputation_rank1 = GetReputationRank(factionEntry1);
+ if(factionEntry1 && current_reputation_rank1 <= Rep->reputation_max_cap1)
+ ModifyFactionReputation(factionEntry1, donerep1);
+
+ // Wiki: Team factions value divided by 2
+ if(Rep->is_teamaward1)
+ {
+ FactionEntry const *team1_factionEntry = sFactionStore.LookupEntry(factionEntry1->team);
+ if(team1_factionEntry)
+ ModifyFactionReputation(team1_factionEntry, donerep1 / 2);
+ }
+ }
+
+ if(Rep->repfaction2 && (!Rep->team_dependent || GetTeam()==HORDE))
+ {
+ int32 donerep2 = CalculateReputationGain(pVictim->getLevel(),Rep->repvalue2,false);
+ donerep2 = int32(donerep2*rate);
+ FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(Rep->repfaction2);
+ uint32 current_reputation_rank2 = GetReputationRank(factionEntry2);
+ if(factionEntry2 && current_reputation_rank2 <= Rep->reputation_max_cap2)
+ ModifyFactionReputation(factionEntry2, donerep2);
+
+ // Wiki: Team factions value divided by 2
+ if(Rep->is_teamaward2)
+ {
+ FactionEntry const *team2_factionEntry = sFactionStore.LookupEntry(factionEntry2->team);
+ if(team2_factionEntry)
+ ModifyFactionReputation(team2_factionEntry, donerep2 / 2);
+ }
+ }
+}
+
+//Calculate how many reputation points player gain with the quest
+void Player::RewardReputation(Quest const *pQuest)
+{
+ // quest reputation reward/loss
+ for(int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
+ {
+ if(pQuest->RewRepFaction[i] && pQuest->RewRepValue[i] )
+ {
+ int32 rep = CalculateReputationGain(pQuest->GetQuestLevel(),pQuest->RewRepValue[i],true);
+ FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->RewRepFaction[i]);
+ if(factionEntry)
+ ModifyFactionReputation(factionEntry, rep);
+ }
+ }
+
+ // TODO: implement reputation spillover
+}
+
+void Player::UpdateArenaFields(void)
+{
+ /* arena calcs go here */
+}
+
+void Player::UpdateHonorFields()
+{
+ /// called when rewarding honor and at each save
+ uint64 now = time(NULL);
+ uint64 today = uint64(time(NULL) / DAY) * DAY;
+
+ if(m_lastHonorUpdateTime < today)
+ {
+ uint64 yesterday = today - DAY;
+
+ uint16 kills_today = PAIR32_LOPART(GetUInt32Value(PLAYER_FIELD_KILLS));
+
+ // update yesterday's contribution
+ if(m_lastHonorUpdateTime >= yesterday )
+ {
+ SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION));
+
+ // this is the first update today, reset today's contribution
+ SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0);
+ SetUInt32Value(PLAYER_FIELD_KILLS, MAKE_PAIR32(0,kills_today));
+ }
+ else
+ {
+ // no honor/kills yesterday or today, reset
+ SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0);
+ SetUInt32Value(PLAYER_FIELD_KILLS, 0);
+ }
+ }
+
+ m_lastHonorUpdateTime = now;
+}
+
+///Calculate the amount of honor gained based on the victim
+///and the size of the group for which the honor is divided
+///An exact honor value can also be given (overriding the calcs)
+bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvptoken)
+{
+ // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
+ if(GetDummyAura(SPELL_AURA_PLAYER_INACTIVE))
+ return false;
+
+ uint64 victim_guid = 0;
+ uint32 victim_rank = 0;
+ time_t now = time(NULL);
+
+ // need call before fields update to have chance move yesterday data to appropriate fields before today data change.
+ UpdateHonorFields();
+
+ // do not reward honor in arenas, but return true to enable onkill spellproc
+ if(InBattleGround() && GetBattleGround() && GetBattleGround()->isArena())
+ return true;
+
+ if(honor <= 0)
+ {
+ if(!uVictim || uVictim == this || uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
+ return false;
+
+ victim_guid = uVictim->GetGUID();
+
+ if( uVictim->GetTypeId() == TYPEID_PLAYER )
+ {
+ Player *pVictim = (Player *)uVictim;
+
+ if( GetTeam() == pVictim->GetTeam() && !sWorld.IsFFAPvPRealm() )
+ return false;
+
+ float f = 1; //need for total kills (?? need more info)
+ uint32 k_grey = 0;
+ uint32 k_level = getLevel();
+ uint32 v_level = pVictim->getLevel();
+
+ {
+ // PLAYER_CHOSEN_TITLE VALUES DESCRIPTION
+ // [0] Just name
+ // [1..14] Alliance honor titles and player name
+ // [15..28] Horde honor titles and player name
+ // [29..38] Other title and player name
+ // [39+] Nothing
+ uint32 victim_title = pVictim->GetUInt32Value(PLAYER_CHOSEN_TITLE);
+ // Get Killer titles, CharTitlesEntry::bit_index
+ // Ranks:
+ // title[1..14] -> rank[5..18]
+ // title[15..28] -> rank[5..18]
+ // title[other] -> 0
+ if (victim_title == 0)
+ victim_guid = 0; // Don't show HK: <rank> message, only log.
+ else if (victim_title < 15)
+ victim_rank = victim_title + 4;
+ else if (victim_title < 29)
+ victim_rank = victim_title - 14 + 4;
+ else
+ victim_guid = 0; // Don't show HK: <rank> message, only log.
+ }
+
+ if(k_level <= 5)
+ k_grey = 0;
+ else if( k_level <= 39 )
+ k_grey = k_level - 5 - k_level/10;
+ else
+ k_grey = k_level - 1 - k_level/5;
+
+ if(v_level<=k_grey)
+ return false;
+
+ float diff_level = (k_level == k_grey) ? 1 : ((float(v_level) - float(k_grey)) / (float(k_level) - float(k_grey)));
+
+ int32 v_rank =1; //need more info
+
+ honor = ((f * diff_level * (190 + v_rank*10))/6);
+ honor *= ((float)k_level) / 70.0f; //factor of dependence on levels of the killer
+
+ // count the number of playerkills in one day
+ ApplyModUInt32Value(PLAYER_FIELD_KILLS, 1, true);
+ // and those in a lifetime
+ ApplyModUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS, 1, true);
+ }
+ else
+ {
+ Creature *cVictim = (Creature *)uVictim;
+
+ if (!cVictim->isRacialLeader())
+ return false;
+
+ honor = 100; // ??? need more info
+ victim_rank = 19; // HK: Leader
+ }
+ }
+
+ if (uVictim != NULL)
+ {
+ honor *= sWorld.getRate(RATE_HONOR);
+
+ if(groupsize > 1)
+ honor /= groupsize;
+
+ honor *= (((float)urand(8,12))/10); // approx honor: 80% - 120% of real honor
+ }
+
+ // honor - for show honor points in log
+ // victim_guid - for show victim name in log
+ // victim_rank [1..4] HK: <dishonored rank>
+ // victim_rank [5..19] HK: <alliance\horde rank>
+ // victim_rank [0,20+] HK: <>
+ WorldPacket data(SMSG_PVP_CREDIT,4+8+4);
+ data << (uint32) honor;
+ data << (uint64) victim_guid;
+ data << (uint32) victim_rank;
+
+ GetSession()->SendPacket(&data);
+
+ // add honor points
+ ModifyHonorPoints(int32(honor));
+
+ ApplyModUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, uint32(honor), true);
+
+ if( sWorld.getConfig(CONFIG_PVP_TOKEN_ENABLE) && pvptoken )
+ {
+ if(!uVictim || uVictim == this || uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
+ return true;
+
+ if(uVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Check if allowed to receive it in current map
+ uint8 MapType = sWorld.getConfig(CONFIG_PVP_TOKEN_MAP_TYPE);
+ if(MapType == 1 && !InBattleGround() && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) || MapType == 2 && !HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) || MapType == 3 && !InBattleGround())
+ return true;
+
+ uint32 noSpaceForCount = 0;
+ uint32 itemId = sWorld.getConfig(CONFIG_PVP_TOKEN_ID);
+ int32 count = sWorld.getConfig(CONFIG_PVP_TOKEN_COUNT);
+
+ // check space and find places
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount );
+ if( msg != EQUIP_ERR_OK ) // convert to possible store amount
+ count = noSpaceForCount;
+
+ if( count == 0 || dest.empty()) // can't add any
+ {
+ // -- TODO: Send to mailbox if no space
+ ChatHandler(this).PSendSysMessage("You don't have any space in your bags for a token.");
+ return true;
+ }
+
+ Item* item = StoreNewItem( dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
+ SendNewItem(item,count,true,false);
+ ChatHandler(this).PSendSysMessage("You have been awarded a token for slaying another player.");
+ }
+ }
+
+ return true;
+}
+
+void Player::ModifyHonorPoints( int32 value )
+{
+ if(value < 0)
+ {
+ if (GetHonorPoints() > sWorld.getConfig(CONFIG_MAX_HONOR_POINTS))
+ SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, sWorld.getConfig(CONFIG_MAX_HONOR_POINTS) + value);
+ else
+ SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, GetHonorPoints() > uint32(-value) ? GetHonorPoints() + value : 0);
+ }
+ else
+ SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, GetHonorPoints() < sWorld.getConfig(CONFIG_MAX_HONOR_POINTS) - value ? GetHonorPoints() + value : sWorld.getConfig(CONFIG_MAX_HONOR_POINTS));
+}
+
+void Player::ModifyArenaPoints( int32 value )
+{
+ if(value < 0)
+ {
+ if (GetArenaPoints() > sWorld.getConfig(CONFIG_MAX_ARENA_POINTS))
+ SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, sWorld.getConfig(CONFIG_MAX_ARENA_POINTS) + value);
+ else
+ SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, GetArenaPoints() > uint32(-value) ? GetArenaPoints() + value : 0);
+ }
+ else
+ SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, GetArenaPoints() < sWorld.getConfig(CONFIG_MAX_ARENA_POINTS) - value ? GetArenaPoints() + value : sWorld.getConfig(CONFIG_MAX_ARENA_POINTS));
+}
+
+uint32 Player::GetGuildIdFromDB(uint64 guid)
+{
+ std::ostringstream ss;
+ ss<<"SELECT guildid FROM guild_member WHERE guid='"<<guid<<"'";
+ QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
+ if( result )
+ {
+ uint32 v = result->Fetch()[0].GetUInt32();
+ delete result;
+ return v;
+ }
+ else
+ return 0;
+}
+
+uint32 Player::GetRankFromDB(uint64 guid)
+{
+ std::ostringstream ss;
+ ss<<"SELECT rank FROM guild_member WHERE guid='"<<guid<<"'";
+ QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
+ if( result )
+ {
+ uint32 v = result->Fetch()[0].GetUInt32();
+ delete result;
+ return v;
+ }
+ else
+ return 0;
+}
+
+uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type)
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT arenateamid FROM arena_team_member WHERE guid='%u'", GUID_LOPART(guid));
+ if(result)
+ {
+ bool found = false;
+ // init id to find the type of the arenateam
+ uint32 id = (*result)[0].GetUInt32();
+ do
+ {
+ QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM arena_team WHERE arenateamid='%u'", id);
+ if(result2)
+ {
+ uint8 dbtype = (*result2)[0].GetUInt32();
+ delete result2;
+ if(dbtype == type)
+ {
+ // if the type matches, we've found the id
+ found = true;
+ break;
+ }
+ }
+ } while(result->NextRow());
+ delete result;
+ if(found) return id;
+ }
+ // no arenateam for the specified guid, return 0
+ return 0;
+}
+
+uint32 Player::GetZoneIdFromDB(uint64 guid)
+{
+ std::ostringstream ss;
+
+ ss<<"SELECT zone FROM characters WHERE guid='"<<GUID_LOPART(guid)<<"'";
+ QueryResult *result = CharacterDatabase.Query( ss.str().c_str() );
+ if (!result)
+ return 0;
+ Field* fields = result->Fetch();
+ uint32 zone = fields[0].GetUInt32();
+ delete result;
+
+ if (!zone)
+ {
+ // stored zone is zero, use generic and slow zone detection
+ ss.str("");
+ ss<<"SELECT map,position_x,position_y FROM characters WHERE guid='"<<GUID_LOPART(guid)<<"'";
+ result = CharacterDatabase.Query(ss.str().c_str());
+ if( !result )
+ return 0;
+ fields = result->Fetch();
+ uint32 map = fields[0].GetUInt32();
+ float posx = fields[1].GetFloat();
+ float posy = fields[2].GetFloat();
+ delete result;
+
+ zone = MapManager::Instance().GetZoneId(map,posx,posy);
+
+ ss.str("");
+ ss << "UPDATE characters SET zone='"<<zone<<"' WHERE guid='"<<GUID_LOPART(guid)<<"'";
+ CharacterDatabase.Execute(ss.str().c_str());
+ }
+
+ return zone;
+}
+
+void Player::UpdateArea(uint32 newArea)
+{
+ // FFA_PVP flags are area and not zone id dependent
+ // so apply them accordingly
+ m_areaUpdateId = newArea;
+
+ AreaTableEntry const* area = GetAreaEntryByAreaID(newArea);
+
+ if(area && (area->flags & AREA_FLAG_ARENA))
+ {
+ if(!isGameMaster())
+ SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
+ }
+ else
+ {
+ // remove ffa flag only if not ffapvp realm
+ // removal in sanctuaries and capitals is handled in zone update
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && !sWorld.IsFFAPvPRealm())
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP);
+ }
+
+ UpdateAreaDependentAuras(newArea);
+}
+
+void Player::UpdateZone(uint32 newZone)
+{
+ m_zoneUpdateId = newZone;
+ m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
+
+ // zone changed, so area changed as well, update it
+ UpdateArea(GetAreaId());
+
+ AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone);
+ if(!zone)
+ return;
+
+ if (sWorld.getConfig(CONFIG_WEATHER))
+ {
+ Weather *wth = sWorld.FindWeather(zone->ID);
+ if(wth)
+ {
+ wth->SendWeatherUpdateToPlayer(this);
+ }
+ else
+ {
+ if(!sWorld.AddWeather(zone->ID))
+ {
+ // send fine weather packet to remove old zone's weather
+ Weather::SendFineWeatherUpdateToPlayer(this);
+ }
+ }
+ }
+
+ pvpInfo.inHostileArea =
+ GetTeam() == ALLIANCE && zone->team == AREATEAM_HORDE ||
+ GetTeam() == HORDE && zone->team == AREATEAM_ALLY ||
+ sWorld.IsPvPRealm() && zone->team == AREATEAM_NONE ||
+ InBattleGround(); // overwrite for battlegrounds, maybe batter some zone flags but current known not 100% fit to this
+
+ if(pvpInfo.inHostileArea) // in hostile area
+ {
+ if(!IsPvP() || pvpInfo.endTimer != 0)
+ UpdatePvP(true, true);
+ }
+ else // in friendly area
+ {
+ if(IsPvP() && !HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_IN_PVP) && pvpInfo.endTimer == 0)
+ pvpInfo.endTimer = time(0); // start toggle-off
+ }
+
+ if(zone->flags & AREA_FLAG_SANCTUARY) // in sanctuary
+ {
+ SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY);
+ if(sWorld.IsFFAPvPRealm())
+ RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+ }
+ else
+ {
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY);
+ }
+
+ if(zone->flags & AREA_FLAG_CAPITAL) // in capital city
+ {
+ SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
+ SetRestType(REST_TYPE_IN_CITY);
+ InnEnter(time(0),GetMapId(),0,0,0);
+
+ if(sWorld.IsFFAPvPRealm())
+ RemoveFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+ }
+ else // anywhere else
+ {
+ if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) // but resting (walk from city or maybe in tavern or leave tavern recently)
+ {
+ if(GetRestType()==REST_TYPE_IN_TAVERN) // has been in tavern. Is still in?
+ {
+ if(GetMapId()!=GetInnPosMapId() || sqrt((GetPositionX()-GetInnPosX())*(GetPositionX()-GetInnPosX())+(GetPositionY()-GetInnPosY())*(GetPositionY()-GetInnPosY())+(GetPositionZ()-GetInnPosZ())*(GetPositionZ()-GetInnPosZ()))>40)
+ {
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
+ SetRestType(REST_TYPE_NO);
+
+ if(sWorld.IsFFAPvPRealm())
+ SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+ }
+ }
+ else // not in tavern (leave city then)
+ {
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
+ SetRestType(REST_TYPE_NO);
+
+ // Set player to FFA PVP when not in rested enviroment.
+ if(sWorld.IsFFAPvPRealm())
+ SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP);
+ }
+ }
+ }
+
+ // remove items with area/map limitations (delete only for alive player to allow back in ghost mode)
+ // if player resurrected at teleport this will be applied in resurrect code
+ if(isAlive())
+ DestroyZoneLimitedItem( true, newZone );
+
+ // recent client version not send leave/join channel packets for built-in local channels
+ UpdateLocalChannels( newZone );
+
+ // group update
+ if(GetGroup())
+ SetGroupUpdateFlag(GROUP_UPDATE_FLAG_ZONE);
+
+ UpdateZoneDependentAuras(newZone);
+}
+
+//If players are too far way of duel flag... then player loose the duel
+void Player::CheckDuelDistance(time_t currTime)
+{
+ if(!duel)
+ return;
+
+ uint64 duelFlagGUID = GetUInt64Value(PLAYER_DUEL_ARBITER);
+ GameObject* obj = ObjectAccessor::GetGameObject(*this, duelFlagGUID);
+ if(!obj)
+ return;
+
+ if(duel->outOfBound == 0)
+ {
+ if(!IsWithinDistInMap(obj, 50))
+ {
+ duel->outOfBound = currTime;
+
+ WorldPacket data(SMSG_DUEL_OUTOFBOUNDS, 0);
+ GetSession()->SendPacket(&data);
+ }
+ }
+ else
+ {
+ if(IsWithinDistInMap(obj, 40))
+ {
+ duel->outOfBound = 0;
+
+ WorldPacket data(SMSG_DUEL_INBOUNDS, 0);
+ GetSession()->SendPacket(&data);
+ }
+ else if(currTime >= (duel->outOfBound+10))
+ {
+ DuelComplete(DUEL_FLED);
+ }
+ }
+}
+
+void Player::DuelComplete(DuelCompleteType type)
+{
+ // duel not requested
+ if(!duel)
+ return;
+
+ WorldPacket data(SMSG_DUEL_COMPLETE, (1));
+ data << (uint8)((type != DUEL_INTERUPTED) ? 1 : 0);
+ GetSession()->SendPacket(&data);
+ duel->opponent->GetSession()->SendPacket(&data);
+
+ if(type != DUEL_INTERUPTED)
+ {
+ data.Initialize(SMSG_DUEL_WINNER, (1+20)); // we guess size
+ data << (uint8)((type==DUEL_WON) ? 0 : 1); // 0 = just won; 1 = fled
+ data << duel->opponent->GetName();
+ data << GetName();
+ SendMessageToSet(&data,true);
+ }
+
+ // cool-down duel spell
+ /*data.Initialize(SMSG_SPELL_COOLDOWN, 17);
+
+ data<<GetGUID();
+ data<<uint8(0x0);
+
+ data<<(uint32)7266;
+ data<<uint32(0x0);
+ GetSession()->SendPacket(&data);
+ data.Initialize(SMSG_SPELL_COOLDOWN, 17);
+ data<<duel->opponent->GetGUID();
+ data<<uint8(0x0);
+ data<<(uint32)7266;
+ data<<uint32(0x0);
+ duel->opponent->GetSession()->SendPacket(&data);*/
+
+ //Remove Duel Flag object
+ GameObject* obj = ObjectAccessor::GetGameObject(*this, GetUInt64Value(PLAYER_DUEL_ARBITER));
+ if(obj)
+ duel->initiator->RemoveGameObject(obj,true);
+
+ /* remove auras */
+ std::vector<uint32> auras2remove;
+ AuraMap const& vAuras = duel->opponent->GetAuras();
+ for (AuraMap::const_iterator i = vAuras.begin(); i != vAuras.end(); i++)
+ {
+ if (!i->second->IsPositive() && i->second->GetCasterGUID() == GetGUID() && i->second->GetAuraApplyTime() >= duel->startTime)
+ auras2remove.push_back(i->second->GetId());
+ }
+
+ for(size_t i=0; i<auras2remove.size(); i++)
+ duel->opponent->RemoveAurasDueToSpell(auras2remove[i]);
+
+ auras2remove.clear();
+ AuraMap const& auras = GetAuras();
+ for (AuraMap::const_iterator i = auras.begin(); i != auras.end(); i++)
+ {
+ if (!i->second->IsPositive() && i->second->GetCasterGUID() == duel->opponent->GetGUID() && i->second->GetAuraApplyTime() >= duel->startTime)
+ auras2remove.push_back(i->second->GetId());
+ }
+ for(size_t i=0; i<auras2remove.size(); i++)
+ RemoveAurasDueToSpell(auras2remove[i]);
+
+ // cleanup combo points
+ if(GetComboTarget()==duel->opponent->GetGUID())
+ ClearComboPoints();
+ else if(GetComboTarget()==duel->opponent->GetPetGUID())
+ ClearComboPoints();
+
+ if(duel->opponent->GetComboTarget()==GetGUID())
+ duel->opponent->ClearComboPoints();
+ else if(duel->opponent->GetComboTarget()==GetPetGUID())
+ duel->opponent->ClearComboPoints();
+
+ // Honor points after duel (the winner) - ImpConfig
+ if(sWorld.getConfig(CONFIG_HONOR_AFTER_DUEL > 0))
+ {
+ uint32 amount = sWorld.getConfig(CONFIG_HONOR_AFTER_DUEL);
+ duel->opponent->RewardHonor(NULL,1,amount);
+ }
+
+ //cleanups
+ SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
+ SetUInt32Value(PLAYER_DUEL_TEAM, 0);
+ duel->opponent->SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
+ duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0);
+
+ delete duel->opponent->duel;
+ duel->opponent->duel = NULL;
+ delete duel;
+ duel = NULL;
+}
+
+//---------------------------------------------------------//
+
+void Player::_ApplyItemMods(Item *item, uint8 slot,bool apply)
+{
+ if(slot >= INVENTORY_SLOT_BAG_END || !item)
+ return;
+
+ // not apply/remove mods for broken item
+ if(item->IsBroken())
+ return;
+
+ ItemPrototype const *proto = item->GetProto();
+
+ if(!proto)
+ return;
+
+ sLog.outDetail("applying mods for item %u ",item->GetGUIDLow());
+
+ uint32 attacktype = Player::GetAttackBySlot(slot);
+ if(attacktype < MAX_ATTACK)
+ _ApplyWeaponDependentAuraMods(item,WeaponAttackType(attacktype),apply);
+
+ _ApplyItemBonuses(proto,slot,apply);
+
+ if( slot==EQUIPMENT_SLOT_RANGED )
+ _ApplyAmmoBonuses();
+
+ ApplyItemEquipSpell(item,apply);
+ ApplyEnchantment(item, apply);
+
+ if(proto->Socket[0].Color) //only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items
+ CorrectMetaGemEnchants(slot, apply);
+
+ sLog.outDebug("_ApplyItemMods complete.");
+}
+
+void Player::_ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply)
+{
+ if(slot >= INVENTORY_SLOT_BAG_END || !proto)
+ return;
+
+ for (int i = 0; i < 10; i++)
+ {
+ float val = float (proto->ItemStat[i].ItemStatValue);
+
+ if(val==0)
+ continue;
+
+ switch (proto->ItemStat[i].ItemStatType)
+ {
+ case ITEM_MOD_MANA:
+ HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply);
+ break;
+ case ITEM_MOD_HEALTH: // modify HP
+ HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply);
+ break;
+ case ITEM_MOD_AGILITY: // modify agility
+ HandleStatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply);
+ ApplyStatBuffMod(STAT_AGILITY, val, apply);
+ break;
+ case ITEM_MOD_STRENGTH: //modify strength
+ HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply);
+ ApplyStatBuffMod(STAT_STRENGTH, val, apply);
+ break;
+ case ITEM_MOD_INTELLECT: //modify intellect
+ HandleStatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply);
+ ApplyStatBuffMod(STAT_INTELLECT, val, apply);
+ break;
+ case ITEM_MOD_SPIRIT: //modify spirit
+ HandleStatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply);
+ ApplyStatBuffMod(STAT_SPIRIT, val, apply);
+ break;
+ case ITEM_MOD_STAMINA: //modify stamina
+ HandleStatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply);
+ ApplyStatBuffMod(STAT_STAMINA, val, apply);
+ break;
+ case ITEM_MOD_DEFENSE_SKILL_RATING:
+ ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply);
+ break;
+ case ITEM_MOD_DODGE_RATING:
+ ApplyRatingMod(CR_DODGE, int32(val), apply);
+ break;
+ case ITEM_MOD_PARRY_RATING:
+ ApplyRatingMod(CR_PARRY, int32(val), apply);
+ break;
+ case ITEM_MOD_BLOCK_RATING:
+ ApplyRatingMod(CR_BLOCK, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_MELEE_RATING:
+ ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_RANGED_RATING:
+ ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_SPELL_RATING:
+ ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_MELEE_RATING:
+ ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_RANGED_RATING:
+ ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_SPELL_RATING:
+ ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
+ ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
+ ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
+ ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
+ ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
+ ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
+ ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_HASTE_MELEE_RATING:
+ ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
+ break;
+ case ITEM_MOD_HASTE_RANGED_RATING:
+ ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
+ break;
+ case ITEM_MOD_HASTE_SPELL_RATING:
+ ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_RATING:
+ ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_RATING:
+ ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_HIT_TAKEN_RATING:
+ ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_CRIT_TAKEN_RATING:
+ ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_RESILIENCE_RATING:
+ ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_HASTE_RATING:
+ ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
+ ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
+ ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
+ break;
+ case ITEM_MOD_EXPERTISE_RATING:
+ ApplyRatingMod(CR_EXPERTISE, int32(val), apply);
+ break;
+ }
+ }
+
+ if (proto->Armor)
+ HandleStatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(proto->Armor), apply);
+
+ if (proto->Block)
+ HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(proto->Block), apply);
+
+ if (proto->HolyRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(proto->HolyRes), apply);
+
+ if (proto->FireRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_FIRE, BASE_VALUE, float(proto->FireRes), apply);
+
+ if (proto->NatureRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_NATURE, BASE_VALUE, float(proto->NatureRes), apply);
+
+ if (proto->FrostRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_FROST, BASE_VALUE, float(proto->FrostRes), apply);
+
+ if (proto->ShadowRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_SHADOW, BASE_VALUE, float(proto->ShadowRes), apply);
+
+ if (proto->ArcaneRes)
+ HandleStatModifier(UNIT_MOD_RESISTANCE_ARCANE, BASE_VALUE, float(proto->ArcaneRes), apply);
+
+ WeaponAttackType attType = BASE_ATTACK;
+ float damage = 0.0f;
+
+ if( slot == EQUIPMENT_SLOT_RANGED && (
+ proto->InventoryType == INVTYPE_RANGED || proto->InventoryType == INVTYPE_THROWN ||
+ proto->InventoryType == INVTYPE_RANGEDRIGHT ))
+ {
+ attType = RANGED_ATTACK;
+ }
+ else if(slot==EQUIPMENT_SLOT_OFFHAND)
+ {
+ attType = OFF_ATTACK;
+ }
+
+ if (proto->Damage[0].DamageMin > 0 )
+ {
+ damage = apply ? proto->Damage[0].DamageMin : BASE_MINDAMAGE;
+ SetBaseWeaponDamage(attType, MINDAMAGE, damage);
+ //sLog.outError("applying mindam: assigning %f to weapon mindamage, now is: %f", damage, GetWeaponDamageRange(attType, MINDAMAGE));
+ }
+
+ if (proto->Damage[0].DamageMax > 0 )
+ {
+ damage = apply ? proto->Damage[0].DamageMax : BASE_MAXDAMAGE;
+ SetBaseWeaponDamage(attType, MAXDAMAGE, damage);
+ }
+
+ if(!IsUseEquipedWeapon(slot==EQUIPMENT_SLOT_MAINHAND))
+ return;
+
+ if (proto->Delay)
+ {
+ if(slot == EQUIPMENT_SLOT_RANGED)
+ SetAttackTime(RANGED_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
+ else if(slot==EQUIPMENT_SLOT_MAINHAND)
+ SetAttackTime(BASE_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
+ else if(slot==EQUIPMENT_SLOT_OFFHAND)
+ SetAttackTime(OFF_ATTACK, apply ? proto->Delay: BASE_ATTACK_TIME);
+ }
+
+ if(CanModifyStats() && (damage || proto->Delay))
+ UpdateDamagePhysical(attType);
+}
+
+void Player::_ApplyWeaponDependentAuraMods(Item *item,WeaponAttackType attackType,bool apply)
+{
+ AuraList const& auraCritList = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT);
+ for(AuraList::const_iterator itr = auraCritList.begin(); itr!=auraCritList.end();++itr)
+ _ApplyWeaponDependentAuraCritMod(item,attackType,*itr,apply);
+
+ AuraList const& auraDamageFlatList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE);
+ for(AuraList::const_iterator itr = auraDamageFlatList.begin(); itr!=auraDamageFlatList.end();++itr)
+ _ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply);
+
+ AuraList const& auraDamagePCTList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
+ for(AuraList::const_iterator itr = auraDamagePCTList.begin(); itr!=auraDamagePCTList.end();++itr)
+ _ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply);
+}
+
+void Player::_ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply)
+{
+ // generic not weapon specific case processes in aura code
+ if(aura->GetSpellProto()->EquippedItemClass == -1)
+ return;
+
+ BaseModGroup mod = BASEMOD_END;
+ switch(attackType)
+ {
+ case BASE_ATTACK: mod = CRIT_PERCENTAGE; break;
+ case OFF_ATTACK: mod = OFFHAND_CRIT_PERCENTAGE;break;
+ case RANGED_ATTACK: mod = RANGED_CRIT_PERCENTAGE; break;
+ default: return;
+ }
+
+ if (item->IsFitToSpellRequirements(aura->GetSpellProto()))
+ {
+ HandleBaseModValue(mod, FLAT_MOD, float (aura->GetModifier()->m_amount), apply);
+ }
+}
+
+void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply)
+{
+ // ignore spell mods for not wands
+ Modifier const* modifier = aura->GetModifier();
+ if((modifier->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)==0 && (getClassMask() & CLASSMASK_WAND_USERS)==0)
+ return;
+
+ // generic not weapon specific case processes in aura code
+ if(aura->GetSpellProto()->EquippedItemClass == -1)
+ return;
+
+ UnitMods unitMod = UNIT_MOD_END;
+ switch(attackType)
+ {
+ case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break;
+ case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break;
+ case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break;
+ default: return;
+ }
+
+ UnitModifierType unitModType = TOTAL_VALUE;
+ switch(modifier->m_auraname)
+ {
+ case SPELL_AURA_MOD_DAMAGE_DONE: unitModType = TOTAL_VALUE; break;
+ case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: unitModType = TOTAL_PCT; break;
+ default: return;
+ }
+
+ if (item->IsFitToSpellRequirements(aura->GetSpellProto()))
+ {
+ HandleStatModifier(unitMod, unitModType, float(modifier->m_amount),apply);
+ }
+}
+
+void Player::ApplyItemEquipSpell(Item *item, bool apply, bool form_change)
+{
+ if(!item)
+ return;
+
+ ItemPrototype const *proto = item->GetProto();
+ if(!proto)
+ return;
+
+ for (int i = 0; i < 5; i++)
+ {
+ _Spell const& spellData = proto->Spells[i];
+
+ // no spell
+ if(!spellData.SpellId )
+ continue;
+
+ // wrong triggering type
+ if(apply && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP)
+ continue;
+
+ // check if it is valid spell
+ SpellEntry const* spellproto = sSpellStore.LookupEntry(spellData.SpellId);
+ if(!spellproto)
+ continue;
+
+ ApplyEquipSpell(spellproto,item,apply,form_change);
+ }
+}
+
+void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply, bool form_change)
+{
+ if(apply)
+ {
+ // Cannot be used in this stance/form
+ if(GetErrorAtShapeshiftedCast(spellInfo, m_form)!=0)
+ return;
+
+ if(form_change) // check aura active state from other form
+ {
+ bool found = false;
+ for (int k=0; k < 3; ++k)
+ {
+ spellEffectPair spair = spellEffectPair(spellInfo->Id, k);
+ for (AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair); ++iter)
+ {
+ if(!item || iter->second->GetCastItemGUID() == item->GetGUID())
+ {
+ found = true;
+ break;
+ }
+ }
+ if(found)
+ break;
+ }
+
+ if(found) // and skip re-cast already active aura at form change
+ return;
+ }
+
+ DEBUG_LOG("WORLD: cast %s Equip spellId - %i", (item ? "item" : "itemset"), spellInfo->Id);
+
+ CastSpell(this,spellInfo,true,item);
+ }
+ else
+ {
+ if(form_change) // check aura compatibility
+ {
+ // Cannot be used in this stance/form
+ if(GetErrorAtShapeshiftedCast(spellInfo, m_form)==0)
+ return; // and remove only not compatible at form change
+ }
+
+ if(item)
+ RemoveAurasDueToItemSpell(item,spellInfo->Id); // un-apply all spells , not only at-equipped
+ else
+ RemoveAurasDueToSpell(spellInfo->Id); // un-apply spell (item set case)
+ }
+}
+
+void Player::UpdateEquipSpellsAtFormChange()
+{
+ for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(m_items[i] && !m_items[i]->IsBroken())
+ {
+ ApplyItemEquipSpell(m_items[i],false,true); // remove spells that not fit to form
+ ApplyItemEquipSpell(m_items[i],true,true); // add spells that fit form but not active
+ }
+ }
+
+ // item set bonuses not dependent from item broken state
+ for(size_t setindex = 0; setindex < ItemSetEff.size(); ++setindex)
+ {
+ ItemSetEffect* eff = ItemSetEff[setindex];
+ if(!eff)
+ continue;
+
+ for(uint32 y=0;y<8; ++y)
+ {
+ SpellEntry const* spellInfo = eff->spells[y];
+ if(!spellInfo)
+ continue;
+
+ ApplyEquipSpell(spellInfo,NULL,false,true); // remove spells that not fit to form
+ ApplyEquipSpell(spellInfo,NULL,true,true); // add spells that fit form but not active
+ }
+ }
+}
+
+void Player::CastItemCombatSpell(Item *item,Unit* Target, WeaponAttackType attType)
+{
+ if(!item || item->IsBroken())
+ return;
+
+ ItemPrototype const *proto = item->GetProto();
+ if(!proto)
+ return;
+
+ if (!Target || Target == this )
+ return;
+
+ for (int i = 0; i < 5; i++)
+ {
+ _Spell const& spellData = proto->Spells[i];
+
+ // no spell
+ if(!spellData.SpellId )
+ continue;
+
+ // wrong triggering type
+ if(spellData.SpellTrigger != ITEM_SPELLTRIGGER_CHANCE_ON_HIT)
+ continue;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId);
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown Item spellid %i", spellData.SpellId);
+ continue;
+ }
+
+ // not allow proc extra attack spell at extra attack
+ if( m_extraAttacks && IsSpellHaveEffect(spellInfo,SPELL_EFFECT_ADD_EXTRA_ATTACKS) )
+ return;
+
+ float chance = spellInfo->procChance;
+
+ if(spellData.SpellPPMRate)
+ {
+ uint32 WeaponSpeed = GetAttackTime(attType);
+ chance = GetPPMProcChance(WeaponSpeed, spellData.SpellPPMRate);
+ }
+ else if(chance > 100.0f)
+ {
+ chance = GetWeaponProcChance();
+ }
+
+ if (roll_chance_f(chance))
+ this->CastSpell(Target, spellInfo->Id, true, item);
+ }
+
+ // item combat enchantments
+ for(int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot)
+ {
+ uint32 enchant_id = item->GetEnchantmentId(EnchantmentSlot(e_slot));
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant) continue;
+ for (int s=0;s<3;s++)
+ {
+ if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
+ continue;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(pEnchant->spellid[s]);
+ if (!spellInfo)
+ {
+ sLog.outError("Player::CastItemCombatSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]);
+ continue;
+ }
+
+ float chance = pEnchant->amount[s] != 0 ? float(pEnchant->amount[s]) : GetWeaponProcChance();
+ if (roll_chance_f(chance))
+ {
+ if(IsPositiveSpell(pEnchant->spellid[s]))
+ CastSpell(this, pEnchant->spellid[s], true, item);
+ else
+ CastSpell(Target, pEnchant->spellid[s], true, item);
+ }
+ }
+ }
+}
+
+void Player::_RemoveAllItemMods()
+{
+ sLog.outDebug("_RemoveAllItemMods start.");
+
+ for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(m_items[i])
+ {
+ ItemPrototype const *proto = m_items[i]->GetProto();
+ if(!proto)
+ continue;
+
+ // item set bonuses not dependent from item broken state
+ if(proto->ItemSet)
+ RemoveItemsSetItem(this,proto);
+
+ if(m_items[i]->IsBroken())
+ continue;
+
+ ApplyItemEquipSpell(m_items[i],false);
+ ApplyEnchantment(m_items[i], false);
+ }
+ }
+
+ for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(m_items[i])
+ {
+ if(m_items[i]->IsBroken())
+ continue;
+ ItemPrototype const *proto = m_items[i]->GetProto();
+ if(!proto)
+ continue;
+
+ uint32 attacktype = Player::GetAttackBySlot(i);
+ if(attacktype < MAX_ATTACK)
+ _ApplyWeaponDependentAuraMods(m_items[i],WeaponAttackType(attacktype),false);
+
+ _ApplyItemBonuses(proto,i, false);
+
+ if( i == EQUIPMENT_SLOT_RANGED )
+ _ApplyAmmoBonuses();
+ }
+ }
+
+ sLog.outDebug("_RemoveAllItemMods complete.");
+}
+
+void Player::_ApplyAllItemMods()
+{
+ sLog.outDebug("_ApplyAllItemMods start.");
+
+ for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(m_items[i])
+ {
+ if(m_items[i]->IsBroken())
+ continue;
+
+ ItemPrototype const *proto = m_items[i]->GetProto();
+ if(!proto)
+ continue;
+
+ uint32 attacktype = Player::GetAttackBySlot(i);
+ if(attacktype < MAX_ATTACK)
+ _ApplyWeaponDependentAuraMods(m_items[i],WeaponAttackType(attacktype),true);
+
+ _ApplyItemBonuses(proto,i, true);
+
+ if( i == EQUIPMENT_SLOT_RANGED )
+ _ApplyAmmoBonuses();
+ }
+ }
+
+ for (int i = 0; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(m_items[i])
+ {
+ ItemPrototype const *proto = m_items[i]->GetProto();
+ if(!proto)
+ continue;
+
+ // item set bonuses not dependent from item broken state
+ if(proto->ItemSet)
+ AddItemsSetItem(this,m_items[i]);
+
+ if(m_items[i]->IsBroken())
+ continue;
+
+ ApplyItemEquipSpell(m_items[i],true);
+ ApplyEnchantment(m_items[i], true);
+ }
+ }
+
+ sLog.outDebug("_ApplyAllItemMods complete.");
+}
+
+void Player::_ApplyAmmoBonuses()
+{
+ // check ammo
+ uint32 ammo_id = GetUInt32Value(PLAYER_AMMO_ID);
+ if(!ammo_id)
+ return;
+
+ float currentAmmoDPS;
+
+ ItemPrototype const *ammo_proto = objmgr.GetItemPrototype( ammo_id );
+ if( !ammo_proto || ammo_proto->Class!=ITEM_CLASS_PROJECTILE || !CheckAmmoCompatibility(ammo_proto))
+ currentAmmoDPS = 0.0f;
+ else
+ currentAmmoDPS = ammo_proto->Damage[0].DamageMin;
+
+ if(currentAmmoDPS == GetAmmoDPS())
+ return;
+
+ m_ammoDPS = currentAmmoDPS;
+
+ if(CanModifyStats())
+ UpdateDamagePhysical(RANGED_ATTACK);
+}
+
+bool Player::CheckAmmoCompatibility(const ItemPrototype *ammo_proto) const
+{
+ if(!ammo_proto)
+ return false;
+
+ // check ranged weapon
+ Item *weapon = GetWeaponForAttack( RANGED_ATTACK );
+ if(!weapon || weapon->IsBroken() )
+ return false;
+
+ ItemPrototype const* weapon_proto = weapon->GetProto();
+ if(!weapon_proto || weapon_proto->Class!=ITEM_CLASS_WEAPON )
+ return false;
+
+ // check ammo ws. weapon compatibility
+ switch(weapon_proto->SubClass)
+ {
+ case ITEM_SUBCLASS_WEAPON_BOW:
+ case ITEM_SUBCLASS_WEAPON_CROSSBOW:
+ if(ammo_proto->SubClass!=ITEM_SUBCLASS_ARROW)
+ return false;
+ break;
+ case ITEM_SUBCLASS_WEAPON_GUN:
+ if(ammo_proto->SubClass!=ITEM_SUBCLASS_BULLET)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/* If in a battleground a player dies, and an enemy removes the insignia, the player's bones is lootable
+ Called by remove insignia spell effect */
+void Player::RemovedInsignia(Player* looterPlr)
+{
+ if (!GetBattleGroundId())
+ return;
+
+ // If not released spirit, do it !
+ if(m_deathTimer > 0)
+ {
+ m_deathTimer = 0;
+ BuildPlayerRepop();
+ RepopAtGraveyard();
+ }
+
+ Corpse *corpse = GetCorpse();
+ if (!corpse)
+ return;
+
+ // We have to convert player corpse to bones, not to be able to resurrect there
+ // SpawnCorpseBones isn't handy, 'cos it saves player while he in BG
+ Corpse *bones = ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID());
+ if (!bones)
+ return;
+
+ // Now we must make bones lootable, and send player loot
+ bones->SetFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE);
+
+ // We store the level of our player in the gold field
+ // We retrieve this information at Player::SendLoot()
+ bones->loot.gold = getLevel();
+ bones->lootRecipient = looterPlr;
+ looterPlr->SendLoot(bones->GetGUID(), LOOT_INSIGNIA);
+}
+
+/*Loot type MUST be
+1-corpse, go
+2-skinning
+3-Fishing
+*/
+
+void Player::SendLootRelease( uint64 guid )
+{
+ WorldPacket data( SMSG_LOOT_RELEASE_RESPONSE, (8+1) );
+ data << uint64(guid) << uint8(1);
+ SendDirectMessage( &data );
+}
+
+void Player::SendLoot(uint64 guid, LootType loot_type)
+{
+ Loot *loot = 0;
+ PermissionTypes permission = ALL_PERMISSION;
+
+ sLog.outDebug("Player::SendLoot");
+ if (IS_GAMEOBJECT_GUID(guid))
+ {
+ sLog.outDebug(" IS_GAMEOBJECT_GUID(guid)");
+ GameObject *go =
+ ObjectAccessor::GetGameObject(*this, guid);
+
+ // not check distance for GO in case owned GO (fishing bobber case, for example)
+ // And permit out of range GO with no owner in case fishing hole
+ if (!go || (loot_type != LOOT_FISHINGHOLE && (loot_type != LOOT_FISHING || go->GetOwnerGUID() != GetGUID()) && !go->IsWithinDistInMap(this,INTERACTION_DISTANCE)))
+ {
+ SendLootRelease(guid);
+ return;
+ }
+
+ loot = &go->loot;
+
+ if(go->getLootState() == GO_READY)
+ {
+ uint32 lootid = go->GetLootId();
+
+ if(lootid)
+ {
+ sLog.outDebug(" if(lootid)");
+ loot->clear();
+ loot->FillLoot(lootid, LootTemplates_Gameobject, this);
+ }
+
+ if(loot_type == LOOT_FISHING)
+ go->getFishLoot(loot);
+
+ go->SetLootState(GO_ACTIVATED);
+ }
+ }
+ else if (IS_ITEM_GUID(guid))
+ {
+ Item *item = GetItemByGuid( guid );
+
+ if (!item)
+ {
+ SendLootRelease(guid);
+ return;
+ }
+
+ if(loot_type == LOOT_DISENCHANTING)
+ {
+ loot = &item->loot;
+
+ if(!item->m_lootGenerated)
+ {
+ item->m_lootGenerated = true;
+ loot->clear();
+ loot->FillLoot(item->GetProto()->DisenchantID, LootTemplates_Disenchant, this);
+ }
+ }
+ else if(loot_type == LOOT_PROSPECTING)
+ {
+ loot = &item->loot;
+
+ if(!item->m_lootGenerated)
+ {
+ item->m_lootGenerated = true;
+ loot->clear();
+ loot->FillLoot(item->GetEntry(), LootTemplates_Prospecting, this);
+ }
+ }
+ else
+ {
+ loot = &item->loot;
+
+ if(!item->m_lootGenerated)
+ {
+ item->m_lootGenerated = true;
+ loot->clear();
+ loot->FillLoot(item->GetEntry(), LootTemplates_Item, this);
+
+ loot->generateMoneyLoot(item->GetProto()->MinMoneyLoot,item->GetProto()->MaxMoneyLoot);
+ }
+ }
+ }
+ else if (IS_CORPSE_GUID(guid)) // remove insignia
+ {
+ Corpse *bones = ObjectAccessor::GetCorpse(*this, guid);
+
+ if (!bones || !((loot_type == LOOT_CORPSE) || (loot_type == LOOT_INSIGNIA)) || (bones->GetType() != CORPSE_BONES) )
+ {
+ SendLootRelease(guid);
+ return;
+ }
+
+ loot = &bones->loot;
+
+ if (!bones->lootForBody)
+ {
+ bones->lootForBody = true;
+ uint32 pLevel = bones->loot.gold;
+ bones->loot.clear();
+ // It may need a better formula
+ // Now it works like this: lvl10: ~6copper, lvl70: ~9silver
+ bones->loot.gold = (uint32)( urand(50, 150) * 0.016f * pow( ((float)pLevel)/5.76f, 2.5f) * sWorld.getRate(RATE_DROP_MONEY) );
+ }
+
+ if (bones->lootRecipient != this)
+ permission = NONE_PERMISSION;
+ }
+ else
+ {
+ Creature *creature = ObjectAccessor::GetCreature(*this, guid);
+
+ // must be in range and creature must be alive for pickpocket and must be dead for another loot
+ if (!creature || creature->isAlive()!=(loot_type == LOOT_PICKPOCKETING) || !creature->IsWithinDistInMap(this,INTERACTION_DISTANCE))
+ {
+ SendLootRelease(guid);
+ return;
+ }
+
+ if(loot_type == LOOT_PICKPOCKETING && IsFriendlyTo(creature))
+ {
+ SendLootRelease(guid);
+ return;
+ }
+
+ loot = &creature->loot;
+
+ if(loot_type == LOOT_PICKPOCKETING)
+ {
+ if ( !creature->lootForPickPocketed )
+ {
+ creature->lootForPickPocketed = true;
+ loot->clear();
+
+ if (uint32 lootid = creature->GetCreatureInfo()->pickpocketLootId)
+ loot->FillLoot(lootid, LootTemplates_Pickpocketing, this);
+
+ // Generate extra money for pick pocket loot
+ const uint32 a = urand(0, creature->getLevel()/2);
+ const uint32 b = urand(0, getLevel()/2);
+ loot->gold = uint32(10 * (a + b) * sWorld.getRate(RATE_DROP_MONEY));
+ }
+ }
+ else
+ {
+ // the player whose group may loot the corpse
+ Player *recipient = creature->GetLootRecipient();
+ if (!recipient)
+ {
+ creature->SetLootRecipient(this);
+ recipient = this;
+ }
+
+ if (creature->lootForPickPocketed)
+ {
+ creature->lootForPickPocketed = false;
+ loot->clear();
+ }
+
+ if(!creature->lootForBody)
+ {
+ creature->lootForBody = true;
+ loot->clear();
+
+ if (uint32 lootid = creature->GetCreatureInfo()->lootid)
+ loot->FillLoot(lootid, LootTemplates_Creature, recipient);
+
+ loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold);
+
+ if(Group* group = recipient->GetGroup())
+ {
+ group->UpdateLooterGuid(creature,true);
+
+ switch (group->GetLootMethod())
+ {
+ case GROUP_LOOT:
+ // GroupLoot delete items over threshold (threshold even not implemented), and roll them. Items with quality<threshold, round robin
+ group->GroupLoot(recipient->GetGUID(), loot, creature);
+ break;
+ case NEED_BEFORE_GREED:
+ group->NeedBeforeGreed(recipient->GetGUID(), loot, creature);
+ break;
+ case MASTER_LOOT:
+ group->MasterLoot(recipient->GetGUID(), loot, creature);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // possible only if creature->lootForBody && loot->empty() at spell cast check
+ if (loot_type == LOOT_SKINNING)
+ {
+ loot->clear();
+ loot->FillLoot(creature->GetCreatureInfo()->SkinLootId, LootTemplates_Skinning, this);
+ }
+ // set group rights only for loot_type != LOOT_SKINNING
+ else
+ {
+ if(Group* group = GetGroup())
+ {
+ if( group == recipient->GetGroup() )
+ {
+ if(group->GetLootMethod() == FREE_FOR_ALL)
+ permission = ALL_PERMISSION;
+ else if(group->GetLooterGuid() == GetGUID())
+ {
+ if(group->GetLootMethod() == MASTER_LOOT)
+ permission = MASTER_PERMISSION;
+ else
+ permission = ALL_PERMISSION;
+ }
+ else
+ permission = GROUP_PERMISSION;
+ }
+ else
+ permission = NONE_PERMISSION;
+ }
+ else if(recipient == this)
+ permission = ALL_PERMISSION;
+ else
+ permission = NONE_PERMISSION;
+ }
+ }
+ }
+
+ SetLootGUID(guid);
+
+ QuestItemList *q_list = 0;
+ if (permission != NONE_PERMISSION)
+ {
+ QuestItemMap const& lootPlayerQuestItems = loot->GetPlayerQuestItems();
+ QuestItemMap::const_iterator itr = lootPlayerQuestItems.find(GetGUIDLow());
+ if (itr == lootPlayerQuestItems.end())
+ q_list = loot->FillQuestLoot(this);
+ else
+ q_list = itr->second;
+ }
+
+ QuestItemList *ffa_list = 0;
+ if (permission != NONE_PERMISSION)
+ {
+ QuestItemMap const& lootPlayerFFAItems = loot->GetPlayerFFAItems();
+ QuestItemMap::const_iterator itr = lootPlayerFFAItems.find(GetGUIDLow());
+ if (itr == lootPlayerFFAItems.end())
+ ffa_list = loot->FillFFALoot(this);
+ else
+ ffa_list = itr->second;
+ }
+
+ QuestItemList *conditional_list = 0;
+ if (permission != NONE_PERMISSION)
+ {
+ QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = loot->GetPlayerNonQuestNonFFAConditionalItems();
+ QuestItemMap::const_iterator itr = lootPlayerNonQuestNonFFAConditionalItems.find(GetGUIDLow());
+ if (itr == lootPlayerNonQuestNonFFAConditionalItems.end())
+ conditional_list = loot->FillNonQuestNonFFAConditionalLoot(this);
+ else
+ conditional_list = itr->second;
+ }
+
+ // LOOT_PICKPOCKETING, LOOT_PROSPECTING, LOOT_DISENCHANTING and LOOT_INSIGNIA unsupported by client, sending LOOT_SKINNING instead
+ if(loot_type == LOOT_PICKPOCKETING || loot_type == LOOT_DISENCHANTING || loot_type == LOOT_PROSPECTING || loot_type == LOOT_INSIGNIA)
+ loot_type = LOOT_SKINNING;
+
+ if(loot_type == LOOT_FISHINGHOLE)
+ loot_type = LOOT_FISHING;
+
+ WorldPacket data(SMSG_LOOT_RESPONSE, (9+50)); // we guess size
+
+ data << uint64(guid);
+ data << uint8(loot_type);
+ data << LootView(*loot, q_list, ffa_list, conditional_list, this, permission);
+
+ SendDirectMessage(&data);
+
+ // add 'this' player as one of the players that are looting 'loot'
+ if (permission != NONE_PERMISSION)
+ loot->AddLooter(GetGUID());
+
+ if ( loot_type == LOOT_CORPSE && !IS_ITEM_GUID(guid) )
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING);
+}
+
+void Player::SendNotifyLootMoneyRemoved()
+{
+ WorldPacket data(SMSG_LOOT_CLEAR_MONEY, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendNotifyLootItemRemoved(uint8 lootSlot)
+{
+ WorldPacket data(SMSG_LOOT_REMOVED, 1);
+ data << uint8(lootSlot);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendUpdateWorldState(uint32 Field, uint32 Value)
+{
+ WorldPacket data(SMSG_UPDATE_WORLD_STATE, 8);
+ data << Field;
+ data << Value;
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendInitWorldStates()
+{
+ // data depends on zoneid/mapid...
+ BattleGround* bg = GetBattleGround();
+ uint16 NumberOfFields = 0;
+ uint32 mapid = GetMapId();
+ uint32 zoneid = GetZoneId();
+ uint32 areaid = GetAreaId();
+ sLog.outDebug("Sending SMSG_INIT_WORLD_STATES to Map:%u, Zone: %u", mapid, zoneid);
+ // may be exist better way to do this...
+ switch(zoneid)
+ {
+ case 0:
+ case 1:
+ case 4:
+ case 8:
+ case 10:
+ case 11:
+ case 12:
+ case 36:
+ case 38:
+ case 40:
+ case 41:
+ case 51:
+ case 267:
+ case 1519:
+ case 1537:
+ case 2257:
+ case 2918:
+ NumberOfFields = 6;
+ break;
+ case 2597:
+ NumberOfFields = 81;
+ break;
+ case 3277:
+ NumberOfFields = 14;
+ break;
+ case 3358:
+ case 3820:
+ NumberOfFields = 38;
+ break;
+ case 3483:
+ NumberOfFields = 22;
+ break;
+ case 3519:
+ NumberOfFields = 36;
+ break;
+ case 3521:
+ NumberOfFields = 35;
+ break;
+ case 3698:
+ case 3702:
+ case 3968:
+ NumberOfFields = 9;
+ break;
+ case 3703:
+ NumberOfFields = 9;
+ break;
+ default:
+ NumberOfFields = 10;
+ break;
+ }
+
+ WorldPacket data(SMSG_INIT_WORLD_STATES, (4+4+4+2+(NumberOfFields*8)));
+ data << uint32(mapid); // mapid
+ data << uint32(zoneid); // zone id
+ data << uint32(areaid); // area id, new 2.1.0
+ data << uint16(NumberOfFields); // count of uint64 blocks
+ data << uint32(0x8d8) << uint32(0x0); // 1
+ data << uint32(0x8d7) << uint32(0x0); // 2
+ data << uint32(0x8d6) << uint32(0x0); // 3
+ data << uint32(0x8d5) << uint32(0x0); // 4
+ data << uint32(0x8d4) << uint32(0x0); // 5
+ data << uint32(0x8d3) << uint32(0x0); // 6
+ if(mapid == 530) // Outland
+ {
+ data << uint32(0x9bf) << uint32(0x0); // 7
+ data << uint32(0x9bd) << uint32(0xF); // 8
+ data << uint32(0x9bb) << uint32(0xF); // 9
+ }
+ switch(zoneid)
+ {
+ case 1:
+ case 11:
+ case 12:
+ case 38:
+ case 40:
+ case 51:
+ case 1519:
+ case 1537:
+ case 2257:
+ break;
+ case 2597: // AV
+ data << uint32(0x7ae) << uint32(0x1); // 7
+ data << uint32(0x532) << uint32(0x1); // 8
+ data << uint32(0x531) << uint32(0x0); // 9
+ data << uint32(0x52e) << uint32(0x0); // 10
+ data << uint32(0x571) << uint32(0x0); // 11
+ data << uint32(0x570) << uint32(0x0); // 12
+ data << uint32(0x567) << uint32(0x1); // 13
+ data << uint32(0x566) << uint32(0x1); // 14
+ data << uint32(0x550) << uint32(0x1); // 15
+ data << uint32(0x544) << uint32(0x0); // 16
+ data << uint32(0x536) << uint32(0x0); // 17
+ data << uint32(0x535) << uint32(0x1); // 18
+ data << uint32(0x518) << uint32(0x0); // 19
+ data << uint32(0x517) << uint32(0x0); // 20
+ data << uint32(0x574) << uint32(0x0); // 21
+ data << uint32(0x573) << uint32(0x0); // 22
+ data << uint32(0x572) << uint32(0x0); // 23
+ data << uint32(0x56f) << uint32(0x0); // 24
+ data << uint32(0x56e) << uint32(0x0); // 25
+ data << uint32(0x56d) << uint32(0x0); // 26
+ data << uint32(0x56c) << uint32(0x0); // 27
+ data << uint32(0x56b) << uint32(0x0); // 28
+ data << uint32(0x56a) << uint32(0x1); // 29
+ data << uint32(0x569) << uint32(0x1); // 30
+ data << uint32(0x568) << uint32(0x1); // 13
+ data << uint32(0x565) << uint32(0x0); // 32
+ data << uint32(0x564) << uint32(0x0); // 33
+ data << uint32(0x563) << uint32(0x0); // 34
+ data << uint32(0x562) << uint32(0x0); // 35
+ data << uint32(0x561) << uint32(0x0); // 36
+ data << uint32(0x560) << uint32(0x0); // 37
+ data << uint32(0x55f) << uint32(0x0); // 38
+ data << uint32(0x55e) << uint32(0x0); // 39
+ data << uint32(0x55d) << uint32(0x0); // 40
+ data << uint32(0x3c6) << uint32(0x4); // 41
+ data << uint32(0x3c4) << uint32(0x6); // 42
+ data << uint32(0x3c2) << uint32(0x4); // 43
+ data << uint32(0x516) << uint32(0x1); // 44
+ data << uint32(0x515) << uint32(0x0); // 45
+ data << uint32(0x3b6) << uint32(0x6); // 46
+ data << uint32(0x55c) << uint32(0x0); // 47
+ data << uint32(0x55b) << uint32(0x0); // 48
+ data << uint32(0x55a) << uint32(0x0); // 49
+ data << uint32(0x559) << uint32(0x0); // 50
+ data << uint32(0x558) << uint32(0x0); // 51
+ data << uint32(0x557) << uint32(0x0); // 52
+ data << uint32(0x556) << uint32(0x0); // 53
+ data << uint32(0x555) << uint32(0x0); // 54
+ data << uint32(0x554) << uint32(0x1); // 55
+ data << uint32(0x553) << uint32(0x1); // 56
+ data << uint32(0x552) << uint32(0x1); // 57
+ data << uint32(0x551) << uint32(0x1); // 58
+ data << uint32(0x54f) << uint32(0x0); // 59
+ data << uint32(0x54e) << uint32(0x0); // 60
+ data << uint32(0x54d) << uint32(0x1); // 61
+ data << uint32(0x54c) << uint32(0x0); // 62
+ data << uint32(0x54b) << uint32(0x0); // 63
+ data << uint32(0x545) << uint32(0x0); // 64
+ data << uint32(0x543) << uint32(0x1); // 65
+ data << uint32(0x542) << uint32(0x0); // 66
+ data << uint32(0x540) << uint32(0x0); // 67
+ data << uint32(0x53f) << uint32(0x0); // 68
+ data << uint32(0x53e) << uint32(0x0); // 69
+ data << uint32(0x53d) << uint32(0x0); // 70
+ data << uint32(0x53c) << uint32(0x0); // 71
+ data << uint32(0x53b) << uint32(0x0); // 72
+ data << uint32(0x53a) << uint32(0x1); // 73
+ data << uint32(0x539) << uint32(0x0); // 74
+ data << uint32(0x538) << uint32(0x0); // 75
+ data << uint32(0x537) << uint32(0x0); // 76
+ data << uint32(0x534) << uint32(0x0); // 77
+ data << uint32(0x533) << uint32(0x0); // 78
+ data << uint32(0x530) << uint32(0x0); // 79
+ data << uint32(0x52f) << uint32(0x0); // 80
+ data << uint32(0x52d) << uint32(0x1); // 81
+ break;
+ case 3277: // WS
+ if (bg && bg->GetTypeID() == BATTLEGROUND_WS)
+ bg->FillInitialWorldStates(data);
+ else
+ {
+ data << uint32(0x62d) << uint32(0x0); // 7 1581 alliance flag captures
+ data << uint32(0x62e) << uint32(0x0); // 8 1582 horde flag captures
+ data << uint32(0x609) << uint32(0x0); // 9 1545 unk, set to 1 on alliance flag pickup...
+ data << uint32(0x60a) << uint32(0x0); // 10 1546 unk, set to 1 on horde flag pickup, after drop it's -1
+ data << uint32(0x60b) << uint32(0x2); // 11 1547 unk
+ data << uint32(0x641) << uint32(0x3); // 12 1601 unk (max flag captures?)
+ data << uint32(0x922) << uint32(0x1); // 13 2338 horde (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
+ data << uint32(0x923) << uint32(0x1); // 14 2339 alliance (0 - hide, 1 - flag ok, 2 - flag picked up (flashing), 3 - flag picked up (not flashing)
+ }
+ break;
+ case 3358: // AB
+ if (bg && bg->GetTypeID() == BATTLEGROUND_AB)
+ bg->FillInitialWorldStates(data);
+ else
+ {
+ data << uint32(0x6e7) << uint32(0x0); // 7 1767 stables alliance
+ data << uint32(0x6e8) << uint32(0x0); // 8 1768 stables horde
+ data << uint32(0x6e9) << uint32(0x0); // 9 1769 unk, ST?
+ data << uint32(0x6ea) << uint32(0x0); // 10 1770 stables (show/hide)
+ data << uint32(0x6ec) << uint32(0x0); // 11 1772 farm (0 - horde controlled, 1 - alliance controlled)
+ data << uint32(0x6ed) << uint32(0x0); // 12 1773 farm (show/hide)
+ data << uint32(0x6ee) << uint32(0x0); // 13 1774 farm color
+ data << uint32(0x6ef) << uint32(0x0); // 14 1775 gold mine color, may be FM?
+ data << uint32(0x6f0) << uint32(0x0); // 15 1776 alliance resources
+ data << uint32(0x6f1) << uint32(0x0); // 16 1777 horde resources
+ data << uint32(0x6f2) << uint32(0x0); // 17 1778 horde bases
+ data << uint32(0x6f3) << uint32(0x0); // 18 1779 alliance bases
+ data << uint32(0x6f4) << uint32(0x7d0); // 19 1780 max resources (2000)
+ data << uint32(0x6f6) << uint32(0x0); // 20 1782 blacksmith color
+ data << uint32(0x6f7) << uint32(0x0); // 21 1783 blacksmith (show/hide)
+ data << uint32(0x6f8) << uint32(0x0); // 22 1784 unk, bs?
+ data << uint32(0x6f9) << uint32(0x0); // 23 1785 unk, bs?
+ data << uint32(0x6fb) << uint32(0x0); // 24 1787 gold mine (0 - horde contr, 1 - alliance contr)
+ data << uint32(0x6fc) << uint32(0x0); // 25 1788 gold mine (0 - conflict, 1 - horde)
+ data << uint32(0x6fd) << uint32(0x0); // 26 1789 gold mine (1 - show/0 - hide)
+ data << uint32(0x6fe) << uint32(0x0); // 27 1790 gold mine color
+ data << uint32(0x700) << uint32(0x0); // 28 1792 gold mine color, wtf?, may be LM?
+ data << uint32(0x701) << uint32(0x0); // 29 1793 lumber mill color (0 - conflict, 1 - horde contr)
+ data << uint32(0x702) << uint32(0x0); // 30 1794 lumber mill (show/hide)
+ data << uint32(0x703) << uint32(0x0); // 31 1795 lumber mill color color
+ data << uint32(0x732) << uint32(0x1); // 32 1842 stables (1 - uncontrolled)
+ data << uint32(0x733) << uint32(0x1); // 33 1843 gold mine (1 - uncontrolled)
+ data << uint32(0x734) << uint32(0x1); // 34 1844 lumber mill (1 - uncontrolled)
+ data << uint32(0x735) << uint32(0x1); // 35 1845 farm (1 - uncontrolled)
+ data << uint32(0x736) << uint32(0x1); // 36 1846 blacksmith (1 - uncontrolled)
+ data << uint32(0x745) << uint32(0x2); // 37 1861 unk
+ data << uint32(0x7a3) << uint32(0x708); // 38 1955 warning limit (1800)
+ }
+ break;
+ case 3820: // EY
+ if (bg && bg->GetTypeID() == BATTLEGROUND_EY)
+ bg->FillInitialWorldStates(data);
+ else
+ {
+ data << uint32(0xac1) << uint32(0x0); // 7 2753 Horde Bases
+ data << uint32(0xac0) << uint32(0x0); // 8 2752 Alliance Bases
+ data << uint32(0xab6) << uint32(0x0); // 9 2742 Mage Tower - Horde conflict
+ data << uint32(0xab5) << uint32(0x0); // 10 2741 Mage Tower - Alliance conflict
+ data << uint32(0xab4) << uint32(0x0); // 11 2740 Fel Reaver - Horde conflict
+ data << uint32(0xab3) << uint32(0x0); // 12 2739 Fel Reaver - Alliance conflict
+ data << uint32(0xab2) << uint32(0x0); // 13 2738 Draenei - Alliance conflict
+ data << uint32(0xab1) << uint32(0x0); // 14 2737 Draenei - Horde conflict
+ data << uint32(0xab0) << uint32(0x0); // 15 2736 unk // 0 at start
+ data << uint32(0xaaf) << uint32(0x0); // 16 2735 unk // 0 at start
+ data << uint32(0xaad) << uint32(0x0); // 17 2733 Draenei - Horde control
+ data << uint32(0xaac) << uint32(0x0); // 18 2732 Draenei - Alliance control
+ data << uint32(0xaab) << uint32(0x1); // 19 2731 Draenei uncontrolled (1 - yes, 0 - no)
+ data << uint32(0xaaa) << uint32(0x0); // 20 2730 Mage Tower - Alliance control
+ data << uint32(0xaa9) << uint32(0x0); // 21 2729 Mage Tower - Horde control
+ data << uint32(0xaa8) << uint32(0x1); // 22 2728 Mage Tower uncontrolled (1 - yes, 0 - no)
+ data << uint32(0xaa7) << uint32(0x0); // 23 2727 Fel Reaver - Horde control
+ data << uint32(0xaa6) << uint32(0x0); // 24 2726 Fel Reaver - Alliance control
+ data << uint32(0xaa5) << uint32(0x1); // 25 2725 Fel Reaver uncontroled (1 - yes, 0 - no)
+ data << uint32(0xaa4) << uint32(0x0); // 26 2724 Boold Elf - Horde control
+ data << uint32(0xaa3) << uint32(0x0); // 27 2723 Boold Elf - Alliance control
+ data << uint32(0xaa2) << uint32(0x1); // 28 2722 Boold Elf uncontrolled (1 - yes, 0 - no)
+ data << uint32(0xac5) << uint32(0x1); // 29 2757 Flag (1 - show, 0 - hide) - doesn't work exactly this way!
+ data << uint32(0xad2) << uint32(0x1); // 30 2770 Horde top-stats (1 - show, 0 - hide) // 02 -> horde picked up the flag
+ data << uint32(0xad1) << uint32(0x1); // 31 2769 Alliance top-stats (1 - show, 0 - hide) // 02 -> alliance picked up the flag
+ data << uint32(0xabe) << uint32(0x0); // 32 2750 Horde resources
+ data << uint32(0xabd) << uint32(0x0); // 33 2749 Alliance resources
+ data << uint32(0xa05) << uint32(0x8e); // 34 2565 unk, constant?
+ data << uint32(0xaa0) << uint32(0x0); // 35 2720 Capturing progress-bar (100 -> empty (only grey), 0 -> blue|red (no grey), default 0)
+ data << uint32(0xa9f) << uint32(0x0); // 36 2719 Capturing progress-bar (0 - left, 100 - right)
+ data << uint32(0xa9e) << uint32(0x0); // 37 2718 Capturing progress-bar (1 - show, 0 - hide)
+ data << uint32(0xc0d) << uint32(0x17b); // 38 3085 unk
+ // and some more ... unknown
+ }
+ break;
+ case 3483: // Hellfire Peninsula
+ data << uint32(0x9ba) << uint32(0x1); // 10
+ data << uint32(0x9b9) << uint32(0x1); // 11
+ data << uint32(0x9b5) << uint32(0x0); // 12
+ data << uint32(0x9b4) << uint32(0x1); // 13
+ data << uint32(0x9b3) << uint32(0x0); // 14
+ data << uint32(0x9b2) << uint32(0x0); // 15
+ data << uint32(0x9b1) << uint32(0x1); // 16
+ data << uint32(0x9b0) << uint32(0x0); // 17
+ data << uint32(0x9ae) << uint32(0x0); // 18 horde pvp objectives captured
+ data << uint32(0x9ac) << uint32(0x0); // 19
+ data << uint32(0x9a8) << uint32(0x0); // 20
+ data << uint32(0x9a7) << uint32(0x0); // 21
+ data << uint32(0x9a6) << uint32(0x1); // 22
+ break;
+ case 3519: // Terokkar Forest
+ data << uint32(0xa41) << uint32(0x0); // 10
+ data << uint32(0xa40) << uint32(0x14); // 11
+ data << uint32(0xa3f) << uint32(0x0); // 12
+ data << uint32(0xa3e) << uint32(0x0); // 13
+ data << uint32(0xa3d) << uint32(0x5); // 14
+ data << uint32(0xa3c) << uint32(0x0); // 15
+ data << uint32(0xa87) << uint32(0x0); // 16
+ data << uint32(0xa86) << uint32(0x0); // 17
+ data << uint32(0xa85) << uint32(0x0); // 18
+ data << uint32(0xa84) << uint32(0x0); // 19
+ data << uint32(0xa83) << uint32(0x0); // 20
+ data << uint32(0xa82) << uint32(0x0); // 21
+ data << uint32(0xa81) << uint32(0x0); // 22
+ data << uint32(0xa80) << uint32(0x0); // 23
+ data << uint32(0xa7e) << uint32(0x0); // 24
+ data << uint32(0xa7d) << uint32(0x0); // 25
+ data << uint32(0xa7c) << uint32(0x0); // 26
+ data << uint32(0xa7b) << uint32(0x0); // 27
+ data << uint32(0xa7a) << uint32(0x0); // 28
+ data << uint32(0xa79) << uint32(0x0); // 29
+ data << uint32(0x9d0) << uint32(0x5); // 30
+ data << uint32(0x9ce) << uint32(0x0); // 31
+ data << uint32(0x9cd) << uint32(0x0); // 32
+ data << uint32(0x9cc) << uint32(0x0); // 33
+ data << uint32(0xa88) << uint32(0x0); // 34
+ data << uint32(0xad0) << uint32(0x0); // 35
+ data << uint32(0xacf) << uint32(0x1); // 36
+ break;
+ case 3521: // Zangarmarsh
+ data << uint32(0x9e1) << uint32(0x0); // 10
+ data << uint32(0x9e0) << uint32(0x0); // 11
+ data << uint32(0x9df) << uint32(0x0); // 12
+ data << uint32(0xa5d) << uint32(0x1); // 13
+ data << uint32(0xa5c) << uint32(0x0); // 14
+ data << uint32(0xa5b) << uint32(0x1); // 15
+ data << uint32(0xa5a) << uint32(0x0); // 16
+ data << uint32(0xa59) << uint32(0x1); // 17
+ data << uint32(0xa58) << uint32(0x0); // 18
+ data << uint32(0xa57) << uint32(0x0); // 19
+ data << uint32(0xa56) << uint32(0x0); // 20
+ data << uint32(0xa55) << uint32(0x1); // 21
+ data << uint32(0xa54) << uint32(0x0); // 22
+ data << uint32(0x9e7) << uint32(0x0); // 23
+ data << uint32(0x9e6) << uint32(0x0); // 24
+ data << uint32(0x9e5) << uint32(0x0); // 25
+ data << uint32(0xa00) << uint32(0x0); // 26
+ data << uint32(0x9ff) << uint32(0x1); // 27
+ data << uint32(0x9fe) << uint32(0x0); // 28
+ data << uint32(0x9fd) << uint32(0x0); // 29
+ data << uint32(0x9fc) << uint32(0x1); // 30
+ data << uint32(0x9fb) << uint32(0x0); // 31
+ data << uint32(0xa62) << uint32(0x0); // 32
+ data << uint32(0xa61) << uint32(0x1); // 33
+ data << uint32(0xa60) << uint32(0x1); // 34
+ data << uint32(0xa5f) << uint32(0x0); // 35
+ break;
+ case 3698: // Nagrand Arena
+ if (bg && bg->GetTypeID() == BATTLEGROUND_NA)
+ bg->FillInitialWorldStates(data);
+ else
+ {
+ data << uint32(0xa0f) << uint32(0x0); // 7
+ data << uint32(0xa10) << uint32(0x0); // 8
+ data << uint32(0xa11) << uint32(0x0); // 9 show
+ }
+ break;
+ case 3702: // Blade's Edge Arena
+ if (bg && bg->GetTypeID() == BATTLEGROUND_BE)
+ bg->FillInitialWorldStates(data);
+ else
+ {
+ data << uint32(0x9f0) << uint32(0x0); // 7 gold
+ data << uint32(0x9f1) << uint32(0x0); // 8 green
+ data << uint32(0x9f3) << uint32(0x0); // 9 show
+ }
+ break;
+ case 3968: // Ruins of Lordaeron
+ if (bg && bg->GetTypeID() == BATTLEGROUND_RL)
+ bg->FillInitialWorldStates(data);
+ else
+ {
+ data << uint32(0xbb8) << uint32(0x0); // 7 gold
+ data << uint32(0xbb9) << uint32(0x0); // 8 green
+ data << uint32(0xbba) << uint32(0x0); // 9 show
+ }
+ break;
+ case 3703: // Shattrath City
+ break;
+ default:
+ data << uint32(0x914) << uint32(0x0); // 7
+ data << uint32(0x913) << uint32(0x0); // 8
+ data << uint32(0x912) << uint32(0x0); // 9
+ data << uint32(0x915) << uint32(0x0); // 10
+ break;
+ }
+ GetSession()->SendPacket(&data);
+}
+
+uint32 Player::GetXPRestBonus(uint32 xp)
+{
+ uint32 rested_bonus = (uint32)GetRestBonus(); // xp for each rested bonus
+
+ if(rested_bonus > xp) // max rested_bonus == xp or (r+x) = 200% xp
+ rested_bonus = xp;
+
+ SetRestBonus( GetRestBonus() - rested_bonus);
+
+ sLog.outDetail("Player gain %u xp (+ %u Rested Bonus). Rested points=%f",xp+rested_bonus,rested_bonus,GetRestBonus());
+ return rested_bonus;
+}
+
+void Player::SetBindPoint(uint64 guid)
+{
+ WorldPacket data(SMSG_BINDER_CONFIRM, 8);
+ data << uint64(guid);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendTalentWipeConfirm(uint64 guid)
+{
+ WorldPacket data(MSG_TALENT_WIPE_CONFIRM, (8+4));
+ data << uint64(guid);
+ data << uint32(resetTalentsCost());
+ if(sWorld.getConfig(CONFIG_NO_RESET_TALENT_COST))
+ data << uint32(0);
+ else
+ data << uint32(resetTalentsCost());
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendPetSkillWipeConfirm()
+{
+ Pet* pet = GetPet();
+ if(!pet)
+ return;
+ WorldPacket data(SMSG_PET_UNLEARN_CONFIRM, (8+4));
+ data << pet->GetGUID();
+ data << uint32(pet->resetTalentsCost());
+ GetSession()->SendPacket( &data );
+}
+
+/*********************************************************/
+/*** STORAGE SYSTEM ***/
+/*********************************************************/
+
+void Player::SetVirtualItemSlot( uint8 i, Item* item)
+{
+ assert(i < 3);
+ if(i < 2 && item)
+ {
+ if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
+ return;
+ uint32 charges = item->GetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT);
+ if(charges == 0)
+ return;
+ if(charges > 1)
+ item->SetEnchantmentCharges(TEMP_ENCHANTMENT_SLOT,charges-1);
+ else if(charges <= 1)
+ {
+ ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false);
+ item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
+ }
+ }
+}
+
+void Player::SetSheath( uint32 sheathed )
+{
+ switch (sheathed)
+ {
+ case SHEATH_STATE_UNARMED: // no prepared weapon
+ SetVirtualItemSlot(0,NULL);
+ SetVirtualItemSlot(1,NULL);
+ SetVirtualItemSlot(2,NULL);
+ break;
+ case SHEATH_STATE_MELEE: // prepared melee weapon
+ {
+ SetVirtualItemSlot(0,GetWeaponForAttack(BASE_ATTACK,true));
+ SetVirtualItemSlot(1,GetWeaponForAttack(OFF_ATTACK,true));
+ SetVirtualItemSlot(2,NULL);
+ }; break;
+ case SHEATH_STATE_RANGED: // prepared ranged weapon
+ SetVirtualItemSlot(0,NULL);
+ SetVirtualItemSlot(1,NULL);
+ SetVirtualItemSlot(2,GetWeaponForAttack(RANGED_ATTACK,true));
+ break;
+ default:
+ SetVirtualItemSlot(0,NULL);
+ SetVirtualItemSlot(1,NULL);
+ SetVirtualItemSlot(2,NULL);
+ break;
+ }
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, sheathed); // this must visualize Sheath changing for other players...
+}
+
+uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap ) const
+{
+ uint8 pClass = getClass();
+
+ uint8 slots[4];
+ slots[0] = NULL_SLOT;
+ slots[1] = NULL_SLOT;
+ slots[2] = NULL_SLOT;
+ slots[3] = NULL_SLOT;
+ switch( proto->InventoryType )
+ {
+ case INVTYPE_HEAD:
+ slots[0] = EQUIPMENT_SLOT_HEAD;
+ break;
+ case INVTYPE_NECK:
+ slots[0] = EQUIPMENT_SLOT_NECK;
+ break;
+ case INVTYPE_SHOULDERS:
+ slots[0] = EQUIPMENT_SLOT_SHOULDERS;
+ break;
+ case INVTYPE_BODY:
+ slots[0] = EQUIPMENT_SLOT_BODY;
+ break;
+ case INVTYPE_CHEST:
+ slots[0] = EQUIPMENT_SLOT_CHEST;
+ break;
+ case INVTYPE_ROBE:
+ slots[0] = EQUIPMENT_SLOT_CHEST;
+ break;
+ case INVTYPE_WAIST:
+ slots[0] = EQUIPMENT_SLOT_WAIST;
+ break;
+ case INVTYPE_LEGS:
+ slots[0] = EQUIPMENT_SLOT_LEGS;
+ break;
+ case INVTYPE_FEET:
+ slots[0] = EQUIPMENT_SLOT_FEET;
+ break;
+ case INVTYPE_WRISTS:
+ slots[0] = EQUIPMENT_SLOT_WRISTS;
+ break;
+ case INVTYPE_HANDS:
+ slots[0] = EQUIPMENT_SLOT_HANDS;
+ break;
+ case INVTYPE_FINGER:
+ slots[0] = EQUIPMENT_SLOT_FINGER1;
+ slots[1] = EQUIPMENT_SLOT_FINGER2;
+ break;
+ case INVTYPE_TRINKET:
+ slots[0] = EQUIPMENT_SLOT_TRINKET1;
+ slots[1] = EQUIPMENT_SLOT_TRINKET2;
+ break;
+ case INVTYPE_CLOAK:
+ slots[0] = EQUIPMENT_SLOT_BACK;
+ break;
+ case INVTYPE_WEAPON:
+ {
+ slots[0] = EQUIPMENT_SLOT_MAINHAND;
+
+ // suggest offhand slot only if know dual wielding
+ // (this will be replace mainhand weapon at auto equip instead unwonted "you don't known dual wielding" ...
+ if(CanDualWield())
+ slots[1] = EQUIPMENT_SLOT_OFFHAND;
+ };break;
+ case INVTYPE_SHIELD:
+ slots[0] = EQUIPMENT_SLOT_OFFHAND;
+ break;
+ case INVTYPE_RANGED:
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case INVTYPE_2HWEAPON:
+ slots[0] = EQUIPMENT_SLOT_MAINHAND;
+ break;
+ case INVTYPE_TABARD:
+ slots[0] = EQUIPMENT_SLOT_TABARD;
+ break;
+ case INVTYPE_WEAPONMAINHAND:
+ slots[0] = EQUIPMENT_SLOT_MAINHAND;
+ break;
+ case INVTYPE_WEAPONOFFHAND:
+ slots[0] = EQUIPMENT_SLOT_OFFHAND;
+ break;
+ case INVTYPE_HOLDABLE:
+ slots[0] = EQUIPMENT_SLOT_OFFHAND;
+ break;
+ case INVTYPE_THROWN:
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case INVTYPE_RANGEDRIGHT:
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case INVTYPE_BAG:
+ slots[0] = INVENTORY_SLOT_BAG_1;
+ slots[1] = INVENTORY_SLOT_BAG_2;
+ slots[2] = INVENTORY_SLOT_BAG_3;
+ slots[3] = INVENTORY_SLOT_BAG_4;
+ break;
+ case INVTYPE_RELIC:
+ {
+ switch(proto->SubClass)
+ {
+ case ITEM_SUBCLASS_ARMOR_LIBRAM:
+ if (pClass == CLASS_PALADIN)
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case ITEM_SUBCLASS_ARMOR_IDOL:
+ if (pClass == CLASS_DRUID)
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case ITEM_SUBCLASS_ARMOR_TOTEM:
+ if (pClass == CLASS_SHAMAN)
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ case ITEM_SUBCLASS_ARMOR_MISC:
+ if (pClass == CLASS_WARLOCK)
+ slots[0] = EQUIPMENT_SLOT_RANGED;
+ break;
+ }
+ break;
+ }
+ default :
+ return NULL_SLOT;
+ }
+
+ if( slot != NULL_SLOT )
+ {
+ if( swap || !GetItemByPos( INVENTORY_SLOT_BAG_0, slot ) )
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ if ( slots[i] == slot )
+ return slot;
+ }
+ }
+ }
+ else
+ {
+ // search free slot at first
+ for (int i = 0; i < 4; i++)
+ {
+ if ( slots[i] != NULL_SLOT && !GetItemByPos( INVENTORY_SLOT_BAG_0, slots[i] ) )
+ {
+ // in case 2hand equipped weapon offhand slot empty but not free
+ if(slots[i]==EQUIPMENT_SLOT_OFFHAND)
+ {
+ Item* mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND );
+ if(!mainItem || mainItem->GetProto()->InventoryType != INVTYPE_2HWEAPON)
+ return slots[i];
+ }
+ else
+ return slots[i];
+ }
+ }
+
+ // if not found free and can swap return first appropriate from used
+ for (int i = 0; i < 4; i++)
+ {
+ if ( slots[i] != NULL_SLOT && swap )
+ return slots[i];
+ }
+ }
+
+ // no free position
+ return NULL_SLOT;
+}
+
+uint8 Player::CanUnequipItems( uint32 item, uint32 count ) const
+{
+ Item *pItem;
+ uint32 tempcount = 0;
+
+ uint8 res = EQUIP_ERR_OK;
+
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ uint8 ires = CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i, false);
+ if(ires==EQUIP_ERR_OK)
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return EQUIP_ERR_OK;
+ }
+ else
+ res = ires;
+ }
+ }
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return EQUIP_ERR_OK;
+ }
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return EQUIP_ERR_OK;
+ }
+ }
+ Bag *pBag;
+ ItemPrototype const *pBagProto;
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ pItem = GetItemByPos( i, j );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ }
+ }
+
+ // not found req. item count and have unequippable items
+ return res;
+}
+
+uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) const
+{
+ uint32 count = 0;
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem != skipItem && pItem->GetEntry() == item )
+ count += pItem->GetCount();
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem != skipItem && pItem->GetEntry() == item )
+ count += pItem->GetCount();
+ }
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ count += pBag->GetItemCount(item,skipItem);
+ }
+
+ if(skipItem && skipItem->GetProto()->GemProperties)
+ {
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem != skipItem && pItem->GetProto()->Socket[0].Color )
+ count += pItem->GetGemCountWithID(item);
+ }
+ }
+
+ if(inBankAlso)
+ {
+ for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem != skipItem && pItem->GetEntry() == item )
+ count += pItem->GetCount();
+ }
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ count += pBag->GetItemCount(item,skipItem);
+ }
+
+ if(skipItem && skipItem->GetProto()->GemProperties)
+ {
+ for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem != skipItem && pItem->GetProto()->Socket[0].Color )
+ count += pItem->GetGemCountWithID(item);
+ }
+ }
+ }
+
+ return count;
+}
+
+Item* Player::GetItemByGuid( uint64 guid ) const
+{
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetGUID() == guid )
+ return pItem;
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetGUID() == guid )
+ return pItem;
+ }
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ ItemPrototype const *pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = pBag->GetItemByPos( j );
+ if( pItem && pItem->GetGUID() == guid )
+ return pItem;
+ }
+ }
+ }
+ }
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ ItemPrototype const *pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = pBag->GetItemByPos( j );
+ if( pItem && pItem->GetGUID() == guid )
+ return pItem;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+Item* Player::GetItemByPos( uint16 pos ) const
+{
+ uint8 bag = pos >> 8;
+ uint8 slot = pos & 255;
+ return GetItemByPos( bag, slot );
+}
+
+Item* Player::GetItemByPos( uint8 bag, uint8 slot ) const
+{
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot < BANK_SLOT_BAG_END || slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END ) )
+ return m_items[slot];
+ else if(bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END
+ || bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END )
+ {
+ Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
+ if ( pBag )
+ return pBag->GetItemByPos(slot);
+ }
+ return NULL;
+}
+
+Item* Player::GetWeaponForAttack(WeaponAttackType attackType, bool useable) const
+{
+ uint16 slot;
+ switch (attackType)
+ {
+ case BASE_ATTACK: slot = EQUIPMENT_SLOT_MAINHAND; break;
+ case OFF_ATTACK: slot = EQUIPMENT_SLOT_OFFHAND; break;
+ case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break;
+ default: return NULL;
+ }
+
+ Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+ if (!item || item->GetProto()->Class != ITEM_CLASS_WEAPON)
+ return NULL;
+
+ if(!useable)
+ return item;
+
+ if( item->IsBroken() || !IsUseEquipedWeapon(attackType==BASE_ATTACK) )
+ return NULL;
+
+ return item;
+}
+
+Item* Player::GetShield(bool useable) const
+{
+ Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
+ if (!item || item->GetProto()->Class != ITEM_CLASS_ARMOR)
+ return NULL;
+
+ if(!useable)
+ return item;
+
+ if( item->IsBroken())
+ return NULL;
+
+ return item;
+}
+
+uint32 Player::GetAttackBySlot( uint8 slot )
+{
+ switch(slot)
+ {
+ case EQUIPMENT_SLOT_MAINHAND: return BASE_ATTACK;
+ case EQUIPMENT_SLOT_OFFHAND: return OFF_ATTACK;
+ case EQUIPMENT_SLOT_RANGED: return RANGED_ATTACK;
+ default: return MAX_ATTACK;
+ }
+}
+
+bool Player::HasBankBagSlot( uint8 slot ) const
+{
+ uint32 maxslot = GetByteValue(PLAYER_BYTES_2, 2) + BANK_SLOT_BAG_START;
+ if( slot < maxslot )
+ return true;
+ return false;
+}
+
+bool Player::IsInventoryPos( uint8 bag, uint8 slot )
+{
+ if( bag == INVENTORY_SLOT_BAG_0 && slot == NULL_SLOT )
+ return true;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END ) )
+ return true;
+ if( bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END )
+ return true;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END ) )
+ return true;
+ return false;
+}
+
+bool Player::IsEquipmentPos( uint8 bag, uint8 slot )
+{
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot < EQUIPMENT_SLOT_END ) )
+ return true;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END ) )
+ return true;
+ return false;
+}
+
+bool Player::IsBankPos( uint8 bag, uint8 slot )
+{
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END ) )
+ return true;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) )
+ return true;
+ if( bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END )
+ return true;
+ return false;
+}
+
+bool Player::IsBagPos( uint16 pos )
+{
+ uint8 bag = pos >> 8;
+ uint8 slot = pos & 255;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END ) )
+ return true;
+ if( bag == INVENTORY_SLOT_BAG_0 && ( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END ) )
+ return true;
+ return false;
+}
+
+bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const
+{
+ uint32 tempcount = 0;
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return true;
+ }
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return true;
+ }
+ }
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ {
+ if(ItemPrototype const *pBagProto = pBag->GetProto())
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = GetItemByPos( i, j );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ if(inBankAlso)
+ {
+ for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
+ {
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return true;
+ }
+ }
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ {
+ if(ItemPrototype const *pBagProto = pBag->GetProto())
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = GetItemByPos( i, j );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ tempcount += pItem->GetCount();
+ if( tempcount >= count )
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+Item* Player::GetItemOrItemWithGemEquipped( uint32 item ) const
+{
+ Item *pItem;
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ return pItem;
+ }
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(item);
+ if (pProto && pProto->GemProperties)
+ {
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetProto()->Socket[0].Color )
+ {
+ if (pItem->GetGemCountWithID(item) > 0 )
+ return pItem;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count ) const
+{
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(entry);
+ if( !pProto )
+ {
+ if(no_space_count)
+ *no_space_count = count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ // no maximum
+ if(pProto->MaxCount == 0)
+ return EQUIP_ERR_OK;
+
+ uint32 curcount = GetItemCount(pProto->ItemId,true,pItem);
+
+ if( curcount + count > pProto->MaxCount )
+ {
+ if(no_space_count)
+ *no_space_count = count +curcount - pProto->MaxCount;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ return EQUIP_ERR_OK;
+}
+
+bool Player::HasItemTotemCategory( uint32 TotemCategory ) const
+{
+ Item *pItem;
+ for(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
+ return true;
+ }
+ for(uint8 i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
+ return true;
+ }
+ Bag *pBag;
+ ItemPrototype const *pBagProto;
+ for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; ++j)
+ {
+ pItem = GetItemByPos( i, j );
+ if( pItem && IsTotemCategoryCompatiableWith(pItem->GetProto()->TotemCategory,TotemCategory ))
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool swap, Item* pSrcItem ) const
+{
+ Item* pItem2 = GetItemByPos( bag, slot );
+
+ // ignore move item (this slot will be empty at move)
+ if(pItem2==pSrcItem)
+ pItem2 = NULL;
+
+ uint32 need_space;
+
+ // empty specific slot - check item fit to slot
+ if( !pItem2 || swap )
+ {
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+ // keyring case
+ if(slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_START+GetMaxKeyringSize() && !(pProto->BagFamily & BAG_FAMILY_MASK_KEYS))
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ // prevent cheating
+ if(slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END || slot >= PLAYER_SLOT_END)
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+ }
+ else
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
+ if( !pBag )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ ItemPrototype const* pBagProto = pBag->GetProto();
+ if( !pBagProto )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ if( !ItemCanGoIntoBag(pProto,pBagProto) )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+ }
+
+ // non empty stack with space
+ need_space = pProto->Stackable;
+ }
+ // non empty slot, check item type
+ else
+ {
+ // check item type
+ if(pItem2->GetEntry() != pProto->ItemId)
+ return EQUIP_ERR_ITEM_CANT_STACK;
+
+ // check free space
+ if(pItem2->GetCount() >= pProto->Stackable)
+ return EQUIP_ERR_ITEM_CANT_STACK;
+
+ need_space = pProto->Stackable - pItem2->GetCount();
+ }
+
+ if(need_space > count)
+ need_space = count;
+
+ ItemPosCount newPosition = ItemPosCount((bag << 8) | slot, need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+ }
+ return EQUIP_ERR_OK;
+}
+
+uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const
+{
+ // skip specific bag already processed in first called _CanStoreItem_InBag
+ if(bag==skip_bag)
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
+ if( !pBag )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ ItemPrototype const* pBagProto = pBag->GetProto();
+ if( !pBagProto )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ // specialized bag mode or non-specilized
+ if( non_specialized != (pBagProto->Class == ITEM_CLASS_CONTAINER && pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER) )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ if( !ItemCanGoIntoBag(pProto,pBagProto) )
+ return EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG;
+
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
+ if(j==skip_slot)
+ continue;
+
+ Item* pItem2 = GetItemByPos( bag, j );
+
+ // ignore move item (this slot will be empty at move)
+ if(pItem2==pSrcItem)
+ pItem2 = NULL;
+
+ // if merge skip empty, if !merge skip non-empty
+ if((pItem2!=NULL)!=merge)
+ continue;
+
+ if( pItem2 )
+ {
+ if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->Stackable )
+ {
+ uint32 need_space = pProto->Stackable - pItem2->GetCount();
+ if(need_space > count)
+ need_space = count;
+
+ ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ else
+ {
+ uint32 need_space = pProto->Stackable;
+ if(need_space > count)
+ need_space = count;
+
+ ItemPosCount newPosition = ItemPosCount((bag << 8) | j, need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ return EQUIP_ERR_OK;
+}
+
+uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const
+{
+ for(uint32 j = slot_begin; j < slot_end; j++)
+ {
+ // skip specific slot already processed in first called _CanStoreItem_InSpecificSlot
+ if(INVENTORY_SLOT_BAG_0==skip_bag && j==skip_slot)
+ continue;
+
+ Item* pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, j );
+
+ // ignore move item (this slot will be empty at move)
+ if(pItem2==pSrcItem)
+ pItem2 = NULL;
+
+ // if merge skip empty, if !merge skip non-empty
+ if((pItem2!=NULL)!=merge)
+ continue;
+
+ if( pItem2 )
+ {
+ if(pItem2->GetEntry() == pProto->ItemId && pItem2->GetCount() < pProto->Stackable )
+ {
+ uint32 need_space = pProto->Stackable - pItem2->GetCount();
+ if(need_space > count)
+ need_space = count;
+ ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ else
+ {
+ uint32 need_space = pProto->Stackable;
+ if(need_space > count)
+ need_space = count;
+
+ ItemPosCount newPosition = ItemPosCount((INVENTORY_SLOT_BAG_0 << 8) | j, need_space);
+ if(!newPosition.isContainedIn(dest))
+ {
+ dest.push_back(newPosition);
+ count -= need_space;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+ }
+ return EQUIP_ERR_OK;
+}
+
+uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 entry, uint32 count, Item *pItem, bool swap, uint32* no_space_count ) const
+{
+ sLog.outDebug( "STORAGE: CanStoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, entry, count);
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype(entry);
+ if( !pProto )
+ {
+ if(no_space_count)
+ *no_space_count = count;
+ return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND;
+ }
+
+ if(pItem && pItem->IsBindedNotWith(GetGUID()))
+ {
+ if(no_space_count)
+ *no_space_count = count;
+ return EQUIP_ERR_DONT_OWN_THAT_ITEM;
+ }
+
+ // check count of items (skip for auto move for same player from bank)
+ uint32 no_similar_count = 0; // can't store this amount similar items
+ uint8 res = _CanTakeMoreSimilarItems(entry,count,pItem,&no_similar_count);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(count==no_similar_count)
+ {
+ if(no_space_count)
+ *no_space_count = no_similar_count;
+ return res;
+ }
+ count -= no_similar_count;
+ }
+
+ // in specific slot
+ if( bag != NULL_BAG && slot != NULL_SLOT )
+ {
+ res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+
+ // not specific slot or have spece for partly store only in specific slot
+
+ // in specific bag
+ if( bag != NULL_BAG )
+ {
+ // search stack in bag for merge to
+ if( pProto->Stackable > 1 )
+ {
+ if( bag == INVENTORY_SLOT_BAG_0 ) // inventory
+ {
+ res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ else // equipped bag
+ {
+ // we need check 2 time (specilized/non_specialized), use NULL_BAG to prevent skipping bag
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,true,false,pItem,NULL_BAG,slot);
+ if(res!=EQUIP_ERR_OK)
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,true,true,pItem,NULL_BAG,slot);
+
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ }
+
+ // search free slot in bag for place to
+ if( bag == INVENTORY_SLOT_BAG_0 ) // inventory
+ {
+ // search free slot - keyring case
+ if(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)
+ {
+ uint32 keyringSize = GetMaxKeyringSize();
+ res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_START+keyringSize,dest,pProto,count,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+
+ res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ else // equipped bag
+ {
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,false,false,pItem,NULL_BAG,slot);
+ if(res!=EQUIP_ERR_OK)
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,false,true,pItem,NULL_BAG,slot);
+
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ }
+
+ // not specific bag or have space for partly store only in specific bag
+
+ // search stack for merge to
+ if( pProto->Stackable > 1 )
+ {
+ res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ if( pProto->BagFamily )
+ {
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,true,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ }
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,true,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ }
+
+ // search free slot - special bag case
+ if( pProto->BagFamily )
+ {
+ if(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)
+ {
+ uint32 keyringSize = GetMaxKeyringSize();
+ res = _CanStoreItem_InInventorySlots(KEYRING_SLOT_START,KEYRING_SLOT_START+keyringSize,dest,pProto,count,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,false,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+ }
+
+ // search free slot
+ res = _CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START,INVENTORY_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ {
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return res;
+ }
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,false,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ {
+ if(no_similar_count==0)
+ return EQUIP_ERR_OK;
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+ return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
+ }
+ }
+
+ if(no_space_count)
+ *no_space_count = count + no_similar_count;
+
+ return EQUIP_ERR_INVENTORY_FULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint8 Player::CanStoreItems( Item **pItems,int count) const
+{
+ Item *pItem2;
+
+ // fill space table
+ int inv_slot_items[INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START];
+ int inv_bags[INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE];
+ int inv_keys[KEYRING_SLOT_END-KEYRING_SLOT_START];
+
+ memset(inv_slot_items,0,sizeof(int)*(INVENTORY_SLOT_ITEM_END-INVENTORY_SLOT_ITEM_START));
+ memset(inv_bags,0,sizeof(int)*(INVENTORY_SLOT_BAG_END-INVENTORY_SLOT_BAG_START)*MAX_BAG_SIZE);
+ memset(inv_keys,0,sizeof(int)*(KEYRING_SLOT_END-KEYRING_SLOT_START));
+
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+
+ if (pItem2 && !pItem2->IsInTrade())
+ {
+ inv_slot_items[i-INVENTORY_SLOT_ITEM_START] = pItem2->GetCount();
+ }
+ }
+
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+
+ if (pItem2 && !pItem2->IsInTrade())
+ {
+ inv_keys[i-KEYRING_SLOT_START] = pItem2->GetCount();
+ }
+ }
+
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag *pBag;
+ ItemPrototype const *pBagProto;
+
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ pItem2 = GetItemByPos( i, j );
+ if (pItem2 && !pItem2->IsInTrade())
+ {
+ inv_bags[i-INVENTORY_SLOT_BAG_START][j] = pItem2->GetCount();
+ }
+ }
+ }
+ }
+ }
+
+ // check free space for all items
+ for (int k=0;k<count;k++)
+ {
+ Item *pItem = pItems[k];
+
+ // no item
+ if (!pItem) continue;
+
+ sLog.outDebug( "STORAGE: CanStoreItems %i. item = %u, count = %u", k+1, pItem->GetEntry(), pItem->GetCount());
+ ItemPrototype const *pProto = pItem->GetProto();
+
+ // strange item
+ if( !pProto )
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+
+ // item it 'bind'
+ if(pItem->IsBindedNotWith(GetGUID()))
+ return EQUIP_ERR_DONT_OWN_THAT_ITEM;
+
+ Bag *pBag;
+ ItemPrototype const *pBagProto;
+
+ // item is 'one item only'
+ uint8 res = CanTakeMoreSimilarItems(pItem);
+ if(res != EQUIP_ERR_OK)
+ return res;
+
+ // search stack for merge to
+ if( pProto->Stackable > 1 )
+ {
+ bool b_found = false;
+
+ for(int t = KEYRING_SLOT_START; t < KEYRING_SLOT_END; t++)
+ {
+ pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
+ if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_keys[t-KEYRING_SLOT_START] + pItem->GetCount() <= pProto->Stackable )
+ {
+ inv_keys[t-KEYRING_SLOT_START] += pItem->GetCount();
+ b_found = true;
+ break;
+ }
+ }
+ if (b_found) continue;
+
+ for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; t++)
+ {
+ pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
+ if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_slot_items[t-INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->Stackable )
+ {
+ inv_slot_items[t-INVENTORY_SLOT_ITEM_START] += pItem->GetCount();
+ b_found = true;
+ break;
+ }
+ }
+ if (b_found) continue;
+
+ for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ pItem2 = GetItemByPos( t, j );
+ if( pItem2 && pItem2->GetEntry() == pItem->GetEntry() && inv_bags[t-INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->Stackable )
+ {
+ inv_bags[t-INVENTORY_SLOT_BAG_START][j] += pItem->GetCount();
+ b_found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (b_found) continue;
+ }
+
+ // special bag case
+ if( pProto->BagFamily )
+ {
+ bool b_found = false;
+ if(pProto->BagFamily & BAG_FAMILY_MASK_KEYS)
+ {
+ uint32 keyringSize = GetMaxKeyringSize();
+ for(uint32 t = KEYRING_SLOT_START; t < KEYRING_SLOT_START+keyringSize; ++t)
+ {
+ if( inv_keys[t-KEYRING_SLOT_START] == 0 )
+ {
+ inv_keys[t-KEYRING_SLOT_START] = 1;
+ b_found = true;
+ break;
+ }
+ }
+ }
+
+ if (b_found) continue;
+
+ for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+
+ // not plain container check
+ if( pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER) &&
+ ItemCanGoIntoBag(pProto,pBagProto) )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 )
+ {
+ inv_bags[t-INVENTORY_SLOT_BAG_START][j] = 1;
+ b_found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (b_found) continue;
+ }
+
+ // search free slot
+ bool b_found = false;
+ for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; t++)
+ {
+ if( inv_slot_items[t-INVENTORY_SLOT_ITEM_START] == 0 )
+ {
+ inv_slot_items[t-INVENTORY_SLOT_ITEM_START] = 1;
+ b_found = true;
+ break;
+ }
+ }
+ if (b_found) continue;
+
+ // search free slot in bags
+ for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; t++)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+ if( pBagProto && ItemCanGoIntoBag(pProto,pBagProto))
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 )
+ {
+ inv_bags[t-INVENTORY_SLOT_BAG_START][j] = 1;
+ b_found = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // no free slot found?
+ if (!b_found)
+ return EQUIP_ERR_INVENTORY_FULL;
+ }
+
+ return EQUIP_ERR_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint8 Player::CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, uint32 count, bool swap ) const
+{
+ dest = 0;
+ Item *pItem = Item::CreateItem( item, count, this );
+ if( pItem )
+ {
+ uint8 result = CanEquipItem(slot, dest, pItem, swap );
+ delete pItem;
+ return result;
+ }
+
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+}
+
+uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading ) const
+{
+ dest = 0;
+ if( pItem )
+ {
+ sLog.outDebug( "STORAGE: CanEquipItem slot = %u, item = %u, count = %u", slot, pItem->GetEntry(), pItem->GetCount());
+ 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;
+
+ // check count of items (skip for auto move for same player from bank)
+ uint8 res = CanTakeMoreSimilarItems(pItem);
+ if(res != EQUIP_ERR_OK)
+ return res;
+
+ // do not allow equipping gear except weapons, offhands, projectiles, relics in
+ // - combat
+ // - in-progress arenas
+ if( !pProto->CanChangeEquipStateInCombat() )
+ {
+ if( isInCombat() )
+ return EQUIP_ERR_NOT_IN_COMBAT;
+
+ if(BattleGround* bg = GetBattleGround())
+ if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS )
+ return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
+ }
+
+ 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;
+
+ uint8 msg = CanUseItem( pItem , not_loading );
+ if( msg != EQUIP_ERR_OK )
+ return msg;
+ if( !swap && GetItemByPos( INVENTORY_SLOT_BAG_0, eslot ) )
+ return EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE;
+
+ // check unique-equipped on item
+ if (pProto->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED)
+ {
+ // there is an equip limit on this item
+ Item* tItem = GetItemOrItemWithGemEquipped(pProto->ItemId);
+ if (tItem && (!swap || tItem->GetSlot() != eslot ) )
+ return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
+ }
+
+ // check unique-equipped on gems
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ {
+ uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id)
+ continue;
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ continue;
+
+ ItemPrototype const* pGem = objmgr.GetItemPrototype(enchantEntry->GemID);
+ if(pGem && (pGem->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED))
+ {
+ Item* tItem = GetItemOrItemWithGemEquipped(enchantEntry->GemID);
+ if(tItem && (!swap || tItem->GetSlot() != eslot ))
+ return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
+ }
+ }
+
+ // check unique-equipped special item classes
+ if (pProto->Class == ITEM_CLASS_QUIVER)
+ {
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
+ {
+ if( Item* pBag = GetItemByPos( INVENTORY_SLOT_BAG_0, i ) )
+ {
+ if( ItemPrototype const* pBagProto = pBag->GetProto() )
+ {
+ if( pBagProto->Class==pProto->Class && pBagProto->SubClass==pProto->SubClass &&
+ (!swap || pBag->GetSlot() != eslot ) )
+ {
+ if(pBagProto->SubClass == ITEM_SUBCLASS_AMMO_POUCH)
+ return EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH;
+ else
+ return EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER;
+ }
+ }
+ }
+ }
+ }
+
+ uint32 type = pProto->InventoryType;
+
+ if(eslot == EQUIPMENT_SLOT_OFFHAND)
+ {
+ if( type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND )
+ {
+ if(!CanDualWield())
+ return EQUIP_ERR_CANT_DUAL_WIELD;
+ }
+
+ Item *mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND );
+ if(mainItem)
+ {
+ if(mainItem->GetProto()->InventoryType == INVTYPE_2HWEAPON)
+ return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED;
+ }
+ }
+
+ // equip two-hand weapon case (with possible unequip 2 items)
+ if( type == INVTYPE_2HWEAPON )
+ {
+ if(eslot != EQUIPMENT_SLOT_MAINHAND)
+ return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
+
+ // offhand item must can be stored in inventitory for offhand item and it also must be unequipped
+ Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND );
+ ItemPosCountVec off_dest;
+ if( offItem && (!not_loading ||
+ CanUnequipItem(uint16(INVENTORY_SLOT_BAG_0) << 8 | EQUIPMENT_SLOT_OFFHAND,false) != EQUIP_ERR_OK ||
+ CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false ) != EQUIP_ERR_OK ) )
+ return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_INVENTORY_FULL;
+ }
+ dest = ((INVENTORY_SLOT_BAG_0 << 8) | eslot);
+ return EQUIP_ERR_OK;
+ }
+ }
+ if( !swap )
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+ else
+ return EQUIP_ERR_ITEMS_CANT_BE_SWAPPED;
+}
+
+uint8 Player::CanUnequipItem( uint16 pos, bool swap ) const
+{
+ // Applied only to equipped items and bank bags
+ if(!IsEquipmentPos(pos) && !IsBagPos(pos))
+ return EQUIP_ERR_OK;
+
+ Item* pItem = GetItemByPos(pos);
+
+ // Applied only to existed equipped item
+ if( !pItem )
+ return EQUIP_ERR_OK;
+
+ sLog.outDebug( "STORAGE: CanUnequipItem slot = %u, item = %u, count = %u", pos, pItem->GetEntry(), pItem->GetCount());
+
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( !pProto )
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+
+ // do not allow unequipping gear except weapons, offhands, projectiles, relics in
+ // - combat
+ // - in-progress arenas
+ if( !pProto->CanChangeEquipStateInCombat() )
+ {
+ if( isInCombat() )
+ return EQUIP_ERR_NOT_IN_COMBAT;
+
+ if(BattleGround* bg = GetBattleGround())
+ if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS )
+ return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
+ }
+
+ if(!swap && pItem->IsBag() && !((Bag*)pItem)->IsEmpty())
+ return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS;
+
+ return EQUIP_ERR_OK;
+}
+
+uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pItem, bool swap, bool not_loading ) const
+{
+ if( !pItem )
+ return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
+
+ uint32 count = pItem->GetCount();
+
+ sLog.outDebug( "STORAGE: CanBankItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), pItem->GetCount());
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( !pProto )
+ return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND;
+
+ if( pItem->IsBindedNotWith(GetGUID()) )
+ return EQUIP_ERR_DONT_OWN_THAT_ITEM;
+
+ // check count of items (skip for auto move for same player from bank)
+ uint8 res = CanTakeMoreSimilarItems(pItem);
+ if(res != EQUIP_ERR_OK)
+ return res;
+
+ // in specific slot
+ if( bag != NULL_BAG && slot != NULL_SLOT )
+ {
+ if( pProto->InventoryType == INVTYPE_BAG )
+ {
+ Bag *pBag = (Bag*)pItem;
+ if( pBag )
+ {
+ if( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END )
+ {
+ if( !HasBankBagSlot( slot ) )
+ return EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT;
+ if( uint8 cantuse = CanUseItem( pItem, not_loading ) != EQUIP_ERR_OK )
+ return cantuse;
+ }
+ else
+ {
+ if( !pBag->IsEmpty() )
+ return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG;
+ }
+ }
+ }
+ else
+ {
+ if( slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END )
+ return EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT;
+ }
+
+ res = _CanStoreItem_InSpecificSlot(bag,slot,dest,pProto,count,swap,pItem);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+
+ // not specific slot or have spece for partly store only in specific slot
+
+ // in specific bag
+ if( bag != NULL_BAG )
+ {
+ if( pProto->InventoryType == INVTYPE_BAG )
+ {
+ Bag *pBag = (Bag*)pItem;
+ if( pBag && !pBag->IsEmpty() )
+ return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG;
+ }
+
+ // search stack in bag for merge to
+ if( pProto->Stackable > 1 )
+ {
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+ res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ else
+ {
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,true,false,pItem,NULL_BAG,slot);
+ if(res!=EQUIP_ERR_OK)
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,true,true,pItem,NULL_BAG,slot);
+
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+
+ // search free slot in bag
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+ res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ else
+ {
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,false,false,pItem,NULL_BAG,slot);
+ if(res!=EQUIP_ERR_OK)
+ res = _CanStoreItem_InBag(bag,dest,pProto,count,false,true,pItem,NULL_BAG,slot);
+
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+
+ // not specific bag or have spece for partly store only in specific bag
+
+ // search stack for merge to
+ if( pProto->Stackable > 1 )
+ {
+ // in slots
+ res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+
+ // in special bags
+ if( pProto->BagFamily )
+ {
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,true,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,true,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+
+ // search free place in special bag
+ if( pProto->BagFamily )
+ {
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,false,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ }
+
+ // search free space
+ res = _CanStoreItem_InInventorySlots(BANK_SLOT_ITEM_START,BANK_SLOT_ITEM_END,dest,pProto,count,false,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ return res;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+
+ for(int i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
+ {
+ res = _CanStoreItem_InBag(i,dest,pProto,count,false,true,pItem,bag,slot);
+ if(res!=EQUIP_ERR_OK)
+ continue;
+
+ if(count==0)
+ return EQUIP_ERR_OK;
+ }
+ return EQUIP_ERR_BANK_FULL;
+}
+
+uint8 Player::CanUseItem( Item *pItem, bool not_loading ) const
+{
+ if( pItem )
+ {
+ sLog.outDebug( "STORAGE: CanUseItem item = %u", pItem->GetEntry());
+ if( !isAlive() && not_loading )
+ return EQUIP_ERR_YOU_ARE_DEAD;
+ //if( isStunned() )
+ // return EQUIP_ERR_YOU_ARE_STUNNED;
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( pProto )
+ {
+ if( pItem->IsBindedNotWith(GetGUID()) )
+ return EQUIP_ERR_DONT_OWN_THAT_ITEM;
+ if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
+ return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
+ if( pItem->GetSkill() != 0 )
+ {
+ if( GetSkillValue( pItem->GetSkill() ) == 0 )
+ return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
+ }
+ if( pProto->RequiredSkill != 0 )
+ {
+ if( GetSkillValue( pProto->RequiredSkill ) == 0 )
+ return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
+ else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
+ return EQUIP_ERR_ERR_CANT_EQUIP_SKILL;
+ }
+ if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
+ return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
+ if( pProto->RequiredReputationFaction && uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank )
+ return EQUIP_ERR_CANT_EQUIP_REPUTATION;
+ if( getLevel() < pProto->RequiredLevel )
+ return EQUIP_ERR_CANT_EQUIP_LEVEL_I;
+ return EQUIP_ERR_OK;
+ }
+ }
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+}
+
+bool Player::CanUseItem( ItemPrototype const *pProto )
+{
+ // Used by group, function NeedBeforeGreed, to know if a prototype can be used by a player
+
+ if( pProto )
+ {
+ if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
+ return false;
+ if( pProto->RequiredSkill != 0 )
+ {
+ if( GetSkillValue( pProto->RequiredSkill ) == 0 )
+ return false;
+ else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
+ return false;
+ }
+ if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
+ return false;
+ if( getLevel() < pProto->RequiredLevel )
+ return false;
+ return true;
+ }
+ return false;
+}
+
+uint8 Player::CanUseAmmo( uint32 item ) const
+{
+ sLog.outDebug( "STORAGE: CanUseAmmo item = %u", item);
+ if( !isAlive() )
+ return EQUIP_ERR_YOU_ARE_DEAD;
+ //if( isStunned() )
+ // return EQUIP_ERR_YOU_ARE_STUNNED;
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
+ if( pProto )
+ {
+ if( pProto->InventoryType!= INVTYPE_AMMO )
+ return EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE;
+ if( (pProto->AllowableClass & getClassMask()) == 0 || (pProto->AllowableRace & getRaceMask()) == 0 )
+ return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
+ if( pProto->RequiredSkill != 0 )
+ {
+ if( GetSkillValue( pProto->RequiredSkill ) == 0 )
+ return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
+ else if( GetSkillValue( pProto->RequiredSkill ) < pProto->RequiredSkillRank )
+ return EQUIP_ERR_ERR_CANT_EQUIP_SKILL;
+ }
+ if( pProto->RequiredSpell != 0 && !HasSpell( pProto->RequiredSpell ) )
+ return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
+ /*if( GetReputation() < pProto->RequiredReputation )
+ return EQUIP_ERR_CANT_EQUIP_REPUTATION;
+ */
+ if( getLevel() < pProto->RequiredLevel )
+ return EQUIP_ERR_CANT_EQUIP_LEVEL_I;
+
+ // Requires No Ammo
+ if(GetDummyAura(46699))
+ return EQUIP_ERR_BAG_FULL6;
+
+ return EQUIP_ERR_OK;
+ }
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+}
+
+void Player::SetAmmo( uint32 item )
+{
+ if(!item)
+ return;
+
+ // already set
+ if( GetUInt32Value(PLAYER_AMMO_ID) == item )
+ return;
+
+ // check ammo
+ if(item)
+ {
+ uint8 msg = CanUseAmmo( item );
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, NULL, NULL );
+ return;
+ }
+ }
+
+ SetUInt32Value(PLAYER_AMMO_ID, item);
+
+ _ApplyAmmoBonuses();
+}
+
+void Player::RemoveAmmo()
+{
+ SetUInt32Value(PLAYER_AMMO_ID, 0);
+
+ m_ammoDPS = 0.0f;
+
+ if(CanModifyStats())
+ UpdateDamagePhysical(RANGED_ATTACK);
+}
+
+// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
+Item* Player::StoreNewItem( ItemPosCountVec const& dest, uint32 item, bool update,int32 randomPropertyId )
+{
+ uint32 count = 0;
+ for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); ++itr)
+ count += itr->count;
+
+ Item *pItem = Item::CreateItem( item, count, this );
+ if( pItem )
+ {
+ ItemAddedQuestCheck( item, count );
+ if(randomPropertyId)
+ pItem->SetItemRandomProperties(randomPropertyId);
+ pItem = StoreItem( dest, pItem, update );
+ }
+ return pItem;
+}
+
+Item* Player::StoreItem( ItemPosCountVec const& dest, Item* pItem, bool update )
+{
+ if( !pItem )
+ return NULL;
+
+ Item* lastItem = pItem;
+
+ for(ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end(); )
+ {
+ uint16 pos = itr->pos;
+ uint32 count = itr->count;
+
+ ++itr;
+
+ if(itr == dest.end())
+ {
+ lastItem = _StoreItem(pos,pItem,count,false,update);
+ break;
+ }
+
+ lastItem = _StoreItem(pos,pItem,count,true,update);
+ }
+
+ return lastItem;
+}
+
+// Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case.
+Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, bool update )
+{
+ if( !pItem )
+ return NULL;
+
+ uint8 bag = pos >> 8;
+ uint8 slot = pos & 255;
+
+ sLog.outDebug( "STORAGE: StoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, pItem->GetEntry(), count);
+
+ Item *pItem2 = GetItemByPos( bag, slot );
+
+ if( !pItem2 )
+ {
+ if(clone)
+ pItem = pItem->CloneItem(count,this);
+ else
+ pItem->SetCount(count);
+
+ if(!pItem)
+ return NULL;
+
+ if( pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP ||
+ pItem->GetProto()->Bonding == BIND_QUEST_ITEM ||
+ pItem->GetProto()->Bonding == BIND_WHEN_EQUIPED && IsBagPos(pos) )
+ pItem->SetBinding( true );
+
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+ m_items[slot] = pItem;
+ SetUInt64Value( (uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2) ), pItem->GetGUID() );
+ pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, GetGUID() );
+ pItem->SetUInt64Value( ITEM_FIELD_OWNER, GetGUID() );
+
+ pItem->SetSlot( slot );
+ pItem->SetContainer( NULL );
+
+ if( IsInWorld() && update )
+ {
+ pItem->AddToWorld();
+ pItem->SendUpdateToPlayer( this );
+ }
+
+ pItem->SetState(ITEM_CHANGED, this);
+ }
+ else
+ {
+ Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
+ if( pBag )
+ {
+ pBag->StoreItem( slot, pItem, update );
+ if( IsInWorld() && update )
+ {
+ pItem->AddToWorld();
+ pItem->SendUpdateToPlayer( this );
+ }
+ pItem->SetState(ITEM_CHANGED, this);
+ pBag->SetState(ITEM_CHANGED, this);
+ }
+ }
+
+ AddEnchantmentDurations(pItem);
+ AddItemDurations(pItem);
+
+ return pItem;
+ }
+ else
+ {
+ if( pItem2->GetProto()->Bonding == BIND_WHEN_PICKED_UP ||
+ pItem2->GetProto()->Bonding == BIND_QUEST_ITEM ||
+ pItem2->GetProto()->Bonding == BIND_WHEN_EQUIPED && IsBagPos(pos) )
+ pItem2->SetBinding( true );
+
+ pItem2->SetCount( pItem2->GetCount() + count );
+ if( IsInWorld() && update )
+ pItem2->SendUpdateToPlayer( this );
+
+ if(!clone)
+ {
+ // delete item (it not in any slot currently)
+ if( IsInWorld() && update )
+ {
+ pItem->RemoveFromWorld();
+ pItem->DestroyForPlayer( this );
+ }
+
+ RemoveEnchantmentDurations(pItem);
+ RemoveItemDurations(pItem);
+
+ pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
+ pItem->SetState(ITEM_REMOVED, this);
+ }
+ // AddItemDurations(pItem2); - pItem2 already have duration listed for player
+ AddEnchantmentDurations(pItem2);
+
+ pItem2->SetState(ITEM_CHANGED, this);
+
+ return pItem2;
+ }
+}
+
+Item* Player::EquipNewItem( uint16 pos, uint32 item, uint32 count, bool update )
+{
+ Item *pItem = Item::CreateItem( item, count, this );
+ if( pItem )
+ {
+ ItemAddedQuestCheck( item, count );
+ Item * retItem = EquipItem( pos, pItem, update );
+
+ return retItem;
+ }
+ return NULL;
+}
+
+Item* Player::EquipItem( uint16 pos, Item *pItem, bool update )
+{
+ if( pItem )
+ {
+ AddEnchantmentDurations(pItem);
+ AddItemDurations(pItem);
+
+ uint8 bag = pos >> 8;
+ uint8 slot = pos & 255;
+
+ Item *pItem2 = GetItemByPos( bag, slot );
+
+ if( !pItem2 )
+ {
+ VisualizeItem( slot, pItem);
+
+ if(isAlive())
+ {
+ ItemPrototype const *pProto = pItem->GetProto();
+
+ // item set bonuses applied only at equip and removed at unequip, and still active for broken items
+ if(pProto && pProto->ItemSet)
+ AddItemsSetItem(this,pItem);
+
+ _ApplyItemMods(pItem, slot, true);
+
+ if(pProto && isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer == 0)
+ {
+ m_weaponChangeTimer = DEFAULT_SWITCH_WEAPON;
+ if (getClass() == CLASS_ROGUE)
+ m_weaponChangeTimer = ROGUE_SWITCH_WEAPON;
+ }
+ }
+
+ if( IsInWorld() && update )
+ {
+ pItem->AddToWorld();
+ pItem->SendUpdateToPlayer( this );
+ }
+
+ ApplyEquipCooldown(pItem);
+
+ if( slot == EQUIPMENT_SLOT_MAINHAND )
+ UpdateExpertise(BASE_ATTACK);
+ else if( slot == EQUIPMENT_SLOT_OFFHAND )
+ UpdateExpertise(OFF_ATTACK);
+ }
+ else
+ {
+ pItem2->SetCount( pItem2->GetCount() + pItem->GetCount() );
+ if( IsInWorld() && update )
+ pItem2->SendUpdateToPlayer( this );
+
+ // delete item (it not in any slot currently)
+ //pItem->DeleteFromDB();
+ if( IsInWorld() && update )
+ {
+ pItem->RemoveFromWorld();
+ pItem->DestroyForPlayer( this );
+ }
+
+ RemoveEnchantmentDurations(pItem);
+ RemoveItemDurations(pItem);
+
+ pItem->SetOwnerGUID(GetGUID()); // prevent error at next SetState in case trade/mail/buy from vendor
+ pItem->SetState(ITEM_REMOVED, this);
+ pItem2->SetState(ITEM_CHANGED, this);
+
+ ApplyEquipCooldown(pItem2);
+
+ return pItem2;
+ }
+ }
+
+ return pItem;
+}
+
+void Player::QuickEquipItem( uint16 pos, Item *pItem)
+{
+ if( pItem )
+ {
+ AddEnchantmentDurations(pItem);
+ AddItemDurations(pItem);
+
+ uint8 slot = pos & 255;
+ VisualizeItem( slot, pItem);
+
+ if( IsInWorld() )
+ {
+ pItem->AddToWorld();
+ pItem->SendUpdateToPlayer( this );
+ }
+ }
+}
+
+void Player::SetVisibleItemSlot(uint8 slot, Item *pItem)
+{
+ // PLAYER_VISIBLE_ITEM_i_CREATOR // Size: 2
+ // PLAYER_VISIBLE_ITEM_i_0 // Size: 12
+ // entry // Size: 1
+ // inspected enchantments // Size: 6
+ // ? // Size: 5
+ // PLAYER_VISIBLE_ITEM_i_PROPERTIES // Size: 1 (property,suffix factor)
+ // PLAYER_VISIBLE_ITEM_i_PAD // Size: 1
+ // // = 16
+
+ if(pItem)
+ {
+ SetUInt64Value(PLAYER_VISIBLE_ITEM_1_CREATOR + (slot * MAX_VISIBLE_ITEM_OFFSET), pItem->GetUInt64Value(ITEM_FIELD_CREATOR));
+
+ int VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET);
+ SetUInt32Value(VisibleBase + 0, pItem->GetEntry());
+
+ for(int i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i)
+ SetUInt32Value(VisibleBase + 1 + i, pItem->GetEnchantmentId(EnchantmentSlot(i)));
+
+ // Use SetInt16Value to prevent set high part to FFFF for negative value
+ SetInt16Value( PLAYER_VISIBLE_ITEM_1_PROPERTIES + (slot * MAX_VISIBLE_ITEM_OFFSET), 0, pItem->GetItemRandomPropertyId());
+ SetUInt32Value(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (slot * MAX_VISIBLE_ITEM_OFFSET), pItem->GetItemSuffixFactor());
+ }
+ else
+ {
+ SetUInt64Value(PLAYER_VISIBLE_ITEM_1_CREATOR + (slot * MAX_VISIBLE_ITEM_OFFSET), 0);
+
+ int VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + (slot * MAX_VISIBLE_ITEM_OFFSET);
+ SetUInt32Value(VisibleBase + 0, 0);
+
+ for(int i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i)
+ SetUInt32Value(VisibleBase + 1 + i, 0);
+
+ SetUInt32Value(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 0 + (slot * MAX_VISIBLE_ITEM_OFFSET), 0);
+ SetUInt32Value(PLAYER_VISIBLE_ITEM_1_PROPERTIES + 1 + (slot * MAX_VISIBLE_ITEM_OFFSET), 0);
+ }
+}
+
+void Player::VisualizeItem( uint8 slot, Item *pItem)
+{
+ if(!pItem)
+ return;
+
+ // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory)
+ if( pItem->GetProto()->Bonding == BIND_WHEN_EQUIPED || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM )
+ pItem->SetBinding( true );
+
+ sLog.outDebug( "STORAGE: EquipItem slot = %u, item = %u", slot, pItem->GetEntry());
+
+ m_items[slot] = pItem;
+ SetUInt64Value( (uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2) ), pItem->GetGUID() );
+ pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, GetGUID() );
+ pItem->SetUInt64Value( ITEM_FIELD_OWNER, GetGUID() );
+ pItem->SetSlot( slot );
+ pItem->SetContainer( NULL );
+
+ if( slot < EQUIPMENT_SLOT_END )
+ SetVisibleItemSlot(slot,pItem);
+
+ pItem->SetState(ITEM_CHANGED, this);
+}
+
+void Player::RemoveItem( uint8 bag, uint8 slot, bool update )
+{
+ // note: removeitem does not actually change the item
+ // it only takes the item out of storage temporarily
+ // note2: if removeitem is to be used for delinking
+ // the item must be removed from the player's updatequeue
+
+ Item *pItem = GetItemByPos( bag, slot );
+ if( pItem )
+ {
+ sLog.outDebug( "STORAGE: RemoveItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
+
+ RemoveEnchantmentDurations(pItem);
+ RemoveItemDurations(pItem);
+
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+ if ( slot < INVENTORY_SLOT_BAG_END )
+ {
+ ItemPrototype const *pProto = pItem->GetProto();
+ // item set bonuses applied only at equip and removed at unequip, and still active for broken items
+
+ if(pProto && pProto->ItemSet)
+ RemoveItemsSetItem(this,pProto);
+
+ _ApplyItemMods(pItem, slot, false);
+
+ // remove item dependent auras and casts (only weapon and armor slots)
+ if(slot < EQUIPMENT_SLOT_END)
+ RemoveItemDependentAurasAndCasts(pItem);
+
+ // remove held enchantments
+ if ( slot == EQUIPMENT_SLOT_MAINHAND )
+ {
+ if (pItem->GetItemSuffixFactor())
+ {
+ pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_3);
+ pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_4);
+ }
+ else
+ {
+ pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0);
+ pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1);
+ }
+ }
+ }
+
+ m_items[slot] = NULL;
+ SetUInt64Value((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot*2)), 0);
+
+ if ( slot < EQUIPMENT_SLOT_END )
+ SetVisibleItemSlot(slot,NULL);
+ }
+ else
+ {
+ Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag );
+ if( pBag )
+ pBag->RemoveItem(slot, update);
+ }
+ pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, 0 );
+ // pItem->SetUInt64Value( ITEM_FIELD_OWNER, 0 ); not clear owner at remove (it will be set at store). This used in mail and auction code
+ pItem->SetSlot( NULL_SLOT );
+ if( IsInWorld() && update )
+ pItem->SendUpdateToPlayer( this );
+
+ if( slot == EQUIPMENT_SLOT_MAINHAND )
+ UpdateExpertise(BASE_ATTACK);
+ else if( slot == EQUIPMENT_SLOT_OFFHAND )
+ UpdateExpertise(OFF_ATTACK);
+ }
+}
+
+// Common operation need to remove item from inventory without delete in trade, auction, guild bank, mail....
+void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
+{
+ if(Item* it = GetItemByPos(bag,slot))
+ {
+ ItemRemovedQuestCheck(it->GetEntry(),it->GetCount());
+ RemoveItem( bag,slot,update);
+ it->RemoveFromUpdateQueueOf(this);
+ if(it->IsInWorld())
+ {
+ it->RemoveFromWorld();
+ it->DestroyForPlayer( this );
+ }
+ }
+}
+
+// Common operation need to add item from inventory without delete in trade, guild bank, mail....
+void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB)
+{
+ // update quest counters
+ ItemAddedQuestCheck(pItem->GetEntry(),pItem->GetCount());
+
+ // store item
+ Item* pLastItem = StoreItem( dest, pItem, update);
+
+ // only set if not merged to existed stack (pItem can be deleted already but we can compare pointers any way)
+ if(pLastItem==pItem)
+ {
+ // update owner for last item (this can be original item with wrong owner
+ if(pLastItem->GetOwnerGUID() != GetGUID())
+ pLastItem->SetOwnerGUID(GetGUID());
+
+ // if this original item then it need create record in inventory
+ // in case trade we laready have item in other player inventory
+ pLastItem->SetState(in_characterInventoryDB ? ITEM_CHANGED : ITEM_NEW, this);
+ }
+}
+
+void Player::DestroyItem( uint8 bag, uint8 slot, bool update )
+{
+ Item *pItem = GetItemByPos( bag, slot );
+ if( pItem )
+ {
+ sLog.outDebug( "STORAGE: DestroyItem bag = %u, slot = %u, item = %u", bag, slot, pItem->GetEntry());
+
+ // start from destroy contained items (only equipped bag can have its)
+ if (pItem->IsBag() && pItem->IsEquipped()) // this also prevent infinity loop if empty bag stored in bag==slot
+ {
+ for (int i = 0; i < MAX_BAG_SIZE; i++)
+ DestroyItem(slot,i,update);
+ }
+
+ if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
+ CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow());
+
+ ItemPrototype const *pProto = pItem->GetProto();
+
+ RemoveEnchantmentDurations(pItem);
+ RemoveItemDurations(pItem);
+
+ ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount() );
+
+ if( bag == INVENTORY_SLOT_BAG_0 )
+ {
+
+ SetUInt64Value((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot*2)), 0);
+
+ // equipment and equipped bags can have applied bonuses
+ if ( slot < INVENTORY_SLOT_BAG_END )
+ {
+ ItemPrototype const *pProto = pItem->GetProto();
+
+ // item set bonuses applied only at equip and removed at unequip, and still active for broken items
+ if(pProto && pProto->ItemSet)
+ RemoveItemsSetItem(this,pProto);
+
+ _ApplyItemMods(pItem, slot, false);
+ }
+
+ if ( slot < EQUIPMENT_SLOT_END )
+ {
+ // remove item dependent auras and casts (only weapon and armor slots)
+ RemoveItemDependentAurasAndCasts(pItem);
+
+ // equipment visual show
+ SetVisibleItemSlot(slot,NULL);
+ }
+
+ m_items[slot] = NULL;
+ }
+ else if(Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag ))
+ pBag->RemoveItem(slot, update);
+
+ if( IsInWorld() && update )
+ {
+ pItem->RemoveFromWorld();
+ pItem->DestroyForPlayer(this);
+ }
+
+ //pItem->SetOwnerGUID(0);
+ pItem->SetUInt64Value( ITEM_FIELD_CONTAINED, 0 );
+ pItem->SetSlot( NULL_SLOT );
+ pItem->SetState(ITEM_REMOVED, this);
+ }
+}
+
+void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool unequip_check)
+{
+ sLog.outDebug( "STORAGE: DestroyItemCount item = %u, count = %u", item, count);
+ Item *pItem;
+ ItemPrototype const *pProto;
+ uint32 remcount = 0;
+
+ // in inventory
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ if( pItem->GetCount() + remcount <= count )
+ {
+ // all items in inventory can unequipped
+ remcount += pItem->GetCount();
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
+ if(remcount >=count)
+ return;
+ }
+ else
+ {
+ pProto = pItem->GetProto();
+ ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
+ pItem->SetCount( pItem->GetCount() - count + remcount );
+ if( IsInWorld() & update )
+ pItem->SendUpdateToPlayer( this );
+ pItem->SetState(ITEM_CHANGED, this);
+ return;
+ }
+ }
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ if( pItem->GetCount() + remcount <= count )
+ {
+ // all keys can be unequipped
+ remcount += pItem->GetCount();
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
+ if(remcount >=count)
+ return;
+ }
+ else
+ {
+ pProto = pItem->GetProto();
+ ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
+ pItem->SetCount( pItem->GetCount() - count + remcount );
+ if( IsInWorld() & update )
+ pItem->SendUpdateToPlayer( this );
+ pItem->SetState(ITEM_CHANGED, this);
+ return;
+ }
+ }
+ }
+
+ // in inventory bags
+ Bag *pBag;
+ ItemPrototype const *pBagProto;
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ pItem = pBag->GetItemByPos(j);
+ if( pItem && pItem->GetEntry() == item )
+ {
+ // all items in bags can be unequipped
+ if( pItem->GetCount() + remcount <= count )
+ {
+ remcount += pItem->GetCount();
+ DestroyItem( i, j, update );
+
+ if(remcount >=count)
+ return;
+ }
+ else
+ {
+ pProto = pItem->GetProto();
+ ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
+ pItem->SetCount( pItem->GetCount() - count + remcount );
+ if( IsInWorld() && update )
+ pItem->SendUpdateToPlayer( this );
+ pItem->SetState(ITEM_CHANGED, this);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // in equipment and bag list
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEntry() == item )
+ {
+ if( pItem->GetCount() + remcount <= count )
+ {
+ if(!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i,false) == EQUIP_ERR_OK )
+ {
+ remcount += pItem->GetCount();
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
+ if(remcount >=count)
+ return;
+ }
+ }
+ else
+ {
+ pProto = pItem->GetProto();
+ ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount );
+ pItem->SetCount( pItem->GetCount() - count + remcount );
+ if( IsInWorld() & update )
+ pItem->SendUpdateToPlayer( this );
+ pItem->SetState(ITEM_CHANGED, this);
+ return;
+ }
+ }
+ }
+}
+
+void Player::DestroyZoneLimitedItem( bool update, uint32 new_zone )
+{
+ sLog.outDebug( "STORAGE: DestroyZoneLimitedItem in map %u and area %u", GetMapId(), new_zone );
+
+ // in inventory
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+ }
+ for(int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+ }
+
+ // in inventory bags
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ ItemPrototype const *pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = pBag->GetItemByPos(j);
+ if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
+ DestroyItem( i, j, update);
+ }
+ }
+ }
+ }
+
+ // in equipment and bag list
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) )
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+ }
+}
+
+void Player::DestroyConjuredItems( bool update )
+{
+ // used when entering arena
+ // distroys all conjured items
+ sLog.outDebug( "STORAGE: DestroyConjuredItems" );
+
+ // in inventory
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetProto() &&
+ (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) &&
+ (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) )
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+ }
+
+ // in inventory bags
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ ItemPrototype const *pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = pBag->GetItemByPos(j);
+ if( pItem && pItem->GetProto() &&
+ (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) &&
+ (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) )
+ DestroyItem( i, j, update);
+ }
+ }
+ }
+ }
+
+ // in equipment and bag list
+ for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetProto() &&
+ (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) &&
+ (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) )
+ DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+ }
+}
+
+void Player::DestroyItemCount( Item* pItem, uint32 &count, bool update )
+{
+ if(!pItem)
+ return;
+
+ sLog.outDebug( "STORAGE: DestroyItemCount item (GUID: %u, Entry: %u) count = %u", pItem->GetGUIDLow(),pItem->GetEntry(), count);
+
+ if( pItem->GetCount() <= count )
+ {
+ count-= pItem->GetCount();
+
+ DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), update);
+ }
+ else
+ {
+ ItemRemovedQuestCheck( pItem->GetEntry(), count);
+ pItem->SetCount( pItem->GetCount() - count );
+ count = 0;
+ if( IsInWorld() & update )
+ pItem->SendUpdateToPlayer( this );
+ pItem->SetState(ITEM_CHANGED, this);
+ }
+}
+
+void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
+{
+ uint8 srcbag = src >> 8;
+ uint8 srcslot = src & 255;
+
+ uint8 dstbag = dst >> 8;
+ uint8 dstslot = dst & 255;
+
+ Item *pSrcItem = GetItemByPos( srcbag, srcslot );
+ if( !pSrcItem )
+ {
+ SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL );
+ return;
+ }
+
+ // not let split all items (can be only at cheating)
+ if(pSrcItem->GetCount() == count)
+ {
+ SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL );
+ return;
+ }
+
+ // not let split more existed items (can be only at cheating)
+ if(pSrcItem->GetCount() < count)
+ {
+ SendEquipError( EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT, pSrcItem, NULL );
+ return;
+ }
+
+ if(pSrcItem->m_lootGenerated) // prevent split looting item (item
+ {
+ //best error message found for attempting to split while looting
+ SendEquipError( EQUIP_ERR_COULDNT_SPLIT_ITEMS, pSrcItem, NULL );
+ return;
+ }
+
+ sLog.outDebug( "STORAGE: SplitItem bag = %u, slot = %u, item = %u, count = %u", dstbag, dstslot, pSrcItem->GetEntry(), count);
+ Item *pNewItem = pSrcItem->CloneItem( count, this );
+ if( !pNewItem )
+ {
+ SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pSrcItem, NULL );
+ return;
+ }
+
+ if( IsInventoryPos( dst ) )
+ {
+ // change item amount before check (for unique max count check)
+ pSrcItem->SetCount( pSrcItem->GetCount() - count );
+
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreItem( dstbag, dstslot, dest, pNewItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ delete pNewItem;
+ pSrcItem->SetCount( pSrcItem->GetCount() + count );
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ if( IsInWorld() )
+ pSrcItem->SendUpdateToPlayer( this );
+ pSrcItem->SetState(ITEM_CHANGED, this);
+ StoreItem( dest, pNewItem, true);
+ }
+ else if( IsBankPos ( dst ) )
+ {
+ // change item amount before check (for unique max count check)
+ pSrcItem->SetCount( pSrcItem->GetCount() - count );
+
+ ItemPosCountVec dest;
+ uint8 msg = CanBankItem( dstbag, dstslot, dest, pNewItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ delete pNewItem;
+ pSrcItem->SetCount( pSrcItem->GetCount() + count );
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ if( IsInWorld() )
+ pSrcItem->SendUpdateToPlayer( this );
+ pSrcItem->SetState(ITEM_CHANGED, this);
+ BankItem( dest, pNewItem, true);
+ }
+ else if( IsEquipmentPos ( dst ) )
+ {
+ // change item amount before check (for unique max count check), provide space for splitted items
+ pSrcItem->SetCount( pSrcItem->GetCount() - count );
+
+ uint16 dest;
+ uint8 msg = CanEquipItem( dstslot, dest, pNewItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ delete pNewItem;
+ pSrcItem->SetCount( pSrcItem->GetCount() + count );
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ if( IsInWorld() )
+ pSrcItem->SendUpdateToPlayer( this );
+ pSrcItem->SetState(ITEM_CHANGED, this);
+ EquipItem( dest, pNewItem, true);
+ AutoUnequipOffhandIfNeed();
+ }
+}
+
+void Player::SwapItem( uint16 src, uint16 dst )
+{
+ uint8 srcbag = src >> 8;
+ uint8 srcslot = src & 255;
+
+ uint8 dstbag = dst >> 8;
+ uint8 dstslot = dst & 255;
+
+ Item *pSrcItem = GetItemByPos( srcbag, srcslot );
+ Item *pDstItem = GetItemByPos( dstbag, dstslot );
+
+ if( !pSrcItem )
+ return;
+
+ sLog.outDebug( "STORAGE: SwapItem bag = %u, slot = %u, item = %u", dstbag, dstslot, pSrcItem->GetEntry());
+
+ if(!isAlive() )
+ {
+ SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, pSrcItem, pDstItem );
+ return;
+ }
+
+ if(pSrcItem->m_lootGenerated) // prevent swap looting item
+ {
+ //best error message found for attempting to swap while looting
+ SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL );
+ return;
+ }
+
+ // check unequip potability for equipped items and bank bags
+ if(IsEquipmentPos ( src ) || IsBagPos ( src ))
+ {
+ // bags can be swapped with empty bag slots
+ uint8 msg = CanUnequipItem( src, !IsBagPos ( src ) || IsBagPos ( dst ));
+ if(msg != EQUIP_ERR_OK)
+ {
+ SendEquipError( msg, pSrcItem, pDstItem );
+ return;
+ }
+ }
+
+ // prevent put equipped/bank bag in self
+ if( IsBagPos ( src ) && srcslot == dstbag)
+ {
+ SendEquipError( EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem );
+ return;
+ }
+
+ if( !pDstItem )
+ {
+ if( IsInventoryPos( dst ) )
+ {
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreItem( dstbag, dstslot, dest, pSrcItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ RemoveItem(srcbag, srcslot, true);
+ StoreItem( dest, pSrcItem, true);
+ }
+ else if( IsBankPos ( dst ) )
+ {
+ ItemPosCountVec dest;
+ uint8 msg = CanBankItem( dstbag, dstslot, dest, pSrcItem, false);
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ RemoveItem(srcbag, srcslot, true);
+ BankItem( dest, pSrcItem, true);
+ }
+ else if( IsEquipmentPos ( dst ) )
+ {
+ uint16 dest;
+ uint8 msg = CanEquipItem( dstslot, dest, pSrcItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ RemoveItem(srcbag, srcslot, true);
+ EquipItem( dest, pSrcItem, true);
+ AutoUnequipOffhandIfNeed();
+ }
+ }
+ else // if (!pDstItem)
+ {
+ if(pDstItem->m_lootGenerated) // prevent swap looting item
+ {
+ //best error message found for attempting to swap while looting
+ SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pDstItem, NULL );
+ return;
+ }
+
+ // check unequip potability for equipped items and bank bags
+ if(IsEquipmentPos ( dst ) || IsBagPos ( dst ))
+ {
+ // bags can be swapped with empty bag slots
+ uint8 msg = CanUnequipItem( dst, !IsBagPos ( dst ) || IsBagPos ( src ) );
+ if(msg != EQUIP_ERR_OK)
+ {
+ SendEquipError( msg, pSrcItem, pDstItem );
+ return;
+ }
+ }
+
+ // attempt merge to / fill target item
+ {
+ uint8 msg;
+ ItemPosCountVec sDest;
+ uint16 eDest;
+ if( IsInventoryPos( dst ) )
+ msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, false );
+ else if( IsBankPos ( dst ) )
+ msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, false );
+ else if( IsEquipmentPos ( dst ) )
+ msg = CanEquipItem( dstslot, eDest, pSrcItem, false );
+ else
+ return;
+
+ // can be merge/fill
+ if(msg == EQUIP_ERR_OK)
+ {
+ if( pSrcItem->GetCount() + pDstItem->GetCount() <= pSrcItem->GetProto()->Stackable )
+ {
+ RemoveItem(srcbag, srcslot, true);
+
+ if( IsInventoryPos( dst ) )
+ StoreItem( sDest, pSrcItem, true);
+ else if( IsBankPos ( dst ) )
+ BankItem( sDest, pSrcItem, true);
+ else if( IsEquipmentPos ( dst ) )
+ {
+ EquipItem( eDest, pSrcItem, true);
+ AutoUnequipOffhandIfNeed();
+ }
+ }
+ else
+ {
+ pSrcItem->SetCount( pSrcItem->GetCount() + pDstItem->GetCount() - pSrcItem->GetProto()->Stackable );
+ pDstItem->SetCount( pSrcItem->GetProto()->Stackable );
+ pSrcItem->SetState(ITEM_CHANGED, this);
+ pDstItem->SetState(ITEM_CHANGED, this);
+ if( IsInWorld() )
+ {
+ pSrcItem->SendUpdateToPlayer( this );
+ pDstItem->SendUpdateToPlayer( this );
+ }
+ }
+ return;
+ }
+ }
+
+ // impossible merge/fill, do real swap
+ uint8 msg;
+
+ // check src->dest move possibility
+ ItemPosCountVec sDest;
+ uint16 eDest;
+ if( IsInventoryPos( dst ) )
+ msg = CanStoreItem( dstbag, dstslot, sDest, pSrcItem, true );
+ else if( IsBankPos( dst ) )
+ msg = CanBankItem( dstbag, dstslot, sDest, pSrcItem, true );
+ else if( IsEquipmentPos( dst ) )
+ {
+ msg = CanEquipItem( dstslot, eDest, pSrcItem, true );
+ if( msg == EQUIP_ERR_OK )
+ msg = CanUnequipItem( eDest, true );
+ }
+
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, pSrcItem, pDstItem );
+ return;
+ }
+
+ // check dest->src move possibility
+ ItemPosCountVec sDest2;
+ uint16 eDest2;
+ if( IsInventoryPos( src ) )
+ msg = CanStoreItem( srcbag, srcslot, sDest2, pDstItem, true );
+ else if( IsBankPos( src ) )
+ msg = CanBankItem( srcbag, srcslot, sDest2, pDstItem, true );
+ else if( IsEquipmentPos( src ) )
+ {
+ msg = CanEquipItem( srcslot, eDest2, pDstItem, true);
+ if( msg == EQUIP_ERR_OK )
+ msg = CanUnequipItem( eDest2, true);
+ }
+
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, pDstItem, pSrcItem );
+ return;
+ }
+
+ // now do moves, remove...
+ RemoveItem(dstbag, dstslot, false);
+ RemoveItem(srcbag, srcslot, false);
+
+ // add to dest
+ if( IsInventoryPos( dst ) )
+ StoreItem(sDest, pSrcItem, true);
+ else if( IsBankPos( dst ) )
+ BankItem(sDest, pSrcItem, true);
+ else if( IsEquipmentPos( dst ) )
+ EquipItem(eDest, pSrcItem, true);
+
+ // add to src
+ if( IsInventoryPos( src ) )
+ StoreItem(sDest2, pDstItem, true);
+ else if( IsBankPos( src ) )
+ BankItem(sDest2, pDstItem, true);
+ else if( IsEquipmentPos( src ) )
+ EquipItem(eDest2, pDstItem, true);
+
+ AutoUnequipOffhandIfNeed();
+ }
+}
+
+void Player::AddItemToBuyBackSlot( Item *pItem )
+{
+ if( pItem )
+ {
+ uint32 slot = m_currentBuybackSlot;
+ // if current back slot non-empty search oldest or free
+ if(m_items[slot])
+ {
+ uint32 oldest_time = GetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 );
+ uint32 oldest_slot = BUYBACK_SLOT_START;
+
+ for(uint32 i = BUYBACK_SLOT_START+1; i < BUYBACK_SLOT_END; ++i )
+ {
+ // found empty
+ if(!m_items[i])
+ {
+ slot = i;
+ break;
+ }
+
+ uint32 i_time = GetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + i - BUYBACK_SLOT_START);
+
+ if(oldest_time > i_time)
+ {
+ oldest_time = i_time;
+ oldest_slot = i;
+ }
+ }
+
+ // find oldest
+ slot = oldest_slot;
+ }
+
+ RemoveItemFromBuyBackSlot( slot, true );
+ sLog.outDebug( "STORAGE: AddItemToBuyBackSlot item = %u, slot = %u", pItem->GetEntry(), slot);
+
+ m_items[slot] = pItem;
+ time_t base = time(NULL);
+ uint32 etime = uint32(base - m_logintime + (30 * 3600));
+ uint32 eslot = slot - BUYBACK_SLOT_START;
+
+ SetUInt64Value( PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + eslot * 2, pItem->GetGUID() );
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( pProto )
+ SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, pProto->SellPrice * pItem->GetCount() );
+ else
+ SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0 );
+ SetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, (uint32)etime );
+
+ // move to next (for non filled list is move most optimized choice)
+ if(m_currentBuybackSlot < BUYBACK_SLOT_END-1)
+ ++m_currentBuybackSlot;
+ }
+}
+
+Item* Player::GetItemFromBuyBackSlot( uint32 slot )
+{
+ sLog.outDebug( "STORAGE: GetItemFromBuyBackSlot slot = %u", slot);
+ if( slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END )
+ return m_items[slot];
+ return NULL;
+}
+
+void Player::RemoveItemFromBuyBackSlot( uint32 slot, bool del )
+{
+ sLog.outDebug( "STORAGE: RemoveItemFromBuyBackSlot slot = %u", slot);
+ if( slot >= BUYBACK_SLOT_START && slot < BUYBACK_SLOT_END )
+ {
+ Item *pItem = m_items[slot];
+ if( pItem )
+ {
+ pItem->RemoveFromWorld();
+ if(del) pItem->SetState(ITEM_REMOVED, this);
+ }
+
+ m_items[slot] = NULL;
+
+ uint32 eslot = slot - BUYBACK_SLOT_START;
+ SetUInt64Value( PLAYER_FIELD_VENDORBUYBACK_SLOT_1 + eslot * 2, 0 );
+ SetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + eslot, 0 );
+ SetUInt32Value( PLAYER_FIELD_BUYBACK_TIMESTAMP_1 + eslot, 0 );
+
+ // if current backslot is filled set to now free slot
+ if(m_items[m_currentBuybackSlot])
+ m_currentBuybackSlot = slot;
+ }
+}
+
+void Player::SendEquipError( uint8 msg, Item* pItem, Item *pItem2 )
+{
+ sLog.outDebug( "WORLD: Sent SMSG_INVENTORY_CHANGE_FAILURE (%u)",msg);
+ WorldPacket data( SMSG_INVENTORY_CHANGE_FAILURE, (msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I ? 22 : 18) );
+ data << uint8(msg);
+
+ if(msg)
+ {
+ data << uint64(pItem ? pItem->GetGUID() : 0);
+ data << uint64(pItem2 ? pItem2->GetGUID() : 0);
+ data << uint8(0); // not 0 there...
+
+ if(msg == EQUIP_ERR_CANT_EQUIP_LEVEL_I)
+ {
+ uint32 level = 0;
+
+ if(pItem)
+ if(ItemPrototype const* proto = pItem->GetProto())
+ level = proto->RequiredLevel;
+
+ data << uint32(level); // new 2.4.0
+ }
+ }
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 param )
+{
+ sLog.outDebug( "WORLD: Sent SMSG_BUY_FAILED" );
+ WorldPacket data( SMSG_BUY_FAILED, (8+4+4+1) );
+ data << uint64(pCreature ? pCreature->GetGUID() : 0);
+ data << uint32(item);
+ if( param > 0 )
+ data << uint32(param);
+ data << uint8(msg);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param )
+{
+ sLog.outDebug( "WORLD: Sent SMSG_SELL_ITEM" );
+ WorldPacket data( SMSG_SELL_ITEM,(8+8+(param?4:0)+1)); // last check 2.0.10
+ data << uint64(pCreature ? pCreature->GetGUID() : 0);
+ data << uint64(guid);
+ if( param > 0 )
+ data << uint32(param);
+ data << uint8(msg);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::ClearTrade()
+{
+ tradeGold = 0;
+ acceptTrade = false;
+ for(int i = 0; i < TRADE_SLOT_COUNT; i++)
+ tradeItems[i] = NULL_SLOT;
+}
+
+void Player::TradeCancel(bool sendback)
+{
+ if(pTrader)
+ {
+ // send yellow "Trade cancelled" message to both traders
+ WorldSession* ws;
+ ws = GetSession();
+ if(sendback)
+ ws->SendCancelTrade();
+ ws = pTrader->GetSession();
+ if(!ws->PlayerLogout())
+ ws->SendCancelTrade();
+
+ // cleanup
+ ClearTrade();
+ pTrader->ClearTrade();
+ // prevent loss of reference
+ pTrader->pTrader = NULL;
+ pTrader = NULL;
+ }
+}
+
+void Player::UpdateItemDuration(uint32 time, bool realtimeonly)
+{
+ if(m_itemDuration.empty())
+ return;
+
+ sLog.outDebug("Player::UpdateItemDuration(%u,%u)", time,realtimeonly);
+
+ for(ItemDurationList::iterator itr = m_itemDuration.begin();itr != m_itemDuration.end(); )
+ {
+ Item* item = *itr;
+ ++itr; // current element can be erased in UpdateDuration
+
+ if (realtimeonly && item->GetProto()->Duration < 0 || !realtimeonly)
+ item->UpdateDuration(this,time);
+ }
+}
+
+void Player::UpdateEnchantTime(uint32 time)
+{
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin(),next;itr != m_enchantDuration.end();itr=next)
+ {
+ assert(itr->item);
+ next=itr;
+ if(!itr->item->GetEnchantmentId(itr->slot))
+ {
+ next = m_enchantDuration.erase(itr);
+ }
+ else if(itr->leftduration <= time)
+ {
+ ApplyEnchantment(itr->item,itr->slot,false,false);
+ itr->item->ClearEnchantment(itr->slot);
+ next = m_enchantDuration.erase(itr);
+ }
+ else if(itr->leftduration > time)
+ {
+ itr->leftduration -= time;
+ ++next;
+ }
+ }
+}
+
+void Player::AddEnchantmentDurations(Item *item)
+{
+ for(int x=0;x<MAX_ENCHANTMENT_SLOT;++x)
+ {
+ if(!item->GetEnchantmentId(EnchantmentSlot(x)))
+ continue;
+
+ uint32 duration = item->GetEnchantmentDuration(EnchantmentSlot(x));
+ if( duration > 0 )
+ AddEnchantmentDuration(item,EnchantmentSlot(x),duration);
+ }
+}
+
+void Player::RemoveEnchantmentDurations(Item *item)
+{
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();)
+ {
+ if(itr->item == item)
+ {
+ // save duration in item
+ item->SetEnchantmentDuration(EnchantmentSlot(itr->slot),itr->leftduration);
+ itr = m_enchantDuration.erase(itr);
+ }
+ else
+ ++itr;
+ }
+}
+
+
+void Player::RemoveAllEnchantments(EnchantmentSlot slot)
+{
+ // remove enchantments from equipped items first to clean up the m_enchantDuration list
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin(),next;itr != m_enchantDuration.end();itr=next)
+ {
+ next = itr;
+ if(itr->slot==slot)
+ {
+ if(itr->item && itr->item->GetEnchantmentId(slot))
+ {
+ // remove from stats
+ ApplyEnchantment(itr->item,slot,false,false);
+ // remove visual
+ itr->item->ClearEnchantment(slot);
+ }
+ // remove from update list
+ next = m_enchantDuration.erase(itr);
+ }
+ else
+ ++next;
+ }
+
+ // remove enchants from inventory items
+ // NOTE: no need to remove these from stats, since these aren't equipped
+ // in inventory
+ for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ {
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pItem && pItem->GetEnchantmentId(slot) )
+ pItem->ClearEnchantment(slot);
+ }
+
+ // in inventory bags
+ for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ {
+ Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if( pBag )
+ {
+ ItemPrototype const *pBagProto = pBag->GetProto();
+ if( pBagProto )
+ {
+ for(uint32 j = 0; j < pBagProto->ContainerSlots; j++)
+ {
+ Item* pItem = pBag->GetItemByPos(j);
+ if( pItem && pItem->GetEnchantmentId(slot) )
+ pItem->ClearEnchantment(slot);
+ }
+ }
+ }
+ }
+}
+
+// duration == 0 will remove item enchant
+void Player::AddEnchantmentDuration(Item *item,EnchantmentSlot slot,uint32 duration)
+{
+ if(!item)
+ return;
+
+ if(slot >= MAX_ENCHANTMENT_SLOT)
+ return;
+
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();++itr)
+ {
+ if(itr->item == item && itr->slot == slot)
+ {
+ itr->item->SetEnchantmentDuration(itr->slot,itr->leftduration);
+ m_enchantDuration.erase(itr);
+ break;
+ }
+ }
+ if(item && duration > 0 )
+ {
+ GetSession()->SendItemEnchantTimeUpdate(GetGUID(), item->GetGUID(),slot,uint32(duration/1000));
+ m_enchantDuration.push_back(EnchantDuration(item,slot,duration));
+ }
+}
+
+void Player::ApplyEnchantment(Item *item,bool apply)
+{
+ for(uint32 slot = 0; slot < MAX_ENCHANTMENT_SLOT; ++slot)
+ ApplyEnchantment(item, EnchantmentSlot(slot), apply);
+}
+
+void Player::ApplyEnchantment(Item *item,EnchantmentSlot slot,bool apply, bool apply_dur, bool ignore_condition)
+{
+ if(!item)
+ return;
+
+ if(!item->IsEquipped())
+ return;
+
+ if(slot >= MAX_ENCHANTMENT_SLOT)
+ return;
+
+ uint32 enchant_id = item->GetEnchantmentId(slot);
+ if(!enchant_id)
+ return;
+
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return;
+
+ if(!ignore_condition && pEnchant->EnchantmentCondition && !((Player*)this)->EnchantmentFitsRequirements(pEnchant->EnchantmentCondition, -1))
+ return;
+
+ for (int s=0; s<3; s++)
+ {
+ uint32 enchant_display_type = pEnchant->type[s];
+ uint32 enchant_amount = pEnchant->amount[s];
+ uint32 enchant_spell_id = pEnchant->spellid[s];
+
+ switch(enchant_display_type)
+ {
+ case ITEM_ENCHANTMENT_TYPE_NONE:
+ break;
+ case ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL:
+ // processed in Player::CastItemCombatSpell
+ break;
+ case ITEM_ENCHANTMENT_TYPE_DAMAGE:
+ if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
+ HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, float(enchant_amount), apply);
+ else if (item->GetSlot() == EQUIPMENT_SLOT_OFFHAND)
+ HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, float(enchant_amount), apply);
+ else if (item->GetSlot() == EQUIPMENT_SLOT_RANGED)
+ HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_VALUE, float(enchant_amount), apply);
+ break;
+ case ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL:
+ if(enchant_spell_id)
+ {
+ if(apply)
+ {
+ int32 basepoints = int32(enchant_amount);
+ // Random Property Exist - try found basepoints for spell (basepoints depencs from item suffix factor)
+ if (item->GetItemRandomPropertyId() !=0 && !enchant_amount)
+ {
+ ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
+ if (item_rand)
+ {
+ // Search enchant_amount
+ for (int k=0; k<3; k++)
+ {
+ if(item_rand->enchant_id[k] == enchant_id)
+ {
+ basepoints = int32((item_rand->prefix[k]*item->GetItemSuffixFactor()) / 10000 );
+ break;
+ }
+ }
+ }
+ }
+ // Cast custom spell vs all equal basepoints getted from enchant_amount
+ if (basepoints)
+ CastCustomSpell(this,enchant_spell_id,&basepoints,&basepoints,&basepoints,true,item);
+ else
+ CastSpell(this,enchant_spell_id,true,item);
+ }
+ else
+ RemoveAurasDueToItemSpell(item,enchant_spell_id);
+ }
+ break;
+ case ITEM_ENCHANTMENT_TYPE_RESISTANCE:
+ if (!enchant_amount)
+ {
+ ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
+ if(item_rand)
+ {
+ for (int k=0; k<3; k++)
+ {
+ if(item_rand->enchant_id[k] == enchant_id)
+ {
+ enchant_amount = uint32((item_rand->prefix[k]*item->GetItemSuffixFactor()) / 10000 );
+ break;
+ }
+ }
+ }
+ }
+
+ HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply);
+ break;
+ case ITEM_ENCHANTMENT_TYPE_STAT:
+ {
+ if (!enchant_amount)
+ {
+ ItemRandomSuffixEntry const *item_rand_suffix = sItemRandomSuffixStore.LookupEntry(abs(item->GetItemRandomPropertyId()));
+ if(item_rand_suffix)
+ {
+ for (int k=0; k<3; k++)
+ {
+ if(item_rand_suffix->enchant_id[k] == enchant_id)
+ {
+ enchant_amount = uint32((item_rand_suffix->prefix[k]*item->GetItemSuffixFactor()) / 10000 );
+ break;
+ }
+ }
+ }
+ }
+
+ sLog.outDebug("Adding %u to stat nb %u",enchant_amount,enchant_spell_id);
+ switch (enchant_spell_id)
+ {
+ case ITEM_MOD_AGILITY:
+ sLog.outDebug("+ %u AGILITY",enchant_amount);
+ HandleStatModifier(UNIT_MOD_STAT_AGILITY, TOTAL_VALUE, float(enchant_amount), apply);
+ ApplyStatBuffMod(STAT_AGILITY, enchant_amount, apply);
+ break;
+ case ITEM_MOD_STRENGTH:
+ sLog.outDebug("+ %u STRENGTH",enchant_amount);
+ HandleStatModifier(UNIT_MOD_STAT_STRENGTH, TOTAL_VALUE, float(enchant_amount), apply);
+ ApplyStatBuffMod(STAT_STRENGTH, enchant_amount, apply);
+ break;
+ case ITEM_MOD_INTELLECT:
+ sLog.outDebug("+ %u INTELLECT",enchant_amount);
+ HandleStatModifier(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE, float(enchant_amount), apply);
+ ApplyStatBuffMod(STAT_INTELLECT, enchant_amount, apply);
+ break;
+ case ITEM_MOD_SPIRIT:
+ sLog.outDebug("+ %u SPIRIT",enchant_amount);
+ HandleStatModifier(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE, float(enchant_amount), apply);
+ ApplyStatBuffMod(STAT_SPIRIT, enchant_amount, apply);
+ break;
+ case ITEM_MOD_STAMINA:
+ sLog.outDebug("+ %u STAMINA",enchant_amount);
+ HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE, float(enchant_amount), apply);
+ ApplyStatBuffMod(STAT_STAMINA, enchant_amount, apply);
+ break;
+ case ITEM_MOD_DEFENSE_SKILL_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply);
+ sLog.outDebug("+ %u DEFENCE", enchant_amount);
+ break;
+ case ITEM_MOD_DODGE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_DODGE, enchant_amount, apply);
+ sLog.outDebug("+ %u DODGE", enchant_amount);
+ break;
+ case ITEM_MOD_PARRY_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_PARRY, enchant_amount, apply);
+ sLog.outDebug("+ %u PARRY", enchant_amount);
+ break;
+ case ITEM_MOD_BLOCK_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_BLOCK, enchant_amount, apply);
+ sLog.outDebug("+ %u SHIELD_BLOCK", enchant_amount);
+ break;
+ case ITEM_MOD_HIT_MELEE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
+ sLog.outDebug("+ %u MELEE_HIT", enchant_amount);
+ break;
+ case ITEM_MOD_HIT_RANGED_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
+ sLog.outDebug("+ %u RANGED_HIT", enchant_amount);
+ break;
+ case ITEM_MOD_HIT_SPELL_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u SPELL_HIT", enchant_amount);
+ break;
+ case ITEM_MOD_CRIT_MELEE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
+ sLog.outDebug("+ %u MELEE_CRIT", enchant_amount);
+ break;
+ case ITEM_MOD_CRIT_RANGED_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
+ sLog.outDebug("+ %u RANGED_CRIT", enchant_amount);
+ break;
+ case ITEM_MOD_CRIT_SPELL_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u SPELL_CRIT", enchant_amount);
+ break;
+// Values from ITEM_STAT_MELEE_HA_RATING to ITEM_MOD_HASTE_RANGED_RATING are never used
+// in Enchantments
+// case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
+// break;
+// case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
+// break;
+// case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
+// break;
+// case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
+// break;
+// case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
+// break;
+// case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
+// break;
+// case ITEM_MOD_HASTE_MELEE_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
+// break;
+// case ITEM_MOD_HASTE_RANGED_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
+// break;
+ case ITEM_MOD_HASTE_SPELL_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
+ break;
+ case ITEM_MOD_HIT_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HIT_MELEE, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_HIT_RANGED, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_HIT_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u HIT", enchant_amount);
+ break;
+ case ITEM_MOD_CRIT_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_MELEE, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_RANGED, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u CRITICAL", enchant_amount);
+ break;
+// Values ITEM_MOD_HIT_TAKEN_RATING and ITEM_MOD_CRIT_TAKEN_RATING are never used in Enchantment
+// case ITEM_MOD_HIT_TAKEN_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_MELEE, enchant_amount, apply);
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_RANGED, enchant_amount, apply);
+// ((Player*)this)->ApplyRatingMod(CR_HIT_TAKEN_SPELL, enchant_amount, apply);
+// break;
+// case ITEM_MOD_CRIT_TAKEN_RATING:
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
+// ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
+// break;
+ case ITEM_MOD_RESILIENCE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u RESILIENCE", enchant_amount);
+ break;
+ case ITEM_MOD_HASTE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_HASTE_MELEE, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_HASTE_RANGED, enchant_amount, apply);
+ ((Player*)this)->ApplyRatingMod(CR_HASTE_SPELL, enchant_amount, apply);
+ sLog.outDebug("+ %u HASTE", enchant_amount);
+ break;
+ case ITEM_MOD_EXPERTISE_RATING:
+ ((Player*)this)->ApplyRatingMod(CR_EXPERTISE, enchant_amount, apply);
+ sLog.outDebug("+ %u EXPERTISE", enchant_amount);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon
+ {
+ if(getClass() == CLASS_SHAMAN)
+ {
+ float addValue = 0.0f;
+ if(item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)
+ {
+ addValue = float(enchant_amount * item->GetProto()->Delay/1000.0f);
+ HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_VALUE, addValue, apply);
+ }
+ else if(item->GetSlot() == EQUIPMENT_SLOT_OFFHAND )
+ {
+ addValue = float(enchant_amount * item->GetProto()->Delay/1000.0f);
+ HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_VALUE, addValue, apply);
+ }
+ }
+ break;
+ }
+ default:
+ sLog.outError("Unknown item enchantment display type: %d",enchant_display_type);
+ break;
+ } /*switch(enchant_display_type)*/
+ } /*for*/
+
+ // visualize enchantment at player and equipped items
+ if(slot < MAX_INSPECTED_ENCHANTMENT_SLOT)
+ {
+ int VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + (item->GetSlot() * MAX_VISIBLE_ITEM_OFFSET);
+ SetUInt32Value(VisibleBase + 1 + slot, apply? item->GetEnchantmentId(slot) : 0);
+ }
+
+ if(apply_dur)
+ {
+ if(apply)
+ {
+ // set duration
+ uint32 duration = item->GetEnchantmentDuration(slot);
+ if(duration > 0)
+ AddEnchantmentDuration(item,slot,duration);
+ }
+ else
+ {
+ // duration == 0 will remove EnchantDuration
+ AddEnchantmentDuration(item,slot,0);
+ }
+ }
+}
+
+void Player::SendEnchantmentDurations()
+{
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();++itr)
+ {
+ GetSession()->SendItemEnchantTimeUpdate(GetGUID(), itr->item->GetGUID(),itr->slot,uint32(itr->leftduration)/1000);
+ }
+}
+
+void Player::SendItemDurations()
+{
+ for(ItemDurationList::iterator itr = m_itemDuration.begin();itr != m_itemDuration.end();++itr)
+ {
+ (*itr)->SendTimeUpdate(this);
+ }
+}
+
+void Player::SendNewItem(Item *item, uint32 count, bool received, bool created, bool broadcast)
+{
+ if(!item) // prevent crash
+ return;
+
+ // last check 2.0.10
+ WorldPacket data( SMSG_ITEM_PUSH_RESULT, (8+4+4+4+1+4+4+4+4+4) );
+ data << GetGUID(); // player GUID
+ data << uint32(received); // 0=looted, 1=from npc
+ data << uint32(created); // 0=received, 1=created
+ data << uint32(1); // always 0x01 (probably meant to be count of listed items)
+ data << (uint8)item->GetBagSlot(); // bagslot
+ // item slot, but when added to stack: 0xFFFFFFFF
+ data << (uint32) ((item->GetCount()==count) ? item->GetSlot() : -1);
+ data << uint32(item->GetEntry()); // item id
+ data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
+ data << uint32(item->GetItemRandomPropertyId()); // random item property id
+ data << uint32(count); // count of items
+ data << GetItemCount(item->GetEntry()); // count of items in inventory
+
+ if (broadcast && GetGroup())
+ GetGroup()->BroadcastPacket(&data);
+ else
+ GetSession()->SendPacket(&data);
+}
+
+/*********************************************************/
+/*** QUEST SYSTEM ***/
+/*********************************************************/
+
+void Player::PrepareQuestMenu( uint64 guid )
+{
+ Object *pObject;
+ QuestRelations* pObjectQR;
+ QuestRelations* pObjectQIR;
+ Creature *pCreature = ObjectAccessor::GetCreature(*this, guid);
+ if( pCreature )
+ {
+ pObject = (Object*)pCreature;
+ pObjectQR = &objmgr.mCreatureQuestRelations;
+ pObjectQIR = &objmgr.mCreatureQuestInvolvedRelations;
+ }
+ else
+ {
+ GameObject *pGameObject = ObjectAccessor::GetGameObject(*this, guid);
+ if( pGameObject )
+ {
+ pObject = (Object*)pGameObject;
+ pObjectQR = &objmgr.mGOQuestRelations;
+ pObjectQIR = &objmgr.mGOQuestInvolvedRelations;
+ }
+ else
+ return;
+ }
+
+ QuestMenu &qm = PlayerTalkClass->GetQuestMenu();
+ qm.ClearMenu();
+
+ for(QuestRelations::const_iterator i = pObjectQIR->lower_bound(pObject->GetEntry()); i != pObjectQIR->upper_bound(pObject->GetEntry()); ++i)
+ {
+ uint32 quest_id = i->second;
+ QuestStatus status = GetQuestStatus( quest_id );
+ if ( status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus( quest_id ) )
+ qm.AddMenuItem(quest_id, DIALOG_STATUS_REWARD_REP);
+ else if ( status == QUEST_STATUS_INCOMPLETE )
+ qm.AddMenuItem(quest_id, DIALOG_STATUS_INCOMPLETE);
+ else if (status == QUEST_STATUS_AVAILABLE )
+ qm.AddMenuItem(quest_id, DIALOG_STATUS_CHAT);
+ }
+
+ for(QuestRelations::const_iterator i = pObjectQR->lower_bound(pObject->GetEntry()); i != pObjectQR->upper_bound(pObject->GetEntry()); ++i)
+ {
+ uint32 quest_id = i->second;
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+ if(!pQuest) continue;
+
+ QuestStatus status = GetQuestStatus( quest_id );
+
+ if (pQuest->IsAutoComplete() && CanTakeQuest(pQuest, false))
+ qm.AddMenuItem(quest_id, DIALOG_STATUS_REWARD_REP);
+ else if ( status == QUEST_STATUS_NONE && CanTakeQuest( pQuest, false ) )
+ qm.AddMenuItem(quest_id, DIALOG_STATUS_AVAILABLE);
+ }
+}
+
+void Player::SendPreparedQuest( uint64 guid )
+{
+ QuestMenu& questMenu = PlayerTalkClass->GetQuestMenu();
+ if( questMenu.Empty() )
+ return;
+
+ QuestMenuItem const& qmi0 = questMenu.GetItem( 0 );
+
+ uint32 status = qmi0.m_qIcon;
+
+ // single element case
+ if ( questMenu.MenuItemCount() == 1 )
+ {
+ // Auto open -- maybe also should verify there is no greeting
+ uint32 quest_id = qmi0.m_qId;
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+ if ( pQuest )
+ {
+ if( status == DIALOG_STATUS_REWARD_REP && !GetQuestRewardStatus( quest_id ) )
+ PlayerTalkClass->SendQuestGiverRequestItems( pQuest, guid, CanRewardQuest(pQuest,false), true );
+ else if( status == DIALOG_STATUS_INCOMPLETE )
+ PlayerTalkClass->SendQuestGiverRequestItems( pQuest, guid, false, true );
+ // Send completable on repeatable quest if player don't have quest
+ else if( pQuest->IsRepeatable() )
+ PlayerTalkClass->SendQuestGiverRequestItems( pQuest, guid, CanCompleteRepeatableQuest(pQuest), true );
+ else
+ PlayerTalkClass->SendQuestGiverQuestDetails( pQuest, guid, true );
+ }
+ }
+ // multiply entries
+ else
+ {
+ QEmote qe;
+ qe._Delay = 0;
+ qe._Emote = 0;
+ std::string title = "";
+ Creature *pCreature = ObjectAccessor::GetCreature(*this, guid);
+ if( pCreature )
+ {
+ uint32 textid = pCreature->GetNpcTextId();
+ GossipText * gossiptext = objmgr.GetGossipText(textid);
+ if( !gossiptext )
+ {
+ qe._Delay = 0; //TEXTEMOTE_MESSAGE; //zyg: player emote
+ qe._Emote = 0; //TEXTEMOTE_HELLO; //zyg: NPC emote
+ title = "";
+ }
+ else
+ {
+ qe = gossiptext->Options[0].Emotes[0];
+
+ if(!gossiptext->Options[0].Text_0.empty())
+ {
+ title = gossiptext->Options[0].Text_0;
+
+ int loc_idx = GetSession()->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textid);
+ if (nl)
+ {
+ if (nl->Text_0[0].size() > loc_idx && !nl->Text_0[0][loc_idx].empty())
+ title = nl->Text_0[0][loc_idx];
+ }
+ }
+ }
+ else
+ {
+ title = gossiptext->Options[0].Text_1;
+
+ int loc_idx = GetSession()->GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ NpcTextLocale const *nl = objmgr.GetNpcTextLocale(textid);
+ if (nl)
+ {
+ if (nl->Text_1[0].size() > loc_idx && !nl->Text_1[0][loc_idx].empty())
+ title = nl->Text_1[0][loc_idx];
+ }
+ }
+ }
+ }
+ }
+ PlayerTalkClass->SendQuestGiverQuestList( qe, title, guid );
+ }
+}
+
+bool Player::IsActiveQuest( uint32 quest_id ) const
+{
+ QuestStatusMap::const_iterator itr = mQuestStatus.find(quest_id);
+
+ return itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE;
+}
+
+Quest const * Player::GetNextQuest( uint64 guid, Quest const *pQuest )
+{
+ Object *pObject;
+ QuestRelations* pObjectQR;
+ QuestRelations* pObjectQIR;
+
+ Creature *pCreature = ObjectAccessor::GetCreature(*this, guid);
+ if( pCreature )
+ {
+ pObject = (Object*)pCreature;
+ pObjectQR = &objmgr.mCreatureQuestRelations;
+ pObjectQIR = &objmgr.mCreatureQuestInvolvedRelations;
+ }
+ else
+ {
+ GameObject *pGameObject = ObjectAccessor::GetGameObject(*this, guid);
+ if( pGameObject )
+ {
+ pObject = (Object*)pGameObject;
+ pObjectQR = &objmgr.mGOQuestRelations;
+ pObjectQIR = &objmgr.mGOQuestInvolvedRelations;
+ }
+ else
+ return NULL;
+ }
+
+ uint32 nextQuestID = pQuest->GetNextQuestInChain();
+ for(QuestRelations::const_iterator itr = pObjectQR->lower_bound(pObject->GetEntry()); itr != pObjectQR->upper_bound(pObject->GetEntry()); ++itr)
+ {
+ if (itr->second == nextQuestID)
+ return objmgr.GetQuestTemplate(nextQuestID);
+ }
+
+ return NULL;
+}
+
+bool Player::CanSeeStartQuest( Quest const *pQuest )
+{
+ if( SatisfyQuestRace( pQuest, false ) && SatisfyQuestSkillOrClass( pQuest, false ) &&
+ SatisfyQuestExclusiveGroup( pQuest, false ) && SatisfyQuestReputation( pQuest, false ) &&
+ SatisfyQuestPreviousQuest( pQuest, false ) && SatisfyQuestNextChain( pQuest, false ) &&
+ SatisfyQuestPrevChain( pQuest, false ) && SatisfyQuestDay( pQuest, false ) )
+ {
+ return getLevel() + sWorld.getConfig(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF) >= pQuest->GetMinLevel();
+ }
+
+ return false;
+}
+
+bool Player::CanTakeQuest( Quest const *pQuest, bool msg )
+{
+ return SatisfyQuestStatus( pQuest, msg ) && SatisfyQuestExclusiveGroup( pQuest, msg )
+ && SatisfyQuestRace( pQuest, msg ) && SatisfyQuestLevel( pQuest, msg )
+ && SatisfyQuestSkillOrClass( pQuest, msg ) && SatisfyQuestReputation( pQuest, msg )
+ && SatisfyQuestPreviousQuest( pQuest, msg ) && SatisfyQuestTimed( pQuest, msg )
+ && SatisfyQuestNextChain( pQuest, msg ) && SatisfyQuestPrevChain( pQuest, msg )
+ && SatisfyQuestDay( pQuest, msg );
+}
+
+bool Player::CanAddQuest( Quest const *pQuest, bool msg )
+{
+ if( !SatisfyQuestLog( msg ) )
+ return false;
+
+ uint32 srcitem = pQuest->GetSrcItemId();
+ if( srcitem > 0 )
+ {
+ uint32 count = pQuest->GetSrcItemCount();
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, srcitem, count );
+
+ // player already have max number (in most case 1) source item, no additional item needed and quest can be added.
+ if( msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
+ return true;
+ else if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, NULL, NULL );
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Player::CanCompleteQuest( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ QuestStatusData& q_status = mQuestStatus[quest_id];
+ if( q_status.m_status == QUEST_STATUS_COMPLETE )
+ return false; // not allow re-complete quest
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+
+ if(!qInfo)
+ return false;
+
+ // auto complete quest
+ if (qInfo->IsAutoComplete() && CanTakeQuest(qInfo, false))
+ return true;
+
+ if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
+ {
+
+ if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if( qInfo->ReqItemCount[i]!= 0 && q_status.m_itemcount[i] < qInfo->ReqItemCount[i] )
+ return false;
+ }
+ }
+
+ if ( qInfo->HasFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if( qInfo->ReqCreatureOrGOId[i] == 0 )
+ continue;
+
+ if( qInfo->ReqCreatureOrGOCount[i] != 0 && q_status.m_creatureOrGOcount[i] < qInfo->ReqCreatureOrGOCount[i] )
+ return false;
+ }
+ }
+
+ if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT ) && !q_status.m_explored )
+ return false;
+
+ if ( qInfo->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) && q_status.m_timer == 0 )
+ return false;
+
+ if ( qInfo->GetRewOrReqMoney() < 0 )
+ {
+ if ( GetMoney() < uint32(-qInfo->GetRewOrReqMoney()) )
+ return false;
+ }
+
+ uint32 repFacId = qInfo->GetRepObjectiveFaction();
+ if ( repFacId && GetReputation(repFacId) < qInfo->GetRepObjectiveValue() )
+ return false;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Player::CanCompleteRepeatableQuest( Quest const *pQuest )
+{
+ // Solve problem that player don't have the quest and try complete it.
+ // if repeatable she must be able to complete event if player don't have it.
+ // Seem that all repeatable quest are DELIVER Flag so, no need to add more.
+ if( !CanTakeQuest(pQuest, false) )
+ return false;
+
+ if (pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER) )
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ if( pQuest->ReqItemId[i] && pQuest->ReqItemCount[i] && !HasItemCount(pQuest->ReqItemId[i],pQuest->ReqItemCount[i]) )
+ return false;
+
+ if( !CanRewardQuest(pQuest, false) )
+ return false;
+
+ return true;
+}
+
+bool Player::CanRewardQuest( Quest const *pQuest, bool msg )
+{
+ // not auto complete quest and not completed quest (only cheating case, then ignore without message)
+ if(!pQuest->IsAutoComplete() && GetQuestStatus(pQuest->GetQuestId()) != QUEST_STATUS_COMPLETE)
+ return false;
+
+ // daily quest can't be rewarded (10 daily quest already completed)
+ if(!SatisfyQuestDay(pQuest,true))
+ return false;
+
+ // rewarded and not repeatable quest (only cheating case, then ignore without message)
+ if(GetQuestRewardStatus(pQuest->GetQuestId()))
+ return false;
+
+ // prevent receive reward with quest items in bank
+ if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if( pQuest->ReqItemCount[i]!= 0 &&
+ GetItemCount(pQuest->ReqItemId[i]) < pQuest->ReqItemCount[i] )
+ {
+ if(msg)
+ SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return false;
+ }
+ }
+ }
+
+ // prevent receive reward with low money and GetRewOrReqMoney() < 0
+ if(pQuest->GetRewOrReqMoney() < 0 && GetMoney() < uint32(-pQuest->GetRewOrReqMoney()) )
+ return false;
+
+ return true;
+}
+
+bool Player::CanRewardQuest( Quest const *pQuest, uint32 reward, bool msg )
+{
+ // prevent receive reward with quest items in bank or for not completed quest
+ if(!CanRewardQuest(pQuest,msg))
+ return false;
+
+ if ( pQuest->GetRewChoiceItemsCount() > 0 )
+ {
+ if( pQuest->RewChoiceItemId[reward] )
+ {
+ ItemPosCountVec dest;
+ uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] );
+ if( res != EQUIP_ERR_OK )
+ {
+ SendEquipError( res, NULL, NULL );
+ return false;
+ }
+ }
+ }
+
+ if ( pQuest->GetRewItemsCount() > 0 )
+ {
+ for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i)
+ {
+ if( pQuest->RewItemId[i] )
+ {
+ ItemPosCountVec dest;
+ uint8 res = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] );
+ if( res != EQUIP_ERR_OK )
+ {
+ SendEquipError( res, NULL, NULL );
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+void Player::AddQuest( Quest const *pQuest, Object *questGiver )
+{
+ uint16 log_slot = FindQuestSlot( 0 );
+ assert(log_slot < MAX_QUEST_LOG_SIZE);
+
+ uint32 quest_id = pQuest->GetQuestId();
+
+ // if not exist then created with set uState==NEW and rewarded=false
+ QuestStatusData& questStatusData = mQuestStatus[quest_id];
+ if (questStatusData.uState != QUEST_NEW)
+ questStatusData.uState = QUEST_CHANGED;
+
+ // check for repeatable quests status reset
+ questStatusData.m_status = QUEST_STATUS_INCOMPLETE;
+ questStatusData.m_explored = false;
+
+ if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ questStatusData.m_itemcount[i] = 0;
+ }
+
+ if ( pQuest->HasFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ questStatusData.m_creatureOrGOcount[i] = 0;
+ }
+
+ GiveQuestSourceItem( pQuest );
+ AdjustQuestReqItemCount( pQuest );
+
+ if( pQuest->GetRepObjectiveFaction() )
+ SetFactionVisibleForFactionId(pQuest->GetRepObjectiveFaction());
+
+ uint32 qtime = 0;
+ if( pQuest->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) )
+ {
+ uint32 limittime = pQuest->GetLimitTime();
+
+ // shared timed quest
+ if(questGiver && questGiver->GetTypeId()==TYPEID_PLAYER)
+ limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / 1000;
+
+ AddTimedQuest( quest_id );
+ questStatusData.m_timer = limittime * 1000;
+ qtime = static_cast<uint32>(time(NULL)) + limittime;
+ }
+ else
+ questStatusData.m_timer = 0;
+
+ SetQuestSlot(log_slot, quest_id, qtime);
+
+ //starting initial quest script
+ if(questGiver && pQuest->GetQuestStartScript()!=0)
+ sWorld.ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this);
+
+ UpdateForQuestsGO();
+}
+
+void Player::CompleteQuest( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ SetQuestStatus( quest_id, QUEST_STATUS_COMPLETE );
+
+ uint16 log_slot = FindQuestSlot( quest_id );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ SetQuestSlotState(log_slot,QUEST_STATE_COMPLETE);
+
+ if(Quest const* qInfo = objmgr.GetQuestTemplate(quest_id))
+ {
+ if( qInfo->HasFlag(QUEST_FLAGS_AUTO_REWARDED) )
+ RewardQuest(qInfo,0,this,false);
+ else
+ SendQuestComplete( quest_id );
+ }
+ }
+}
+
+void Player::IncompleteQuest( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ SetQuestStatus( quest_id, QUEST_STATUS_INCOMPLETE );
+
+ uint16 log_slot = FindQuestSlot( quest_id );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ RemoveQuestSlotState(log_slot,QUEST_STATE_COMPLETE);
+ }
+}
+
+void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce )
+{
+ uint32 quest_id = pQuest->GetQuestId();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; i++ )
+ {
+ if ( pQuest->ReqItemId[i] )
+ DestroyItemCount( pQuest->ReqItemId[i], pQuest->ReqItemCount[i], true);
+ }
+
+ //if( qInfo->HasSpecialFlag( QUEST_FLAGS_TIMED ) )
+ // SetTimedQuest( 0 );
+ m_timedquests.erase(pQuest->GetQuestId());
+
+ if ( pQuest->GetRewChoiceItemsCount() > 0 )
+ {
+ if( pQuest->RewChoiceItemId[reward] )
+ {
+ ItemPosCountVec dest;
+ if( CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewChoiceItemId[reward], pQuest->RewChoiceItemCount[reward] ) == EQUIP_ERR_OK )
+ {
+ Item* item = StoreNewItem( dest, pQuest->RewChoiceItemId[reward], true);
+ SendNewItem(item, pQuest->RewChoiceItemCount[reward], true, false);
+ }
+ }
+ }
+
+ if ( pQuest->GetRewItemsCount() > 0 )
+ {
+ for (uint32 i=0; i < pQuest->GetRewItemsCount(); ++i)
+ {
+ if( pQuest->RewItemId[i] )
+ {
+ ItemPosCountVec dest;
+ if( CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, pQuest->RewItemId[i], pQuest->RewItemCount[i] ) == EQUIP_ERR_OK )
+ {
+ Item* item = StoreNewItem( dest, pQuest->RewItemId[i], true);
+ SendNewItem(item, pQuest->RewItemCount[i], true, false);
+ }
+ }
+ }
+ }
+
+ RewardReputation( pQuest );
+
+ if( pQuest->GetRewSpellCast() > 0 )
+ CastSpell( this, pQuest->GetRewSpellCast(), true);
+ else if( pQuest->GetRewSpell() > 0)
+ CastSpell( this, pQuest->GetRewSpell(), true);
+
+ uint16 log_slot = FindQuestSlot( quest_id );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ SetQuestSlot(log_slot,0);
+
+ QuestStatusData& q_status = mQuestStatus[quest_id];
+
+ // Not give XP in case already completed once repeatable quest
+ uint32 XP = q_status.m_rewarded ? 0 : uint32(pQuest->XPValue( this )*sWorld.getRate(RATE_XP_QUEST));
+
+ if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
+ GiveXP( XP , NULL );
+ else
+ ModifyMoney( int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)) );
+
+ // Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
+ ModifyMoney( pQuest->GetRewOrReqMoney() );
+
+ // title reward
+ if(pQuest->GetCharTitleId())
+ {
+ if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
+ SetFlag64(PLAYER__FIELD_KNOWN_TITLES, (uint64(1) << titleEntry->bit_index));
+ }
+
+ // Send reward mail
+ if(pQuest->GetRewMailTemplateId())
+ {
+ MailMessageType mailType;
+ uint32 senderGuidOrEntry;
+ switch(questGiver->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ mailType = MAIL_CREATURE;
+ senderGuidOrEntry = questGiver->GetEntry();
+ break;
+ case TYPEID_GAMEOBJECT:
+ mailType = MAIL_GAMEOBJECT;
+ senderGuidOrEntry = questGiver->GetEntry();
+ break;
+ case TYPEID_ITEM:
+ mailType = MAIL_ITEM;
+ senderGuidOrEntry = questGiver->GetEntry();
+ break;
+ case TYPEID_PLAYER:
+ mailType = MAIL_NORMAL;
+ senderGuidOrEntry = questGiver->GetGUIDLow();
+ break;
+ default:
+ mailType = MAIL_NORMAL;
+ senderGuidOrEntry = GetGUIDLow();
+ break;
+ }
+
+ Loot questMailLoot;
+
+ questMailLoot.FillLoot(pQuest->GetQuestId(), LootTemplates_QuestMail, this);
+
+ // fill mail
+ MailItemsInfo mi; // item list preparing
+
+ for(size_t i = 0; mi.size() < MAX_MAIL_ITEMS && i < questMailLoot.items.size(); ++i)
+ {
+ if(LootItem* lootitem = questMailLoot.LootItemInSlot(i,this))
+ {
+ if(Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,this))
+ {
+ item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
+ mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
+ }
+ }
+ }
+
+ for(size_t i = 0; mi.size() < MAX_MAIL_ITEMS && i < questMailLoot.quest_items.size(); ++i)
+ {
+ if(LootItem* lootitem = questMailLoot.LootItemInSlot(i+questMailLoot.items.size(),this))
+ {
+ if(Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,this))
+ {
+ 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(this, mailType, MAIL_STATIONERY_NORMAL, senderGuidOrEntry, GetGUIDLow(), "", 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE,pQuest->GetRewMailDelaySecs(),pQuest->GetRewMailTemplateId());
+ }
+
+ if(pQuest->IsDaily())
+ SetDailyQuestStatus(quest_id);
+
+ if ( !pQuest->IsRepeatable() )
+ SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE);
+ else
+ SetQuestStatus(quest_id, QUEST_STATUS_NONE);
+
+ q_status.m_rewarded = true;
+
+ if(announce)
+ SendQuestReward( pQuest, XP, questGiver );
+
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+}
+
+void Player::FailQuest( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ IncompleteQuest( quest_id );
+
+ uint16 log_slot = FindQuestSlot( quest_id );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ {
+ SetQuestSlotTimer(log_slot, 1 );
+ SetQuestSlotState(log_slot,QUEST_STATE_FAIL);
+ }
+ SendQuestFailed( quest_id );
+ }
+}
+
+void Player::FailTimedQuest( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ QuestStatusData& q_status = mQuestStatus[quest_id];
+
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+ q_status.m_timer = 0;
+
+ IncompleteQuest( quest_id );
+
+ uint16 log_slot = FindQuestSlot( quest_id );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ {
+ SetQuestSlotTimer(log_slot, 1 );
+ SetQuestSlotState(log_slot,QUEST_STATE_FAIL);
+ }
+ SendQuestTimerFailed( quest_id );
+ }
+}
+
+bool Player::SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg )
+{
+ int32 zoneOrSort = qInfo->GetZoneOrSort();
+ int32 skillOrClass = qInfo->GetSkillOrClass();
+
+ // skip zone zoneOrSort and 0 case skillOrClass
+ if( zoneOrSort >= 0 && skillOrClass == 0 )
+ return true;
+
+ int32 questSort = -zoneOrSort;
+ uint8 reqSortClass = ClassByQuestSort(questSort);
+
+ // check class sort cases in zoneOrSort
+ if( reqSortClass != 0 && getClass() != reqSortClass)
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+
+ // check class
+ if( skillOrClass < 0 )
+ {
+ uint8 reqClass = -int32(skillOrClass);
+ if(getClass() != reqClass)
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+ // check skill
+ else if( skillOrClass > 0 )
+ {
+ uint32 reqSkill = skillOrClass;
+ if( GetSkillValue( reqSkill ) < qInfo->GetRequiredSkillValue() )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Player::SatisfyQuestLevel( Quest const* qInfo, bool msg )
+{
+ if( getLevel() < qInfo->GetMinLevel() )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ return true;
+}
+
+bool Player::SatisfyQuestLog( bool msg )
+{
+ // exist free slot
+ if( FindQuestSlot(0) < MAX_QUEST_LOG_SIZE )
+ return true;
+
+ if( msg )
+ {
+ WorldPacket data( SMSG_QUESTLOG_FULL, 0 );
+ GetSession()->SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent QUEST_LOG_FULL_MESSAGE" );
+ }
+ return false;
+}
+
+bool Player::SatisfyQuestPreviousQuest( Quest const* qInfo, bool msg )
+{
+ // No previous quest (might be first quest in a series)
+ if( qInfo->prevQuests.empty())
+ return true;
+
+ for(Quest::PrevQuests::const_iterator iter = qInfo->prevQuests.begin(); iter != qInfo->prevQuests.end(); ++iter )
+ {
+ uint32 prevId = abs(*iter);
+
+ QuestStatusMap::iterator i_prevstatus = mQuestStatus.find( prevId );
+ Quest const* qPrevInfo = objmgr.GetQuestTemplate(prevId);
+
+ if( qPrevInfo && i_prevstatus != mQuestStatus.end() )
+ {
+ // If any of the positive previous quests completed, return true
+ if( *iter > 0 && i_prevstatus->second.m_rewarded )
+ {
+ // skip one-from-all exclusive group
+ if(qPrevInfo->GetExclusiveGroup() >= 0)
+ return true;
+
+ // each-from-all exclusive group ( < 0)
+ // can be start if only all quests in prev quest exclusive group complited and rewarded
+ ObjectMgr::ExclusiveQuestGroups::iterator iter = objmgr.mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
+ ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
+
+ assert(iter!=end); // always must be found if qPrevInfo->ExclusiveGroup != 0
+
+ for(; iter != end; ++iter)
+ {
+ uint32 exclude_Id = iter->second;
+
+ // skip checked quest id, only state of other quests in group is interesting
+ if(exclude_Id == prevId)
+ continue;
+
+ QuestStatusMap::iterator i_exstatus = mQuestStatus.find( exclude_Id );
+
+ // alternative quest from group also must be completed and rewarded(reported)
+ if( i_exstatus == mQuestStatus.end() || !i_exstatus->second.m_rewarded )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+ return true;
+ }
+ // If any of the negative previous quests active, return true
+ if( *iter < 0 && (i_prevstatus->second.m_status == QUEST_STATUS_INCOMPLETE
+ || (i_prevstatus->second.m_status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(prevId))))
+ {
+ // skip one-from-all exclusive group
+ if(qPrevInfo->GetExclusiveGroup() >= 0)
+ return true;
+
+ // each-from-all exclusive group ( < 0)
+ // can be start if only all quests in prev quest exclusive group active
+ ObjectMgr::ExclusiveQuestGroups::iterator iter = objmgr.mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
+ ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
+
+ assert(iter!=end); // always must be found if qPrevInfo->ExclusiveGroup != 0
+
+ for(; iter != end; ++iter)
+ {
+ uint32 exclude_Id = iter->second;
+
+ // skip checked quest id, only state of other quests in group is interesting
+ if(exclude_Id == prevId)
+ continue;
+
+ QuestStatusMap::iterator i_exstatus = mQuestStatus.find( exclude_Id );
+
+ // alternative quest from group also must be active
+ if( i_exstatus == mQuestStatus.end() ||
+ i_exstatus->second.m_status != QUEST_STATUS_INCOMPLETE &&
+ (i_prevstatus->second.m_status != QUEST_STATUS_COMPLETE || GetQuestRewardStatus(prevId)) )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ }
+
+ // Has only positive prev. quests in non-rewarded state
+ // and negative prev. quests in non-active state
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+
+ return false;
+}
+
+bool Player::SatisfyQuestRace( Quest const* qInfo, bool msg )
+{
+ uint32 reqraces = qInfo->GetRequiredRaces();
+ if ( reqraces == 0 )
+ return true;
+ if( (reqraces & getRaceMask()) == 0 )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_QUEST_FAILED_WRONG_RACE );
+ return false;
+ }
+ return true;
+}
+
+bool Player::SatisfyQuestReputation( Quest const* qInfo, bool msg )
+{
+ uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); //Min required rep
+ if(fIdMin && GetReputation(fIdMin) < qInfo->GetRequiredMinRepValue())
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+
+ uint32 fIdMax = qInfo->GetRequiredMaxRepFaction(); //Max required rep
+ if(fIdMax && GetReputation(fIdMax) >= qInfo->GetRequiredMaxRepValue())
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+
+ return true;
+}
+
+bool Player::SatisfyQuestStatus( Quest const* qInfo, bool msg )
+{
+ QuestStatusMap::iterator itr = mQuestStatus.find( qInfo->GetQuestId() );
+ if ( itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_QUEST_ALREADY_ON );
+ return false;
+ }
+ return true;
+}
+
+bool Player::SatisfyQuestTimed( Quest const* qInfo, bool msg )
+{
+ if ( (find(m_timedquests.begin(), m_timedquests.end(), qInfo->GetQuestId()) != m_timedquests.end()) && qInfo->HasFlag(QUEST_MANGOS_FLAGS_TIMED) )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_QUEST_ONLY_ONE_TIMED );
+ return false;
+ }
+ return true;
+}
+
+bool Player::SatisfyQuestExclusiveGroup( Quest const* qInfo, bool msg )
+{
+ // non positive exclusive group, if > 0 then can be start if any other quest in exclusive group already started/completed
+ if(qInfo->GetExclusiveGroup() <= 0)
+ return true;
+
+ ObjectMgr::ExclusiveQuestGroups::iterator iter = objmgr.mExclusiveQuestGroups.lower_bound(qInfo->GetExclusiveGroup());
+ ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qInfo->GetExclusiveGroup());
+
+ assert(iter!=end); // always must be found if qInfo->ExclusiveGroup != 0
+
+ for(; iter != end; ++iter)
+ {
+ uint32 exclude_Id = iter->second;
+
+ // skip checked quest id, only state of other quests in group is interesting
+ if(exclude_Id == qInfo->GetQuestId())
+ continue;
+
+ QuestStatusMap::iterator i_exstatus = mQuestStatus.find( exclude_Id );
+
+ // alternative quest already started or completed
+ if( i_exstatus != mQuestStatus.end()
+ && (i_exstatus->second.m_status == QUEST_STATUS_COMPLETE || i_exstatus->second.m_status == QUEST_STATUS_INCOMPLETE) )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Player::SatisfyQuestNextChain( Quest const* qInfo, bool msg )
+{
+ if(!qInfo->GetNextQuestInChain())
+ return true;
+
+ // next quest in chain already started or completed
+ QuestStatusMap::iterator itr = mQuestStatus.find( qInfo->GetNextQuestInChain() );
+ if( itr != mQuestStatus.end()
+ && (itr->second.m_status == QUEST_STATUS_COMPLETE || itr->second.m_status == QUEST_STATUS_INCOMPLETE) )
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+
+ // check for all quests further up the chain
+ // only necessary if there are quest chains with more than one quest that can be skipped
+ //return SatisfyQuestNextChain( qInfo->GetNextQuestInChain(), msg );
+ return true;
+}
+
+bool Player::SatisfyQuestPrevChain( Quest const* qInfo, bool msg )
+{
+ // No previous quest in chain
+ if( qInfo->prevChainQuests.empty())
+ return true;
+
+ for(Quest::PrevChainQuests::const_iterator iter = qInfo->prevChainQuests.begin(); iter != qInfo->prevChainQuests.end(); ++iter )
+ {
+ uint32 prevId = *iter;
+
+ QuestStatusMap::iterator i_prevstatus = mQuestStatus.find( prevId );
+
+ if( i_prevstatus != mQuestStatus.end() )
+ {
+ // If any of the previous quests in chain active, return false
+ if( i_prevstatus->second.m_status == QUEST_STATUS_INCOMPLETE
+ || (i_prevstatus->second.m_status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(prevId)))
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
+ return false;
+ }
+ }
+
+ // check for all quests further down the chain
+ // only necessary if there are quest chains with more than one quest that can be skipped
+ //if( !SatisfyQuestPrevChain( prevId, msg ) )
+ // return false;
+ }
+
+ // No previous quest in chain active
+ return true;
+}
+
+bool Player::SatisfyQuestDay( Quest const* qInfo, bool msg )
+{
+ if(!qInfo->IsDaily())
+ return true;
+
+ bool have_slot = false;
+ for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ {
+ uint32 id = GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx);
+ if(qInfo->GetQuestId()==id)
+ return false;
+
+ if(!id)
+ have_slot = true;
+ }
+
+ if(!have_slot)
+ {
+ if( msg )
+ SendCanTakeQuestResponse( INVALIDREASON_DAILY_QUESTS_REMAINING );
+ return false;
+ }
+
+ return true;
+}
+
+bool Player::GiveQuestSourceItem( Quest const *pQuest )
+{
+ uint32 srcitem = pQuest->GetSrcItemId();
+ if( srcitem > 0 )
+ {
+ uint32 count = pQuest->GetSrcItemCount();
+ if( count <= 0 )
+ count = 1;
+
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, srcitem, count );
+ if( msg == EQUIP_ERR_OK )
+ {
+ Item * item = StoreNewItem(dest, srcitem, true);
+ SendNewItem(item, count, true, false);
+ return true;
+ }
+ // player already have max amount required item, just report success
+ else if( msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
+ return true;
+ else
+ SendEquipError( msg, NULL, NULL );
+ return false;
+ }
+
+ return true;
+}
+
+bool Player::TakeQuestSourceItem( uint32 quest_id, bool msg )
+{
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+ if( qInfo )
+ {
+ uint32 srcitem = qInfo->GetSrcItemId();
+ if( srcitem > 0 )
+ {
+ uint32 count = qInfo->GetSrcItemCount();
+ if( count <= 0 )
+ count = 1;
+
+ // exist one case when destroy source quest item not possible:
+ // non un-equippable item (equipped non-empty bag, for example)
+ uint8 res = CanUnequipItems(srcitem,count);
+ if(res != EQUIP_ERR_OK)
+ {
+ if(msg)
+ SendEquipError( res, NULL, NULL );
+ return false;
+ }
+
+ DestroyItemCount(srcitem, count, true, true);
+ }
+ }
+ return true;
+}
+
+bool Player::GetQuestRewardStatus( uint32 quest_id ) const
+{
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+ if( qInfo )
+ {
+ // for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
+ QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
+ if( itr != mQuestStatus.end() && itr->second.m_status != QUEST_STATUS_NONE
+ && !qInfo->IsRepeatable() )
+ return itr->second.m_rewarded;
+
+ return false;
+ }
+ return false;
+}
+
+QuestStatus Player::GetQuestStatus( uint32 quest_id ) const
+{
+ if( quest_id )
+ {
+ QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
+ if( itr != mQuestStatus.end() )
+ return itr->second.m_status;
+ }
+ return QUEST_STATUS_NONE;
+}
+
+bool Player::CanShareQuest(uint32 quest_id) const
+{
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+ if( qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE) )
+ {
+ QuestStatusMap::const_iterator itr = mQuestStatus.find( quest_id );
+ if( itr != mQuestStatus.end() )
+ return itr->second.m_status == QUEST_STATUS_NONE || itr->second.m_status == QUEST_STATUS_INCOMPLETE;
+ }
+ return false;
+}
+
+void Player::SetQuestStatus( uint32 quest_id, QuestStatus status )
+{
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+ if( qInfo )
+ {
+ if( status == QUEST_STATUS_NONE || status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_COMPLETE )
+ {
+ if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) )
+ m_timedquests.erase(qInfo->GetQuestId());
+ }
+
+ QuestStatusData& q_status = mQuestStatus[quest_id];
+
+ q_status.m_status = status;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+ }
+
+ UpdateForQuestsGO();
+}
+
+// not used in MaNGOS, but used in scripting code
+uint32 Player::GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry)
+{
+ Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
+ if( !qInfo )
+ return 0;
+
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ if ( qInfo->ReqCreatureOrGOId[j] == entry )
+ return mQuestStatus[quest_id].m_creatureOrGOcount[j];
+
+ return 0;
+}
+
+void Player::AdjustQuestReqItemCount( Quest const* pQuest )
+{
+ if ( pQuest->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ {
+ for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ uint32 reqitemcount = pQuest->ReqItemCount[i];
+ if( reqitemcount != 0 )
+ {
+ uint32 quest_id = pQuest->GetQuestId();
+ uint32 curitemcount = GetItemCount(pQuest->ReqItemId[i],true);
+
+ QuestStatusData& q_status = mQuestStatus[quest_id];
+ q_status.m_itemcount[i] = std::min(curitemcount, reqitemcount);
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+ }
+ }
+ }
+}
+
+uint16 Player::FindQuestSlot( uint32 quest_id ) const
+{
+ for ( uint16 i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ if ( GetQuestSlotQuestId(i) == quest_id )
+ return i;
+
+ return MAX_QUEST_LOG_SIZE;
+}
+
+void Player::AreaExploredOrEventHappens( uint32 questId )
+{
+ if( questId )
+ {
+ uint16 log_slot = FindQuestSlot( questId );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ {
+ QuestStatusData& q_status = mQuestStatus[questId];
+
+ if(!q_status.m_explored)
+ {
+ q_status.m_explored = true;
+ if (q_status.uState != QUEST_NEW)
+ q_status.uState = QUEST_CHANGED;
+ }
+ }
+ if( CanCompleteQuest( questId ) )
+ CompleteQuest( questId );
+ }
+}
+
+//not used in mangosd, function for external script library
+void Player::GroupEventHappens( uint32 questId, WorldObject const* pEventObject )
+{
+ if( Group *pGroup = GetGroup() )
+ {
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player *pGroupGuy = itr->getSource();
+
+ // for any leave or dead (with not released body) group member at appropriate distance
+ if( pGroupGuy && pGroupGuy->IsAtGroupRewardDistance(pEventObject) && !pGroupGuy->GetCorpse() )
+ pGroupGuy->AreaExploredOrEventHappens(questId);
+ }
+ }
+ else
+ AreaExploredOrEventHappens(questId);
+}
+
+void Player::ItemAddedQuestCheck( uint32 entry, uint32 count )
+{
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if ( questid == 0 )
+ continue;
+
+ QuestStatusData& q_status = mQuestStatus[questid];
+
+ if ( q_status.m_status != QUEST_STATUS_INCOMPLETE )
+ continue;
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if( !qInfo || !qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ continue;
+
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ uint32 reqitem = qInfo->ReqItemId[j];
+ if ( reqitem == entry )
+ {
+ uint32 reqitemcount = qInfo->ReqItemCount[j];
+ uint32 curitemcount = q_status.m_itemcount[j];
+ if ( curitemcount < reqitemcount )
+ {
+ uint32 additemcount = ( curitemcount + count <= reqitemcount ? count : reqitemcount - curitemcount);
+ q_status.m_itemcount[j] += additemcount;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+
+ SendQuestUpdateAddItem( qInfo, j, additemcount );
+ }
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+ return;
+ }
+ }
+ }
+ UpdateForQuestsGO();
+}
+
+void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count )
+{
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if(!questid)
+ continue;
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if ( !qInfo )
+ continue;
+ if( !qInfo->HasFlag( QUEST_MANGOS_FLAGS_DELIVER ) )
+ continue;
+
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ uint32 reqitem = qInfo->ReqItemId[j];
+ if ( reqitem == entry )
+ {
+ QuestStatusData& q_status = mQuestStatus[questid];
+
+ uint32 reqitemcount = qInfo->ReqItemCount[j];
+ uint32 curitemcount;
+ if( q_status.m_status != QUEST_STATUS_COMPLETE )
+ curitemcount = q_status.m_itemcount[j];
+ else
+ curitemcount = GetItemCount(entry,true);
+ if ( curitemcount < reqitemcount + count )
+ {
+ uint32 remitemcount = ( curitemcount <= reqitemcount ? count : count + reqitemcount - curitemcount);
+ q_status.m_itemcount[j] = curitemcount - remitemcount;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+
+ IncompleteQuest( questid );
+ }
+ return;
+ }
+ }
+ }
+ UpdateForQuestsGO();
+}
+
+void Player::KilledMonster( uint32 entry, uint64 guid )
+{
+ uint32 addkillcount = 1;
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if(!questid)
+ continue;
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if( !qInfo )
+ continue;
+ // just if !ingroup || !noraidgroup || raidgroup
+ QuestStatusData& q_status = mQuestStatus[questid];
+ if( q_status.m_status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->GetType() == QUEST_TYPE_RAID))
+ {
+ if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST) )
+ {
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ // skip GO activate objective or none
+ if(qInfo->ReqCreatureOrGOId[j] <=0)
+ continue;
+
+ // skip Cast at creature objective
+ if(qInfo->ReqSpell[j] !=0 )
+ continue;
+
+ uint32 reqkill = qInfo->ReqCreatureOrGOId[j];
+
+ if ( reqkill == entry )
+ {
+ uint32 reqkillcount = qInfo->ReqCreatureOrGOCount[j];
+ uint32 curkillcount = q_status.m_creatureOrGOcount[j];
+ if ( curkillcount < reqkillcount )
+ {
+ q_status.m_creatureOrGOcount[j] = curkillcount + addkillcount;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+
+ SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curkillcount, addkillcount);
+ }
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+
+ // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
+ continue;
+ }
+ }
+ }
+ }
+ }
+}
+
+void Player::CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id )
+{
+ bool isCreature = IS_CREATURE_GUID(guid);
+
+ uint32 addCastCount = 1;
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if(!questid)
+ continue;
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if ( !qInfo )
+ continue;
+
+ QuestStatusData& q_status = mQuestStatus[questid];
+
+ if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
+ {
+ if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST ) )
+ {
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ // skip kill creature objective (0) or wrong spell casts
+ if(qInfo->ReqSpell[j] != spell_id )
+ continue;
+
+ uint32 reqTarget = 0;
+
+ if(isCreature)
+ {
+ // creature activate objectives
+ if(qInfo->ReqCreatureOrGOId[j] > 0)
+ // checked at quest_template loading
+ reqTarget = qInfo->ReqCreatureOrGOId[j];
+ }
+ else
+ {
+ // GO activate objective
+ if(qInfo->ReqCreatureOrGOId[j] < 0)
+ // checked at quest_template loading
+ reqTarget = - qInfo->ReqCreatureOrGOId[j];
+ }
+
+ // other not this creature/GO related objectives
+ if( reqTarget != entry )
+ continue;
+
+ uint32 reqCastCount = qInfo->ReqCreatureOrGOCount[j];
+ uint32 curCastCount = q_status.m_creatureOrGOcount[j];
+ if ( curCastCount < reqCastCount )
+ {
+ q_status.m_creatureOrGOcount[j] = curCastCount + addCastCount;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+
+ SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curCastCount, addCastCount);
+ }
+
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+
+ // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
+ break;
+ }
+ }
+ }
+ }
+}
+
+void Player::TalkedToCreature( uint32 entry, uint64 guid )
+{
+ uint32 addTalkCount = 1;
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if(!questid)
+ continue;
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if ( !qInfo )
+ continue;
+
+ QuestStatusData& q_status = mQuestStatus[questid];
+
+ if ( q_status.m_status == QUEST_STATUS_INCOMPLETE )
+ {
+ if( qInfo->HasFlag( QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO ) )
+ {
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ // skip spell casts and Gameobject objectives
+ if(qInfo->ReqSpell[j] > 0 || qInfo->ReqCreatureOrGOId[j] < 0)
+ continue;
+
+ uint32 reqTarget = 0;
+
+ if(qInfo->ReqCreatureOrGOId[j] > 0) // creature activate objectives
+ // checked at quest_template loading
+ reqTarget = qInfo->ReqCreatureOrGOId[j];
+ else
+ continue;
+
+ if ( reqTarget == entry )
+ {
+ uint32 reqTalkCount = qInfo->ReqCreatureOrGOCount[j];
+ uint32 curTalkCount = q_status.m_creatureOrGOcount[j];
+ if ( curTalkCount < reqTalkCount )
+ {
+ q_status.m_creatureOrGOcount[j] = curTalkCount + addTalkCount;
+ if (q_status.uState != QUEST_NEW) q_status.uState = QUEST_CHANGED;
+
+ SendQuestUpdateAddCreatureOrGo( qInfo, guid, j, curTalkCount, addTalkCount);
+ }
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+
+ // same objective target can be in many active quests, but not in 2 objectives for single quest (code optimization).
+ continue;
+ }
+ }
+ }
+ }
+ }
+}
+
+void Player::MoneyChanged( uint32 count )
+{
+ for( int i = 0; i < MAX_QUEST_LOG_SIZE; i++ )
+ {
+ uint32 questid = GetQuestSlotQuestId(i);
+ if (!questid)
+ continue;
+
+ Quest const* qInfo = objmgr.GetQuestTemplate(questid);
+ if( qInfo && qInfo->GetRewOrReqMoney() < 0 )
+ {
+ QuestStatusData& q_status = mQuestStatus[questid];
+
+ if( q_status.m_status == QUEST_STATUS_INCOMPLETE )
+ {
+ if(int32(count) >= -qInfo->GetRewOrReqMoney())
+ {
+ if ( CanCompleteQuest( questid ) )
+ CompleteQuest( questid );
+ }
+ }
+ else if( q_status.m_status == QUEST_STATUS_COMPLETE )
+ {
+ if(int32(count) < -qInfo->GetRewOrReqMoney())
+ IncompleteQuest( questid );
+ }
+ }
+ }
+}
+
+bool Player::HasQuestForItem( uint32 itemid ) const
+{
+ for( QuestStatusMap::const_iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
+ {
+ QuestStatusData const& q_status = i->second;
+
+ if (q_status.m_status == QUEST_STATUS_INCOMPLETE)
+ {
+ Quest const* qinfo = objmgr.GetQuestTemplate(i->first);
+ if(!qinfo)
+ continue;
+
+ // hide quest if player is in raid-group and quest is no raid quest
+ if(GetGroup() && GetGroup()->isRaidGroup() && qinfo->GetType() != QUEST_TYPE_RAID)
+ continue;
+
+ // There should be no mixed ReqItem/ReqSource drop
+ // This part for ReqItem drop
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ if(itemid == qinfo->ReqItemId[j] && q_status.m_itemcount[j] < qinfo->ReqItemCount[j] )
+ return true;
+ }
+ // This part - for ReqSource
+ for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; j++)
+ {
+ // examined item is a source item
+ if (qinfo->ReqSourceId[j] == itemid && qinfo->ReqSourceRef[j] > 0 && qinfo->ReqSourceRef[j] <= QUEST_OBJECTIVES_COUNT)
+ {
+ uint32 idx = qinfo->ReqSourceRef[j]-1;
+
+ // total count of created ReqItems and SourceItems is less than ReqItemCount
+ if(qinfo->ReqItemId[idx] != 0 &&
+ q_status.m_itemcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqItemCount[idx] * qinfo->ReqSourceCount[j])
+ return true;
+
+ // total count of casted ReqCreatureOrGOs and SourceItems is less than ReqCreatureOrGOCount
+ if (qinfo->ReqCreatureOrGOId[idx] != 0)
+ {
+ if(q_status.m_creatureOrGOcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqCreatureOrGOCount[idx] * qinfo->ReqSourceCount[j])
+ return true;
+ }
+ // spell with SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT (with script) case
+ else if(qinfo->ReqSpell[idx] != 0)
+ {
+ // not casted and need more reagents/item for use.
+ if(!q_status.m_explored && GetItemCount(itemid,true) < qinfo->ReqSourceCount[j])
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void Player::SendQuestComplete( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ WorldPacket data( SMSG_QUESTUPDATE_COMPLETE, 4 );
+ data << quest_id;
+ GetSession()->SendPacket( &data );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_COMPLETE quest = %u", quest_id );
+ }
+}
+
+void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGiver )
+{
+ uint32 questid = pQuest->GetQuestId();
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_QUEST_COMPLETE quest = %u", questid );
+ WorldPacket data( SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4+4+pQuest->GetRewItemsCount()*8) );
+ data << questid;
+ data << uint32(0x03);
+
+ if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
+ {
+ data << XP;
+ data << uint32(pQuest->GetRewOrReqMoney());
+ }
+ else
+ {
+ data << uint32(0);
+ data << uint32(pQuest->GetRewOrReqMoney() + int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)));
+ }
+ data << uint32(0); // new 2.3.0, HonorPoints?
+ data << uint32( pQuest->GetRewItemsCount() ); // max is 5
+
+ for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i)
+ {
+ if ( pQuest->RewItemId[i] > 0 )
+ data << pQuest->RewItemId[i] << pQuest->RewItemCount[i];
+ else
+ data << uint32(0) << uint32(0);
+ }
+ GetSession()->SendPacket( &data );
+
+ if (pQuest->GetQuestCompleteScript() != 0)
+ sWorld.ScriptsStart(sQuestEndScripts, pQuest->GetQuestCompleteScript(), questGiver, this);
+}
+
+void Player::SendQuestFailed( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ WorldPacket data( SMSG_QUESTGIVER_QUEST_FAILED, 4 );
+ data << quest_id;
+ GetSession()->SendPacket( &data );
+ sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED");
+ }
+}
+
+void Player::SendQuestTimerFailed( uint32 quest_id )
+{
+ if( quest_id )
+ {
+ WorldPacket data( SMSG_QUESTUPDATE_FAILEDTIMER, 4 );
+ data << quest_id;
+ GetSession()->SendPacket( &data );
+ sLog.outDebug("WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
+ }
+}
+
+void Player::SendCanTakeQuestResponse( uint32 msg )
+{
+ WorldPacket data( SMSG_QUESTGIVER_QUEST_INVALID, 4 );
+ data << uint32(msg);
+ GetSession()->SendPacket( &data );
+ sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID");
+}
+
+void Player::SendPushToPartyResponse( Player *pPlayer, uint32 msg )
+{
+ if( pPlayer )
+ {
+ WorldPacket data( MSG_QUEST_PUSH_RESULT, (8+1) );
+ data << uint64(pPlayer->GetGUID());
+ data << uint8(msg); // valid values: 0-8
+ GetSession()->SendPacket( &data );
+ sLog.outDebug("WORLD: Sent MSG_QUEST_PUSH_RESULT");
+ }
+}
+
+void Player::SendQuestUpdateAddItem( Quest const* pQuest, uint32 item_idx, uint32 count )
+{
+ WorldPacket data( SMSG_QUESTUPDATE_ADD_ITEM, (4+4) );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_ADD_ITEM" );
+ data << pQuest->ReqItemId[item_idx];
+ data << count;
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendQuestUpdateAddCreatureOrGo( Quest const* pQuest, uint64 guid, uint32 creatureOrGO_idx, uint32 old_count, uint32 add_count )
+{
+ assert(old_count + add_count < 256 && "mob/GO count store in 8 bits 2^8 = 256 (0..256)");
+
+ int32 entry = pQuest->ReqCreatureOrGOId[ creatureOrGO_idx ];
+ if (entry < 0)
+ // client expected gameobject template id in form (id|0x80000000)
+ entry = (-entry) | 0x80000000;
+
+ WorldPacket data( SMSG_QUESTUPDATE_ADD_KILL, (4*4+8) );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTUPDATE_ADD_KILL" );
+ data << uint32(pQuest->GetQuestId());
+ data << uint32(entry);
+ data << uint32(old_count + add_count);
+ data << uint32(pQuest->ReqCreatureOrGOCount[ creatureOrGO_idx ]);
+ data << uint64(guid);
+ GetSession()->SendPacket(&data);
+
+ uint16 log_slot = FindQuestSlot( pQuest->GetQuestId() );
+ if( log_slot < MAX_QUEST_LOG_SIZE)
+ SetQuestSlotCounter(log_slot,creatureOrGO_idx,GetQuestSlotCounter(log_slot,creatureOrGO_idx)+add_count);
+}
+
+/*********************************************************/
+/*** LOAD SYSTEM ***/
+/*********************************************************/
+
+bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid )
+{
+ bool delete_result = true;
+ if(!result)
+ {
+ // 0 1 2 3 4 5 6 7 8
+ result = CharacterDatabase.PQuery("SELECT data, name, position_x, position_y, position_z, map, totaltime, leveltime, at_login FROM characters WHERE guid = '%u'",guid);
+ if(!result) return false;
+ }
+ else delete_result = false;
+
+ Field *fields = result->Fetch();
+
+ if(!LoadValues( fields[0].GetString()))
+ {
+ sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid));
+ if(delete_result) delete result;
+ return false;
+ }
+
+ // overwrite possible wrong/corrupted guid
+ SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
+
+ m_name = fields[1].GetCppString();
+
+ Relocate(fields[2].GetFloat(),fields[3].GetFloat(),fields[4].GetFloat());
+ SetMapId(fields[5].GetUInt32());
+ // the instance id is not needed at character enum
+
+ m_Played_time[0] = fields[6].GetUInt32();
+ m_Played_time[1] = fields[7].GetUInt32();
+
+ m_atLoginFlags = fields[8].GetUInt32();
+
+ // I don't see these used anywhere ..
+ /*_LoadGroup();
+
+ _LoadBoundInstances();*/
+
+ if (delete_result) delete result;
+
+ for (int i = 0; i < PLAYER_SLOTS_COUNT; i++)
+ m_items[i] = NULL;
+
+ if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) )
+ m_deathState = DEAD;
+
+ return true;
+}
+
+void Player::_LoadDeclinedNames(QueryResult* result)
+{
+ if(!result)
+ return;
+
+ if(m_declinedname)
+ delete m_declinedname;
+
+ m_declinedname = new DeclinedName;
+ Field *fields = result->Fetch();
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ m_declinedname->name[i] = fields[i].GetCppString();
+
+ delete result;
+}
+
+bool Player::LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid)
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,taxi_path FROM characters WHERE guid = '%u'",GUID_LOPART(guid));
+ if(!result)
+ return false;
+
+ Field *fields = result->Fetch();
+
+ x = fields[0].GetFloat();
+ y = fields[1].GetFloat();
+ z = fields[2].GetFloat();
+ o = fields[3].GetFloat();
+ mapid = fields[4].GetUInt32();
+ in_flight = !fields[5].GetCppString().empty();
+
+ delete result;
+ return true;
+}
+
+bool Player::LoadValuesArrayFromDB(Tokens& data, uint64 guid)
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT data FROM characters WHERE guid='%u'",GUID_LOPART(guid));
+ if( !result )
+ return false;
+
+ Field *fields = result->Fetch();
+
+ data = StrSplit(fields[0].GetCppString(), " ");
+
+ delete result;
+
+ return true;
+}
+
+uint32 Player::GetUInt32ValueFromArray(Tokens const& data, uint16 index)
+{
+ if(index >= data.size())
+ return 0;
+
+ return (uint32)atoi(data[index].c_str());
+}
+
+float Player::GetFloatValueFromArray(Tokens const& data, uint16 index)
+{
+ float result;
+ uint32 temp = Player::GetUInt32ValueFromArray(data,index);
+ memcpy(&result, &temp, sizeof(result));
+
+ return result;
+}
+
+uint32 Player::GetUInt32ValueFromDB(uint16 index, uint64 guid)
+{
+ Tokens data;
+ if(!LoadValuesArrayFromDB(data,guid))
+ return 0;
+
+ return GetUInt32ValueFromArray(data,index);
+}
+
+float Player::GetFloatValueFromDB(uint16 index, uint64 guid)
+{
+ float result;
+ uint32 temp = Player::GetUInt32ValueFromDB(index, guid);
+ memcpy(&result, &temp, sizeof(result));
+
+ return result;
+}
+
+bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
+{
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [28] [29] 30 31 32
+ //QueryResult *result = CharacterDatabase.PQuery("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);
+ QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM);
+
+ if(!result)
+ {
+ sLog.outError("ERROR: Player (GUID: %u) not found in table `characters`, can't load. ",guid);
+ return false;
+ }
+
+ Field *fields = result->Fetch();
+
+ uint32 dbAccountId = fields[1].GetUInt32();
+
+ // check if the character's account in the db and the logged in account match.
+ // player should be able to load/delete character only with correct account!
+ if( dbAccountId != GetSession()->GetAccountId() )
+ {
+ sLog.outError("ERROR: Player (GUID: %u) loading from wrong account (is: %u, should be: %u)",guid,GetSession()->GetAccountId(),dbAccountId);
+ delete result;
+ return false;
+ }
+
+ Object::_Create( guid, 0, HIGHGUID_PLAYER );
+
+ m_name = fields[3].GetCppString();
+
+ // check name limitations
+ if(!ObjectMgr::IsValidName(m_name) || GetSession()->GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(m_name))
+ {
+ delete result;
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid ='%u'", uint32(AT_LOGIN_RENAME),guid);
+ return false;
+ }
+
+ if(!LoadValues( fields[2].GetString()))
+ {
+ sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid));
+ delete result;
+ return false;
+ }
+
+ // overwrite possible wrong/corrupted guid
+ SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
+
+ // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory)
+ for(uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
+ {
+ SetUInt64Value( (uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2) ), 0 );
+ SetVisibleItemSlot(slot,NULL);
+
+ if (m_items[slot])
+ {
+ delete m_items[slot];
+ m_items[slot] = NULL;
+ }
+ }
+
+ // update money limits
+ if(GetMoney() > MAX_MONEY_AMOUNT)
+ SetMoney(MAX_MONEY_AMOUNT);
+
+ sLog.outDebug("Load Basic value of player %s is: ", m_name.c_str());
+ outDebugValues();
+
+ m_race = fields[4].GetUInt8();
+ //Need to call it to initialize m_team (m_team can be calculated from m_race)
+ //Other way is to saves m_team into characters table.
+ setFactionForRace(m_race);
+ SetCharm(0);
+
+ m_class = fields[5].GetUInt8();
+
+ PlayerInfo const *info = objmgr.GetPlayerInfo(m_race, m_class);
+ if(!info)
+ {
+ sLog.outError("Player have incorrect race/class pair. Can't be loaded.");
+ delete result;
+ return false;
+ }
+
+ InitPrimaryProffesions(); // to max set before any spell loaded
+
+ uint32 transGUID = fields[24].GetUInt32();
+ Relocate(fields[6].GetFloat(),fields[7].GetFloat(),fields[8].GetFloat(),fields[10].GetFloat());
+ SetMapId(fields[9].GetUInt32());
+ SetDifficulty(fields[32].GetUInt32()); // may be changed in _LoadGroup
+
+ _LoadGroup(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP));
+
+ // check arena teams integrity
+ for(uint32 arena_slot = 0; arena_slot < MAX_ARENA_SLOT; ++arena_slot)
+ {
+ uint32 arena_team_id = GetArenaTeamId(arena_slot);
+ if(!arena_team_id)
+ continue;
+
+ if(ArenaTeam * at = objmgr.GetArenaTeamById(arena_team_id))
+ if(at->HaveMember(GetGUID()))
+ continue;
+
+ // arena team not exist or not member, cleanup fields
+ for(int j =0; j < 6; ++j)
+ SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arena_slot * 6 + j, 0);
+ }
+
+ _LoadBoundInstances(holder->GetResult(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES));
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Player (guidlow %d) have invalid coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",guid,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
+
+ SetMapId(info->mapId);
+ Relocate(info->positionX,info->positionY,info->positionZ,0.0f);
+
+ transGUID = 0;
+
+ m_movementInfo.t_x = 0.0f;
+ m_movementInfo.t_y = 0.0f;
+ m_movementInfo.t_z = 0.0f;
+ m_movementInfo.t_o = 0.0f;
+ }
+
+ // load the player's map here if it's not already loaded
+ Map *map = GetMap();
+ // since the player may not be bound to the map yet, make sure subsequent
+ // getmap calls won't create new maps
+ SetInstanceId(map->GetInstanceId());
+
+ SaveRecallPosition();
+
+ if (transGUID != 0)
+ {
+ m_movementInfo.t_x = fields[20].GetFloat();
+ m_movementInfo.t_y = fields[21].GetFloat();
+ m_movementInfo.t_z = fields[22].GetFloat();
+ m_movementInfo.t_o = fields[23].GetFloat();
+
+ if( !MaNGOS::IsValidMapCoord(
+ GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y,
+ GetPositionZ()+m_movementInfo.t_z,GetOrientation()+m_movementInfo.t_o) ||
+ // transport size limited
+ m_movementInfo.t_x > 50 || m_movementInfo.t_y > 50 || m_movementInfo.t_z > 50 )
+ {
+ sLog.outError("ERROR: Player (guidlow %d) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.",
+ guid,GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y,
+ GetPositionZ()+m_movementInfo.t_z,GetOrientation()+m_movementInfo.t_o);
+
+ SetMapId(info->mapId);
+ Relocate(info->positionX,info->positionY,info->positionZ,0.0f);
+
+ m_movementInfo.t_x = 0.0f;
+ m_movementInfo.t_y = 0.0f;
+ m_movementInfo.t_z = 0.0f;
+ m_movementInfo.t_o = 0.0f;
+
+ transGUID = 0;
+ }
+ }
+
+ if (transGUID != 0)
+ {
+ for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter)
+ {
+ if( (*iter)->GetGUIDLow() == transGUID)
+ {
+ m_transport = *iter;
+ m_transport->AddPassenger(this);
+ SetMapId(m_transport->GetMapId());
+ break;
+ }
+ }
+
+ if(!m_transport)
+ {
+ sLog.outError("ERROR: Player (guidlow %d) have invalid transport guid (%u). Teleport to default race/class locations.",
+ guid,transGUID);
+
+ SetMapId(info->mapId);
+ Relocate(info->positionX,info->positionY,info->positionZ,0.0f);
+
+ m_movementInfo.t_x = 0.0f;
+ m_movementInfo.t_y = 0.0f;
+ m_movementInfo.t_z = 0.0f;
+ m_movementInfo.t_o = 0.0f;
+
+ transGUID = 0;
+ }
+ }
+
+ time_t now = time(NULL);
+ time_t logoutTime = time_t(fields[16].GetUInt64());
+
+ // since last logout (in seconds)
+ uint64 time_diff = uint64(now - logoutTime);
+
+ // set value, including drunk invisibility detection
+ // calculate sobering. after 15 minutes logged out, the player will be sober again
+ float soberFactor;
+ if(time_diff > 15*MINUTE)
+ soberFactor = 0;
+ else
+ soberFactor = 1-time_diff/(15.0f*MINUTE);
+ uint16 newDrunkenValue = uint16(soberFactor*(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE));
+ SetDrunkValue(newDrunkenValue);
+
+ m_rest_bonus = fields[15].GetFloat();
+ //speed collect rest bonus in offline, in logout, far from tavern, city (section/in hour)
+ float bubble0 = 0.031;
+ //speed collect rest bonus in offline, in logout, in tavern, city (section/in hour)
+ float bubble1 = 0.125;
+
+ if((int32)fields[16].GetUInt32() > 0)
+ {
+ float bubble = fields[17].GetUInt32() > 0
+ ? bubble1*sWorld.getRate(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY)
+ : bubble0*sWorld.getRate(RATE_REST_OFFLINE_IN_WILDERNESS);
+
+ SetRestBonus(GetRestBonus()+ time_diff*((float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)/72000)*bubble);
+ }
+
+ m_cinematic = fields[12].GetUInt32();
+ m_Played_time[0]= fields[13].GetUInt32();
+ m_Played_time[1]= fields[14].GetUInt32();
+
+ m_resetTalentsCost = fields[18].GetUInt32();
+ m_resetTalentsTime = time_t(fields[19].GetUInt64());
+
+ // reserve some flags
+ uint32 old_safe_flags = GetUInt32Value(PLAYER_FLAGS) & ( PLAYER_FLAGS_HIDE_CLOAK | PLAYER_FLAGS_HIDE_HELM );
+
+ if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM) )
+ SetUInt32Value(PLAYER_FLAGS, 0 | old_safe_flags);
+
+ m_taxi.LoadTaxiMask( fields[11].GetString() ); // must be before InitTaxiNodesForLevel
+
+ uint32 gmstate = fields[25].GetUInt32();
+
+ m_stableSlots = fields[26].GetUInt32();
+ if(m_stableSlots > 2)
+ {
+ sLog.outError("Player can have not more 2 stable slots, but have in DB %u",uint32(m_stableSlots));
+ m_stableSlots = 2;
+ }
+
+ m_atLoginFlags = fields[27].GetUInt32();
+
+ // Honor system
+ // Update Honor kills data
+ m_lastHonorUpdateTime = logoutTime;
+ UpdateHonorFields();
+
+ m_deathExpireTime = (time_t)fields[30].GetUInt64();
+ if(m_deathExpireTime > now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP)
+ m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP-1;
+
+ std::string taxi_nodes = fields[31].GetCppString();
+
+ delete result;
+
+ // clear channel spell data (if saved at channel spell casting)
+ SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, 0);
+ SetUInt32Value(UNIT_CHANNEL_SPELL,0);
+
+ // clear charm/summon related fields
+ SetUInt64Value(UNIT_FIELD_CHARM,0);
+ SetUInt64Value(UNIT_FIELD_SUMMON,0);
+ SetUInt64Value(UNIT_FIELD_CHARMEDBY,0);
+ SetUInt64Value(UNIT_FIELD_SUMMONEDBY,0);
+ SetUInt64Value(UNIT_FIELD_CREATEDBY,0);
+
+ // reset some aura modifiers before aura apply
+ SetUInt64Value(PLAYER_FARSIGHT, 0);
+ SetUInt32Value(PLAYER_TRACK_CREATURES, 0 );
+ SetUInt32Value(PLAYER_TRACK_RESOURCES, 0 );
+
+ // reset skill modifiers and set correct unlearn flags
+ for (uint32 i = 0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
+
+ // set correct unlearn bit
+ uint32 id = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
+ if(!id) continue;
+
+ SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(id);
+ if(!pSkill) continue;
+
+ // enable unlearn button for primary professions only
+ if (pSkill->categoryId == SKILL_CATEGORY_PROFESSION)
+ SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,1));
+ else
+ SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0));
+ }
+
+ // make sure the unit is considered out of combat for proper loading
+ ClearInCombat();
+
+ // make sure the unit is considered not in duel for proper loading
+ SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
+ SetUInt32Value(PLAYER_DUEL_TEAM, 0);
+
+ // remember loaded power/health values to restore after stats initialization and modifier applying
+ uint32 savedHealth = GetHealth();
+ uint32 savedPower[MAX_POWERS];
+ for(uint32 i = 0; i < MAX_POWERS; ++i)
+ savedPower[i] = GetPower(Powers(i));
+
+ // reset stats before loading any modifiers
+ InitStatsForLevel();
+ InitTaxiNodesForLevel();
+
+ // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
+
+ //mails are loaded only when needed ;-) - when player in game click on mailbox.
+ //_LoadMail();
+
+ _LoadAuras(holder->GetResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff);
+
+ // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
+ if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) )
+ m_deathState = DEAD;
+
+ _LoadSpells(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS));
+
+ // after spell load
+ InitTalentForLevel();
+ learnSkillRewardedSpells();
+
+ // after spell load, learn rewarded spell if need also
+ _LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
+ _LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS));
+
+ _LoadTutorials(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTUTORIALS));
+
+ // must be before inventory (some items required reputation check)
+ _LoadReputation(holder->GetResult(PLAYER_LOGIN_QUERY_LOADREPUTATION));
+
+ _LoadInventory(holder->GetResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff);
+
+ // update items with duration and realtime
+ UpdateItemDuration(time_diff, true);
+
+ _LoadActions(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACTIONS));
+
+ // unread mails and next delivery time, actual mails not loaded
+ _LoadMailInit(holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILCOUNT), holder->GetResult(PLAYER_LOGIN_QUERY_LOADMAILDATE));
+
+ m_social = sSocialMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSOCIALLIST), GetGUIDLow());
+
+ if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND)))
+ return false;
+
+ // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES
+ // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded
+ if(uint32 curTitle = GetUInt32Value(PLAYER_CHOSEN_TITLE))
+ {
+ if(!HasFlag64(PLAYER__FIELD_KNOWN_TITLES,uint64(1) << curTitle))
+ SetUInt32Value(PLAYER_CHOSEN_TITLE,0);
+ }
+
+ // Not finish taxi flight path
+ if(!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes))
+ {
+ // problems with taxi path loading
+ TaxiNodesEntry const* nodeEntry = NULL;
+ if(uint32 node_id = m_taxi.GetTaxiSource())
+ nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
+
+ if(!nodeEntry) // don't know taxi start node, to homebind
+ {
+ sLog.outError("Character %u have wrong data in taxi destination list, teleport to homebind.",GetGUIDLow());
+ SetMapId(m_homebindMapId);
+ Relocate( m_homebindX, m_homebindY, m_homebindZ,0.0f);
+ SaveRecallPosition(); // save as recall also to prevent recall and fall from sky
+ }
+ else // have start node, to it
+ {
+ sLog.outError("Character %u have too short taxi destination list, teleport to original node.",GetGUIDLow());
+ SetMapId(nodeEntry->map_id);
+ Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z,0.0f);
+ SaveRecallPosition(); // save as recall also to prevent recall and fall from sky
+ }
+ m_taxi.ClearTaxiDestinations();
+ }
+ else if(uint32 node_id = m_taxi.GetTaxiSource())
+ {
+ // save source node as recall coord to prevent recall and fall from sky
+ TaxiNodesEntry const* nodeEntry = sTaxiNodesStore.LookupEntry(node_id);
+ assert(nodeEntry); // checked in m_taxi.LoadTaxiDestinationsFromString
+ m_recallMap = nodeEntry->map_id;
+ m_recallX = nodeEntry->x;
+ m_recallY = nodeEntry->y;
+ m_recallZ = nodeEntry->z;
+
+ // flight will started later
+ }
+
+ _LoadSpellCooldowns(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS));
+
+ // Spell code allow apply any auras to dead character in load time in aura/spell/item loading
+ // Do now before stats re-calculation cleanup for ghost state unexpected auras
+ if(!isAlive())
+ RemoveAllAurasOnDeath();
+
+ //apply all stat bonuses from items and auras
+ SetCanModifyStats(true);
+ UpdateAllStats();
+
+ // restore remembered power/health values (but not more max values)
+ SetHealth(savedHealth > GetMaxHealth() ? GetMaxHealth() : savedHealth);
+ for(uint32 i = 0; i < MAX_POWERS; ++i)
+ SetPower(Powers(i),savedPower[i] > GetMaxPower(Powers(i)) ? GetMaxPower(Powers(i)) : savedPower[i]);
+
+ sLog.outDebug("The value of player %s after load item and aura is: ", m_name.c_str());
+ outDebugValues();
+
+ // GM state
+ if(GetSession()->GetSecurity() > SEC_PLAYER)
+ {
+ switch(sWorld.getConfig(CONFIG_GM_LOGIN_STATE))
+ {
+ default:
+ case 0: break; // disable
+ case 1: SetGameMaster(true); break; // enable
+ case 2: // save state
+ 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;
+ }
+ }
+
+ _LoadDeclinedNames(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES));
+
+ return true;
+}
+
+bool Player::isAllowedToLoot(Creature* creature)
+{
+ if(Player* recipient = creature->GetLootRecipient())
+ {
+ if (recipient == this)
+ return true;
+ if( Group* otherGroup = recipient->GetGroup())
+ {
+ Group* thisGroup = GetGroup();
+ if(!thisGroup)
+ return false;
+ return thisGroup == otherGroup;
+ }
+ return false;
+ }
+ else
+ // prevent other players from looting if the recipient got disconnected
+ return !creature->hasLootRecipient();
+}
+
+void Player::_LoadActions(QueryResult *result)
+{
+ m_actionButtons.clear();
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT button,action,type,misc FROM character_action WHERE guid = '%u' ORDER BY button",GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint8 button = fields[0].GetUInt8();
+
+ addActionButton(button, fields[1].GetUInt16(), fields[2].GetUInt8(), fields[3].GetUInt8());
+
+ m_actionButtons[button].uState = ACTIONBUTTON_UNCHANGED;
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Player::_LoadAuras(QueryResult *result, uint32 timediff)
+{
+ m_Auras.clear();
+ for (int i = 0; i < TOTAL_AURAS; i++)
+ m_modAuras[i].clear();
+
+ // all aura related fields
+ for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i)
+ SetUInt32Value(i, 0);
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'",GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint64 caster_guid = fields[0].GetUInt64();
+ uint32 spellid = fields[1].GetUInt32();
+ uint32 effindex = fields[2].GetUInt32();
+ int32 damage = (int32)fields[3].GetUInt32();
+ int32 maxduration = (int32)fields[4].GetUInt32();
+ int32 remaintime = (int32)fields[5].GetUInt32();
+ int32 remaincharges = (int32)fields[6].GetUInt32();
+
+ SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
+ if(!spellproto)
+ {
+ sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex);
+ continue;
+ }
+
+ if(effindex >= 3)
+ {
+ sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex);
+ continue;
+ }
+
+ // negative effects should continue counting down after logout
+ if (remaintime != -1 && !IsPositiveEffect(spellid, effindex))
+ {
+ if(remaintime <= int32(timediff))
+ continue;
+
+ remaintime -= timediff;
+ }
+
+ // prevent wrong values of remaincharges
+ if(spellproto->procCharges)
+ {
+ if(remaincharges <= 0 || remaincharges > spellproto->procCharges)
+ remaincharges = spellproto->procCharges;
+ }
+ else
+ remaincharges = -1;
+
+ //do not load single target auras (unless they were cast by the player)
+ if (caster_guid != GetGUID() && IsSingleTargetSpell(spellproto))
+ continue;
+
+ Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL);
+ if(!damage)
+ damage = aura->GetModifier()->m_amount;
+ aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges);
+ AddAura(aura);
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+
+ if(m_class == CLASS_WARRIOR)
+ CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true);
+}
+
+void Player::LoadCorpse()
+{
+ if( isAlive() )
+ {
+ ObjectAccessor::Instance().ConvertCorpseForPlayer(GetGUID());
+ }
+ else
+ {
+ if(Corpse *corpse = GetCorpse())
+ {
+ ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, corpse && !sMapStore.LookupEntry(corpse->GetMapId())->Instanceable() );
+ }
+ else
+ {
+ //Prevent Dead Player login without corpse
+ ResurrectPlayer(0.5f);
+ }
+ }
+}
+
+void Player::_LoadInventory(QueryResult *result, uint32 timediff)
+{
+ //QueryResult *result = CharacterDatabase.PQuery("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", GetGUIDLow());
+ std::map<uint64, Bag*> bagMap; // fast guid lookup for bags
+ //NOTE: the "order by `bag`" is important because it makes sure
+ //the bagMap is filled before items in the bags are loaded
+ //NOTE2: the "order by `slot`" is needed becaue mainhand weapons are (wrongly?)
+ //expected to be equipped before offhand items (TODO: fixme)
+
+ uint32 zone = GetZoneId();
+
+ if (result)
+ {
+ std::list<Item*> problematicItems;
+
+ // prevent items from being added to the queue when stored
+ m_itemUpdateQueueBlocked = true;
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 bag_guid = fields[1].GetUInt32();
+ uint8 slot = fields[2].GetUInt8();
+ uint32 item_guid = fields[3].GetUInt32();
+ uint32 item_id = fields[4].GetUInt32();
+
+ ItemPrototype const * proto = objmgr.GetItemPrototype(item_id);
+
+ if(!proto)
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid);
+ sLog.outError( "Player::_LoadInventory: Player %s has an unknown item (id: #%u) in inventory, deleted.", GetName(),item_id );
+ continue;
+ }
+
+ Item *item = NewItemOrBag(proto);
+
+ if(!item->LoadFromDB(item_guid, GetGUID(), result))
+ {
+ sLog.outError( "Player::_LoadInventory: Player %s has broken item (id: #%u) in inventory, deleted.", GetName(),item_id );
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
+ item->FSetState(ITEM_REMOVED);
+ item->SaveToDB(); // it also deletes item object !
+ continue;
+ }
+
+ // not allow have in alive state item limited to another map/zone
+ if(isAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(),zone) )
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
+ item->FSetState(ITEM_REMOVED);
+ item->SaveToDB(); // it also deletes item object !
+ continue;
+ }
+
+ // "Conjured items disappear if you are logged out for more than 15 minutes"
+ if ((timediff > 15*60) && (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED)))
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
+ item->FSetState(ITEM_REMOVED);
+ item->SaveToDB(); // it also deletes item object !
+ continue;
+ }
+
+ bool success = true;
+
+ if (!bag_guid)
+ {
+ // the item is not in a bag
+ item->SetContainer( NULL );
+ item->SetSlot(slot);
+
+ if( IsInventoryPos( INVENTORY_SLOT_BAG_0, slot ) )
+ {
+ ItemPosCountVec dest;
+ if( CanStoreItem( INVENTORY_SLOT_BAG_0, slot, dest, item, false ) == EQUIP_ERR_OK )
+ item = StoreItem(dest, item, true);
+ else
+ success = false;
+ }
+ else if( IsEquipmentPos( INVENTORY_SLOT_BAG_0, slot ) )
+ {
+ uint16 dest;
+ if( CanEquipItem( slot, dest, item, false, false ) == EQUIP_ERR_OK )
+ QuickEquipItem(dest, item);
+ else
+ success = false;
+ }
+ else if( IsBankPos( INVENTORY_SLOT_BAG_0, slot ) )
+ {
+ ItemPosCountVec dest;
+ if( CanBankItem( INVENTORY_SLOT_BAG_0, slot, dest, item, false, false ) == EQUIP_ERR_OK )
+ item = BankItem(dest, item, true);
+ else
+ success = false;
+ }
+
+ if(success)
+ {
+ // store bags that may contain items in them
+ if(item->IsBag() && IsBagPos(item->GetPos()))
+ bagMap[item_guid] = (Bag*)item;
+ }
+ }
+ else
+ {
+ item->SetSlot(NULL_SLOT);
+ // the item is in a bag, find the bag
+ std::map<uint64, Bag*>::iterator itr = bagMap.find(bag_guid);
+ if(itr != bagMap.end())
+ itr->second->StoreItem(slot, item, true );
+ else
+ success = false;
+ }
+
+ // item's state may have changed after stored
+ if (success)
+ item->SetState(ITEM_UNCHANGED, this);
+ else
+ {
+ sLog.outError("Player::_LoadInventory: Player %s has item (GUID: %u Entry: %u) can't be loaded to inventory (Bag GUID: %u Slot: %u) by some reason, will send by mail.", GetName(),item_guid, item_id, bag_guid, slot);
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item_guid);
+ problematicItems.push_back(item);
+ }
+ } while (result->NextRow());
+
+ delete result;
+ m_itemUpdateQueueBlocked = false;
+
+ // send by mail problematic items
+ while(!problematicItems.empty())
+ {
+ // fill mail
+ MailItemsInfo mi; // item list prepering
+
+ for(int i = 0; !problematicItems.empty() && i < MAX_MAIL_ITEMS; ++i)
+ {
+ Item* item = problematicItems.front();
+ problematicItems.pop_front();
+
+ mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
+ }
+
+ std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM);
+
+ WorldSession::SendMailTo(this, MAIL_NORMAL, MAIL_STATIONERY_GM, GetGUIDLow(), GetGUIDLow(), subject, 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+ }
+ }
+ //if(isAlive())
+ _ApplyAllItemMods();
+}
+
+// load mailed item which should receive current player
+void Player::_LoadMailedItems(Mail *mail)
+{
+ QueryResult* result = CharacterDatabase.PQuery("SELECT item_guid, item_template FROM mail_items WHERE mail_id='%u'", mail->messageID);
+ if(!result)
+ return;
+
+ do
+ {
+ Field *fields = result->Fetch();
+ uint32 item_guid_low = fields[0].GetUInt32();
+ uint32 item_template = fields[1].GetUInt32();
+
+ mail->AddItem(item_guid_low, item_template);
+
+ ItemPrototype const *proto = objmgr.GetItemPrototype(item_template);
+
+ if(!proto)
+ {
+ sLog.outError( "Player %u have unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), item_guid_low, item_template,mail->messageID);
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low);
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid_low);
+ continue;
+ }
+
+ Item *item = NewItemOrBag(proto);
+
+ if(!item->LoadFromDB(item_guid_low, 0))
+ {
+ sLog.outError( "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, item_guid_low);
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", item_guid_low);
+ item->FSetState(ITEM_REMOVED);
+ item->SaveToDB(); // it also deletes item object !
+ continue;
+ }
+
+ AddMItem(item);
+ } while (result->NextRow());
+
+ delete result;
+}
+
+void Player::_LoadMailInit(QueryResult *resultUnread, QueryResult *resultDelivery)
+{
+ //set a count of unread mails
+ //QueryResult *resultMails = CharacterDatabase.PQuery("SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" I64FMTD "'", GUID_LOPART(playerGuid),(uint64)cTime);
+ if (resultUnread)
+ {
+ Field *fieldMail = resultUnread->Fetch();
+ unReadMails = fieldMail[0].GetUInt8();
+ delete resultUnread;
+ }
+
+ // store nearest delivery time (it > 0 and if it < current then at next player update SendNewMaill will be called)
+ //resultMails = CharacterDatabase.PQuery("SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(playerGuid));
+ if (resultDelivery)
+ {
+ Field *fieldMail = resultDelivery->Fetch();
+ m_nextMailDelivereTime = (time_t)fieldMail[0].GetUInt64();
+ delete resultDelivery;
+ }
+}
+
+void Player::_LoadMail()
+{
+ m_mail.clear();
+ //mails are in right order 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ QueryResult *result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,subject,itemTextId,has_items,expire_time,deliver_time,money,cod,checked,stationery,mailTemplateId FROM mail WHERE receiver = '%u' ORDER BY id DESC",GetGUIDLow());
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ Mail *m = new Mail;
+ m->messageID = fields[0].GetUInt32();
+ m->messageType = fields[1].GetUInt8();
+ m->sender = fields[2].GetUInt32();
+ m->receiver = fields[3].GetUInt32();
+ m->subject = fields[4].GetCppString();
+ m->itemTextId = fields[5].GetUInt32();
+ bool has_items = fields[6].GetBool();
+ m->expire_time = (time_t)fields[7].GetUInt64();
+ m->deliver_time = (time_t)fields[8].GetUInt64();
+ m->money = fields[9].GetUInt32();
+ m->COD = fields[10].GetUInt32();
+ m->checked = fields[11].GetUInt32();
+ m->stationery = fields[12].GetUInt8();
+ m->mailTemplateId = fields[13].GetInt16();
+
+ if(m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId))
+ {
+ sLog.outError( "Player::_LoadMail - Mail (%u) have not existed MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId);
+ m->mailTemplateId = 0;
+ }
+
+ m->state = MAIL_STATE_UNCHANGED;
+
+ if (has_items)
+ _LoadMailedItems(m);
+
+ m_mail.push_back(m);
+ } while( result->NextRow() );
+ delete result;
+ }
+ m_mailsLoaded = true;
+}
+
+void Player::LoadPet()
+{
+ //fixme: the pet should still be loaded if the player is not in world
+ // just not added to the map
+ if(IsInWorld())
+ {
+ Pet *pet = new Pet;
+ if(!pet->LoadPetFromDB(this,0,0,true))
+ delete pet;
+ }
+}
+
+void Player::_LoadQuestStatus(QueryResult *result)
+{
+ mQuestStatus.clear();
+
+ uint32 slot = 0;
+
+ //// 0 1 2 3 4 5 6 7 8 9 10 11 12
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT quest, status, rewarded, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3, itemcount4 FROM character_queststatus WHERE guid = '%u'", GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 quest_id = fields[0].GetUInt32();
+ // used to be new, no delete?
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+ if( pQuest )
+ {
+ // find or create
+ QuestStatusData& questStatusData = mQuestStatus[quest_id];
+
+ uint32 qstatus = fields[1].GetUInt32();
+ if(qstatus < MAX_QUEST_STATUS)
+ questStatusData.m_status = QuestStatus(qstatus);
+ else
+ {
+ questStatusData.m_status = QUEST_STATUS_NONE;
+ sLog.outError("Player %s have invalid quest %d status (%d), replaced by QUEST_STATUS_NONE(0).",GetName(),quest_id,qstatus);
+ }
+
+ questStatusData.m_rewarded = ( fields[2].GetUInt8() > 0 );
+ questStatusData.m_explored = ( fields[3].GetUInt8() > 0 );
+
+ time_t quest_time = time_t(fields[4].GetUInt64());
+
+ if( pQuest->HasFlag( QUEST_MANGOS_FLAGS_TIMED ) && !GetQuestRewardStatus(quest_id) && questStatusData.m_status != QUEST_STATUS_NONE )
+ {
+ AddTimedQuest( quest_id );
+
+ if (quest_time <= sWorld.GetGameTime())
+ questStatusData.m_timer = 1;
+ else
+ questStatusData.m_timer = (quest_time - sWorld.GetGameTime()) * 1000;
+ }
+ else
+ quest_time = 0;
+
+ questStatusData.m_creatureOrGOcount[0] = fields[5].GetUInt32();
+ questStatusData.m_creatureOrGOcount[1] = fields[6].GetUInt32();
+ questStatusData.m_creatureOrGOcount[2] = fields[7].GetUInt32();
+ questStatusData.m_creatureOrGOcount[3] = fields[8].GetUInt32();
+ questStatusData.m_itemcount[0] = fields[9].GetUInt32();
+ questStatusData.m_itemcount[1] = fields[10].GetUInt32();
+ questStatusData.m_itemcount[2] = fields[11].GetUInt32();
+ questStatusData.m_itemcount[3] = fields[12].GetUInt32();
+
+ questStatusData.uState = QUEST_UNCHANGED;
+
+ // add to quest log
+ if( slot < MAX_QUEST_LOG_SIZE &&
+ ( questStatusData.m_status==QUEST_STATUS_INCOMPLETE ||
+ questStatusData.m_status==QUEST_STATUS_COMPLETE && !questStatusData.m_rewarded ) )
+ {
+ SetQuestSlot(slot,quest_id,quest_time);
+
+ if(questStatusData.m_status == QUEST_STATUS_COMPLETE)
+ SetQuestSlotState(slot,QUEST_STATE_COMPLETE);
+
+ for(uint8 idx = 0; idx < QUEST_OBJECTIVES_COUNT; ++idx)
+ if(questStatusData.m_creatureOrGOcount[idx])
+ SetQuestSlotCounter(slot,idx,questStatusData.m_creatureOrGOcount[idx]);
+
+ ++slot;
+ }
+
+ if(questStatusData.m_rewarded)
+ {
+ // learn rewarded spell if unknown
+ learnQuestRewardedSpells(pQuest);
+
+ // set rewarded title if any
+ if(pQuest->GetCharTitleId())
+ {
+ if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
+ SetFlag64(PLAYER__FIELD_KNOWN_TITLES, (uint64(1) << titleEntry->bit_index));
+ }
+ }
+
+ sLog.outDebug("Quest status is {%u} for quest {%u} for player (GUID: %u)", questStatusData.m_status, quest_id, GetGUIDLow());
+ }
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+
+ // clear quest log tail
+ for ( uint16 i = slot; i < MAX_QUEST_LOG_SIZE; ++i )
+ SetQuestSlot(i,0);
+}
+
+void Player::_LoadDailyQuestStatus(QueryResult *result)
+{
+ for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,0);
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GetGUIDLow());
+
+ if(result)
+ {
+ uint32 quest_daily_idx = 0;
+
+ do
+ {
+ if(quest_daily_idx >= PLAYER_MAX_DAILY_QUESTS) // max amount with exist data in query
+ {
+ sLog.outError("Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`",GetGUIDLow());
+ break;
+ }
+
+ Field *fields = result->Fetch();
+
+ uint32 quest_id = fields[0].GetUInt32();
+
+ // save _any_ from daily quest times (it must be after last reset anyway)
+ m_lastDailyQuestTime = (time_t)fields[1].GetUInt64();
+
+ Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+ if( !pQuest )
+ continue;
+
+ SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,quest_id);
+ ++quest_daily_idx;
+
+ sLog.outDebug("Daily quest {%u} cooldown for player (GUID: %u)", quest_id, GetGUIDLow());
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+
+ m_DailyQuestChanged = false;
+}
+
+void Player::_LoadReputation(QueryResult *result)
+{
+ m_factions.clear();
+
+ // Set initial reputations (so everything is nifty before DB data load)
+ SetInitialFactions();
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'",GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ FactionEntry const *factionEntry = sFactionStore.LookupEntry(fields[0].GetUInt32());
+ if( factionEntry && (factionEntry->reputationListID >= 0))
+ {
+ FactionState* faction = &m_factions[factionEntry->reputationListID];
+
+ // update standing to current
+ faction->Standing = int32(fields[1].GetUInt32());
+
+ uint32 dbFactionFlags = fields[2].GetUInt32();
+
+ if( dbFactionFlags & FACTION_FLAG_VISIBLE )
+ SetFactionVisible(faction); // have internal checks for forced invisibility
+
+ if( dbFactionFlags & FACTION_FLAG_INACTIVE)
+ SetFactionInactive(faction,true); // have internal checks for visibility requirement
+
+ if( dbFactionFlags & FACTION_FLAG_AT_WAR ) // DB at war
+ SetFactionAtWar(faction,true); // have internal checks for FACTION_FLAG_PEACE_FORCED
+ else // DB not at war
+ {
+ // allow remove if visible (and then not FACTION_FLAG_INVISIBLE_FORCED or FACTION_FLAG_HIDDEN)
+ if( faction->Flags & FACTION_FLAG_VISIBLE )
+ SetFactionAtWar(faction,false); // have internal checks for FACTION_FLAG_PEACE_FORCED
+ }
+
+ // set atWar for hostile
+ if(GetReputationRank(factionEntry) <= REP_HOSTILE)
+ SetFactionAtWar(faction,true);
+
+ // reset changed flag if values similar to saved in DB
+ if(faction->Flags==dbFactionFlags)
+ faction->Changed = false;
+ }
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Player::_LoadSpells(QueryResult *result)
+{
+ for (PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ delete itr->second;
+ m_spells.clear();
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM character_spell WHERE guid = '%u'",GetGUIDLow());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ addSpell(fields[0].GetUInt16(), fields[2].GetBool(), false, true, fields[1].GetUInt16(), fields[3].GetBool());
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Player::_LoadTutorials(QueryResult *result)
+{
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmid);
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ for (int iI=0; iI<8; iI++)
+ m_Tutorials[iI] = fields[iI].GetUInt32();
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+
+ m_TutorialsChanged = false;
+}
+
+void Player::_LoadGroup(QueryResult *result)
+{
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", GetGUIDLow());
+ if(result)
+ {
+ uint64 leaderGuid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ delete result;
+ Group* group = objmgr.GetGroupByLeader(leaderGuid);
+ if(group)
+ {
+ uint8 subgroup = group->GetMemberGroup(GetGUID());
+ SetGroup(group, subgroup);
+ if(getLevel() >= LEVELREQUIREMENT_HEROIC)
+ {
+ // the group leader may change the instance difficulty while the player is offline
+ SetDifficulty(group->GetDifficulty());
+ }
+ }
+ }
+}
+
+void Player::_LoadBoundInstances(QueryResult *result)
+{
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ m_boundInstances[i].clear();
+
+ Group *group = GetGroup();
+
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid));
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ bool perm = fields[1].GetBool();
+ uint32 mapId = fields[2].GetUInt32();
+ uint32 instanceId = fields[0].GetUInt32();
+ uint8 difficulty = fields[3].GetUInt8();
+ time_t resetTime = (time_t)fields[4].GetUInt64();
+ // the resettime for normal instances is only saved when the InstanceSave is unloaded
+ // so the value read from the DB may be wrong here but only if the InstanceSave is loaded
+ // and in that case it is not used
+
+ if(!perm && group)
+ {
+ sLog.outError("_LoadBoundInstances: player %s(%d) is in group %d but has a non-permanent character bind to map %d,%d,%d", GetName(), GetGUIDLow(), GUID_LOPART(group->GetLeaderGUID()), mapId, instanceId, difficulty);
+ CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'", GetGUIDLow(), instanceId);
+ continue;
+ }
+
+ // since non permanent binds are always solo bind, they can always be reset
+ InstanceSave *save = sInstanceSaveManager.AddInstanceSave(mapId, instanceId, difficulty, resetTime, !perm, true);
+ if(save) BindToInstance(save, perm, true);
+ } while(result->NextRow());
+ delete result;
+ }
+}
+
+InstancePlayerBind* Player::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;
+}
+
+void Player::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload)
+{
+ BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
+ UnbindInstance(itr, difficulty, unload);
+}
+
+void Player::UnbindInstance(BoundInstancesMap::iterator &itr, uint8 difficulty, bool unload)
+{
+ if(itr != m_boundInstances[difficulty].end())
+ {
+ if(!unload) CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), itr->second.save->GetInstanceId());
+ itr->second.save->RemovePlayer(this); // save can become invalid
+ m_boundInstances[difficulty].erase(itr++);
+ }
+}
+
+InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent, bool load)
+{
+ if(save)
+ {
+ InstancePlayerBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
+ if(bind.save)
+ {
+ // update the save when the group kills a boss
+ if(permanent != bind.perm || save != bind.save)
+ if(!load) CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GetGUIDLow(), bind.save->GetInstanceId());
+ }
+ else
+ if(!load) CharacterDatabase.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')", GetGUIDLow(), save->GetInstanceId(), permanent);
+
+ if(bind.save != save)
+ {
+ if(bind.save) bind.save->RemovePlayer(this);
+ save->AddPlayer(this);
+ }
+
+ if(permanent) save->SetCanReset(false);
+
+ bind.save = save;
+ bind.perm = permanent;
+ if(!load) sLog.outDebug("Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", GetName(), GetGUIDLow(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
+ return &bind;
+ }
+ else
+ return NULL;
+}
+
+void Player::SendRaidInfo()
+{
+ WorldPacket data(SMSG_RAID_INSTANCE_INFO, 4);
+
+ uint32 counter = 0, i;
+ for(i = 0; i < TOTAL_DIFFICULTIES; i++)
+ for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); itr++)
+ if(itr->second.perm) counter++;
+
+ data << counter;
+ for(i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); itr++)
+ {
+ if(itr->second.perm)
+ {
+ InstanceSave *save = itr->second.save;
+ data << (save->GetMapId());
+ data << (uint32)(save->GetResetTime() - time(NULL));
+ data << save->GetInstanceId();
+ data << uint32(counter);
+ counter--;
+ }
+ }
+ }
+ GetSession()->SendPacket(&data);
+}
+
+/*
+- called on every successful teleportation to a map
+*/
+void Player::SendSavedInstances()
+{
+ bool hasBeenSaved = false;
+ WorldPacket data;
+
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
+ {
+ if(itr->second.perm) // only permanent binds are sent
+ {
+ hasBeenSaved = true;
+ break;
+ }
+ }
+ }
+
+ //Send opcode 811. true or flase means, whether you have current raid/heroic instances
+ data.Initialize(SMSG_UPDATE_INSTANCE_OWNERSHIP);
+ data << uint32(hasBeenSaved);
+ GetSession()->SendPacket(&data);
+
+ if(!hasBeenSaved)
+ return;
+
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
+ {
+ if(itr->second.perm)
+ {
+ data.Initialize(SMSG_UPDATE_LAST_INSTANCE);
+ data << uint32(itr->second.save->GetMapId());
+ GetSession()->SendPacket(&data);
+ }
+ }
+ }
+}
+
+/// convert the player's binds to the group
+void Player::ConvertInstancesToGroup(Player *player, Group *group, uint64 player_guid)
+{
+ bool has_binds = false;
+ bool has_solo = false;
+
+ if(player) { player_guid = player->GetGUID(); if(!group) group = player->GetGroup(); }
+ assert(player_guid);
+
+ // copy all binds to the group, when changing leader it's assumed the character
+ // will not have any solo binds
+
+ if(player)
+ {
+ for(uint8 i = 0; i < TOTAL_DIFFICULTIES; i++)
+ {
+ for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();)
+ {
+ has_binds = true;
+ if(group) group->BindToInstance(itr->second.save, itr->second.perm, true);
+ // permanent binds are not removed
+ if(!itr->second.perm)
+ {
+ player->UnbindInstance(itr, i, true); // increments itr
+ has_solo = true;
+ }
+ else
+ ++itr;
+ }
+ }
+ }
+
+ // if the player's not online we don't know what binds it has
+ if(!player || !group || has_binds) CharacterDatabase.PExecute("INSERT INTO group_instance SELECT guid, instance, permanent FROM character_instance WHERE guid = '%u'", GUID_LOPART(player_guid));
+ // the following should not get executed when changing leaders
+ if(!player || has_solo) CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND permanent = 0", GUID_LOPART(player_guid));
+}
+
+bool Player::_LoadHomeBind(QueryResult *result)
+{
+ bool ok = false;
+ //QueryResult *result = CharacterDatabase.PQuery("SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(playerGuid));
+ if (result)
+ {
+ Field *fields = result->Fetch();
+ m_homebindMapId = fields[0].GetUInt32();
+ m_homebindZoneId = fields[1].GetUInt16();
+ m_homebindX = fields[2].GetFloat();
+ m_homebindY = fields[3].GetFloat();
+ m_homebindZ = fields[4].GetFloat();
+ delete result;
+
+ // accept saved data only for valid position (and non instanceable)
+ if( MapManager::IsValidMapCoord(m_homebindMapId,m_homebindX,m_homebindY,m_homebindZ) &&
+ !sMapStore.LookupEntry(m_homebindMapId)->Instanceable() )
+ {
+ ok = true;
+ }
+ else
+ CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'", GetGUIDLow());
+ }
+
+ if(!ok)
+ {
+ PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass());
+ if(!info) return false;
+
+ m_homebindMapId = info->mapId;
+ m_homebindZoneId = info->zoneId;
+ m_homebindX = info->positionX;
+ m_homebindY = info->positionY;
+ m_homebindZ = info->positionZ;
+
+ CharacterDatabase.PExecute("INSERT INTO character_homebind (guid,map,zone,position_x,position_y,position_z) VALUES ('%u', '%u', '%u', '%f', '%f', '%f')", GetGUIDLow(), m_homebindMapId, (uint32)m_homebindZoneId, m_homebindX, m_homebindY, m_homebindZ);
+ }
+
+ DEBUG_LOG("Setting player home position: mapid is: %u, zoneid is %u, X is %f, Y is %f, Z is %f\n",
+ m_homebindMapId, m_homebindZoneId, m_homebindX, m_homebindY, m_homebindZ);
+
+ return true;
+}
+
+/*********************************************************/
+/*** SAVE SYSTEM ***/
+/*********************************************************/
+
+void Player::SaveToDB()
+{
+ // delay auto save at any saves (manual, in code, or autosave)
+ m_nextSave = sWorld.getConfig(CONFIG_INTERVAL_SAVE);
+
+ // first save/honor gain after midnight will also update the player's honor fields
+ UpdateHonorFields();
+
+ // players aren't saved on battleground maps
+ uint32 mapid = IsBeingTeleported() ? GetTeleportDest().mapid : GetMapId();
+ const MapEntry * me = sMapStore.LookupEntry(mapid);
+ if(!me || me->IsBattleGroundOrArena())
+ return;
+
+ int is_save_resting = HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0;
+ //save, far from tavern/city
+ //save, but in tavern/city
+ sLog.outDebug("The value of player %s at save: ", m_name.c_str());
+ outDebugValues();
+
+ // save state (after auras removing), if aura remove some flags then it must set it back by self)
+ uint32 tmp_bytes = GetUInt32Value(UNIT_FIELD_BYTES_1);
+ uint32 tmp_bytes2 = GetUInt32Value(UNIT_FIELD_BYTES_2);
+ uint32 tmp_flags = GetUInt32Value(UNIT_FIELD_FLAGS);
+ uint32 tmp_pflags = GetUInt32Value(PLAYER_FLAGS);
+ uint32 tmp_displayid = GetDisplayId();
+
+ // Set player sit state to standing on save, also stealth and shifted form
+ SetByteValue(UNIT_FIELD_BYTES_1, 0, 0); // stand state
+ SetByteValue(UNIT_FIELD_BYTES_2, 3, 0); // shapeshift
+ SetByteValue(UNIT_FIELD_BYTES_1, 3, 0); // stand flags?
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ SetDisplayId(GetNativeDisplayId());
+
+ bool inworld = IsInWorld();
+
+ CharacterDatabase.BeginTransaction();
+
+ CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",GetGUIDLow());
+
+ std::string sql_name = m_name;
+ CharacterDatabase.escape_string(sql_name);
+
+ std::ostringstream ss;
+ ss << "INSERT INTO characters (guid,account,name,race,class,"
+ "map, dungeon_difficulty, position_x, position_y, position_z, orientation, data, "
+ "taximask, online, 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, "
+ "death_expire_time, taxi_path) VALUES ("
+ << GetGUIDLow() << ", "
+ << GetSession()->GetAccountId() << ", '"
+ << sql_name << "', "
+ << m_race << ", "
+ << m_class << ", ";
+
+ bool save_to_dest = false;
+ if(IsBeingTeleported())
+ {
+ // don't save to battlegrounds or arenas
+ const MapEntry *entry = sMapStore.LookupEntry(GetTeleportDest().mapid);
+ if(entry && entry->map_type != MAP_BATTLEGROUND && entry->map_type != MAP_ARENA)
+ save_to_dest = true;
+ }
+
+ if(!save_to_dest)
+ {
+ ss << GetMapId() << ", "
+ << (uint32)GetDifficulty() << ", "
+ << finiteAlways(GetPositionX()) << ", "
+ << finiteAlways(GetPositionY()) << ", "
+ << finiteAlways(GetPositionZ()) << ", "
+ << finiteAlways(GetOrientation()) << ", '";
+ }
+ else
+ {
+ ss << GetTeleportDest().mapid << ", "
+ << (uint32)GetDifficulty() << ", "
+ << finiteAlways(GetTeleportDest().x) << ", "
+ << finiteAlways(GetTeleportDest().y) << ", "
+ << finiteAlways(GetTeleportDest().z) << ", "
+ << finiteAlways(GetTeleportDest().o) << ", '";
+ }
+
+ uint16 i;
+ for( i = 0; i < m_valuesCount; i++ )
+ {
+ ss << GetUInt32Value(i) << " ";
+ }
+
+ ss << "', '";
+
+ for( i = 0; i < 8; i++ )
+ ss << m_taxi.GetTaximask(i) << " ";
+
+ ss << "', ";
+ ss << (inworld ? 1 : 0);
+
+ ss << ", ";
+ ss << m_cinematic;
+
+ ss << ", ";
+ ss << m_Played_time[0];
+ ss << ", ";
+ ss << m_Played_time[1];
+
+ ss << ", ";
+ ss << finiteAlways(m_rest_bonus);
+ ss << ", ";
+ ss << (uint64)time(NULL);
+ ss << ", ";
+ ss << is_save_resting;
+ ss << ", ";
+ ss << m_resetTalentsCost;
+ ss << ", ";
+ ss << (uint64)m_resetTalentsTime;
+
+ ss << ", ";
+ ss << finiteAlways(m_movementInfo.t_x);
+ ss << ", ";
+ ss << finiteAlways(m_movementInfo.t_y);
+ ss << ", ";
+ ss << finiteAlways(m_movementInfo.t_z);
+ ss << ", ";
+ ss << finiteAlways(m_movementInfo.t_o);
+ ss << ", ";
+ if (m_transport)
+ ss << m_transport->GetGUIDLow();
+ else
+ ss << "0";
+
+ ss << ", ";
+ ss << m_ExtraFlags;
+
+ ss << ", ";
+ ss << uint32(m_stableSlots); // to prevent save uint8 as char
+
+ ss << ", ";
+ ss << uint32(m_atLoginFlags);
+
+ ss << ", ";
+ ss << GetZoneId();
+
+ ss << ", ";
+ ss << (uint64)m_deathExpireTime;
+
+ ss << ", '";
+ ss << m_taxi.SaveTaxiDestinationsToString();
+ ss << "' )";
+
+ CharacterDatabase.Execute( ss.str().c_str() );
+
+ if(m_mailsUpdated) //save mails only when needed
+ _SaveMail();
+
+ _SaveInventory();
+ _SaveQuestStatus();
+ _SaveDailyQuestStatus();
+ _SaveTutorials();
+ _SaveSpells();
+ _SaveSpellCooldowns();
+ _SaveActions();
+ _SaveAuras();
+ _SaveReputation();
+
+ CharacterDatabase.CommitTransaction();
+
+ // restore state (before aura apply, if aura remove flag then aura must set it ack by self)
+ SetDisplayId(tmp_displayid);
+ SetUInt32Value(UNIT_FIELD_BYTES_1, tmp_bytes);
+ SetUInt32Value(UNIT_FIELD_BYTES_2, tmp_bytes2);
+ SetUInt32Value(UNIT_FIELD_FLAGS, tmp_flags);
+ SetUInt32Value(PLAYER_FLAGS, tmp_pflags);
+
+ // save pet (hunter pet level and experience and all type pets health/mana).
+ if(Pet* pet = GetPet())
+ pet->SavePetToDB(PET_SAVE_AS_CURRENT);
+}
+
+// fast save function for item/money cheating preventing - save only inventory and money state
+void Player::SaveInventoryAndGoldToDB()
+{
+ _SaveInventory();
+ SetUInt32ValueInDB(PLAYER_FIELD_COINAGE,GetMoney(),GetGUID());
+}
+
+void Player::_SaveActions()
+{
+ for(ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end(); )
+ {
+ switch (itr->second.uState)
+ {
+ case ACTIONBUTTON_NEW:
+ CharacterDatabase.PExecute("INSERT INTO character_action (guid,button,action,type,misc) VALUES ('%u', '%u', '%u', '%u', '%u')",
+ GetGUIDLow(), (uint32)itr->first, (uint32)itr->second.action, (uint32)itr->second.type, (uint32)itr->second.misc );
+ itr->second.uState = ACTIONBUTTON_UNCHANGED;
+ ++itr;
+ break;
+ case ACTIONBUTTON_CHANGED:
+ CharacterDatabase.PExecute("UPDATE character_action SET action = '%u', type = '%u', misc= '%u' WHERE guid= '%u' AND button= '%u' ",
+ (uint32)itr->second.action, (uint32)itr->second.type, (uint32)itr->second.misc, GetGUIDLow(), (uint32)itr->first );
+ itr->second.uState = ACTIONBUTTON_UNCHANGED;
+ ++itr;
+ break;
+ case ACTIONBUTTON_DELETED:
+ CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u' and button = '%u'", GetGUIDLow(), (uint32)itr->first );
+ m_actionButtons.erase(itr++);
+ break;
+ default:
+ ++itr;
+ break;
+ };
+ }
+}
+
+void Player::_SaveAuras()
+{
+ CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",GetGUIDLow());
+
+ AuraMap const& auras = GetAuras();
+ for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ SpellEntry const *spellInfo = itr->second->GetSpellProto();
+
+ //skip all auras from spells that are passive or need a shapeshift
+ if (itr->second->IsPassive() || itr->second->IsRemovedOnShapeLost())
+ continue;
+
+ //do not save single target auras (unless they were cast by the player)
+ if (itr->second->GetCasterGUID() != GetGUID() && IsSingleTargetSpell(spellInfo))
+ continue;
+
+ uint8 i;
+ // or apply at cast SPELL_AURA_MOD_SHAPESHIFT or SPELL_AURA_MOD_STEALTH auras
+ for (i = 0; i < 3; i++)
+ if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT ||
+ spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH)
+ break;
+
+ if (i == 3)
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u' and spell = '%u' and effect_index= '%u'",GetGUIDLow(),(uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex());
+ CharacterDatabase.PExecute("INSERT INTO character_aura (guid,caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges) "
+ "VALUES ('%u', '" I64FMTD "' ,'%u', '%u', '%d', '%d', '%d', '%d')",
+ GetGUIDLow(), itr->second->GetCasterGUID(), (uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex(), (*itr).second->GetModifier()->m_amount,int((*itr).second->GetAuraMaxDuration()),int((*itr).second->GetAuraDuration()),int((*itr).second->m_procCharges));
+ }
+ }
+}
+
+void Player::_SaveInventory()
+{
+ // force items in buyback slots to new state
+ // and remove those that aren't already
+ for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; i++)
+ {
+ Item *item = m_items[i];
+ if (!item || item->GetState() == ITEM_NEW) continue;
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item->GetGUIDLow());
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item->GetGUIDLow());
+ m_items[i]->FSetState(ITEM_NEW);
+ }
+
+ // update enchantment durations
+ for(EnchantDurationList::iterator itr = m_enchantDuration.begin();itr != m_enchantDuration.end();++itr)
+ {
+ itr->item->SetEnchantmentDuration(itr->slot,itr->leftduration);
+ }
+
+ // if no changes
+ if (m_itemUpdateQueue.empty()) return;
+
+ // do not save if the update queue is corrupt
+ bool error = false;
+ for(size_t i = 0; i < m_itemUpdateQueue.size(); i++)
+ {
+ Item *item = m_itemUpdateQueue[i];
+ if(!item || item->GetState() == ITEM_REMOVED) continue;
+ Item *test = GetItemByPos( item->GetBagSlot(), item->GetSlot());
+
+ if (test == NULL)
+ {
+ sLog.outError("Player(GUID: %u Name: %s)::_SaveInventory - the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the player doesn't have an item at that position!", GetGUIDLow(), GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow());
+ error = true;
+ }
+ else if (test != item)
+ {
+ sLog.outError("Player(GUID: %u Name: %s)::_SaveInventory - the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the item with guid %d is there instead!", GetGUIDLow(), GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), test->GetGUIDLow());
+ error = true;
+ }
+ }
+
+ if (error)
+ {
+ sLog.outError("Player::_SaveInventory - one or more errors occurred save aborted!");
+ ChatHandler(this).SendSysMessage(LANG_ITEM_SAVE_FAILED);
+ return;
+ }
+
+ for(size_t i = 0; i < m_itemUpdateQueue.size(); i++)
+ {
+ Item *item = m_itemUpdateQueue[i];
+ if(!item) continue;
+
+ Bag *container = item->GetContainer();
+ uint32 bag_guid = container ? container->GetGUIDLow() : 0;
+
+ switch(item->GetState())
+ {
+ case ITEM_NEW:
+ CharacterDatabase.PExecute("INSERT INTO character_inventory (guid,bag,slot,item,item_template) VALUES ('%u', '%u', '%u', '%u', '%u')", GetGUIDLow(), bag_guid, item->GetSlot(), item->GetGUIDLow(), item->GetEntry());
+ break;
+ case ITEM_CHANGED:
+ CharacterDatabase.PExecute("UPDATE character_inventory SET guid='%u', bag='%u', slot='%u', item_template='%u' WHERE item='%u'", GetGUIDLow(), bag_guid, item->GetSlot(), item->GetEntry(), item->GetGUIDLow());
+ break;
+ case ITEM_REMOVED:
+ CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'", item->GetGUIDLow());
+ break;
+ case ITEM_UNCHANGED:
+ break;
+ }
+
+ item->SaveToDB(); // item have unchanged inventory record and can be save standalone
+ }
+ m_itemUpdateQueue.clear();
+}
+
+void Player::_SaveMail()
+{
+ if (!m_mailsLoaded)
+ return;
+
+ for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); itr++)
+ {
+ Mail *m = (*itr);
+ if (m->state == MAIL_STATE_CHANGED)
+ {
+ CharacterDatabase.PExecute("UPDATE mail SET itemTextId = '%u',has_items = '%u',expire_time = '" I64FMTD "', deliver_time = '" I64FMTD "',money = '%u',cod = '%u',checked = '%u' WHERE id = '%u'",
+ m->itemTextId, m->HasItems() ? 1 : 0, (uint64)m->expire_time, (uint64)m->deliver_time, m->money, m->COD, m->checked, m->messageID);
+ if(m->removedItems.size())
+ {
+ for(std::vector<uint32>::iterator itr2 = m->removedItems.begin(); itr2 != m->removedItems.end(); ++itr2)
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid = '%u'", *itr2);
+ m->removedItems.clear();
+ }
+ m->state = MAIL_STATE_UNCHANGED;
+ }
+ else if (m->state == MAIL_STATE_DELETED)
+ {
+ if (m->HasItems())
+ for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
+ CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
+ if (m->itemTextId)
+ CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", m->itemTextId);
+ CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
+ CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", m->messageID);
+ }
+ }
+
+ //deallocate deleted mails...
+ for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); )
+ {
+ if ((*itr)->state == MAIL_STATE_DELETED)
+ {
+ Mail* m = *itr;
+ m_mail.erase(itr);
+ delete m;
+ itr = m_mail.begin();
+ }
+ else
+ ++itr;
+ }
+
+ m_mailsUpdated = false;
+}
+
+void Player::_SaveQuestStatus()
+{
+ // we don't need transactions here.
+ for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
+ {
+ switch (i->second.uState)
+ {
+ case QUEST_NEW :
+ CharacterDatabase.PExecute("INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4) "
+ "VALUES ('%u', '%u', '%u', '%u', '%u', '" I64FMTD "', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
+ GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]);
+ break;
+ case QUEST_CHANGED :
+ CharacterDatabase.PExecute("UPDATE character_queststatus SET status = '%u',rewarded = '%u',explored = '%u',timer = '" I64FMTD "',mobcount1 = '%u',mobcount2 = '%u',mobcount3 = '%u',mobcount4 = '%u',itemcount1 = '%u',itemcount2 = '%u',itemcount3 = '%u',itemcount4 = '%u' WHERE guid = '%u' AND quest = '%u' ",
+ i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first );
+ break;
+ case QUEST_UNCHANGED:
+ break;
+ };
+ i->second.uState = QUEST_UNCHANGED;
+ }
+}
+
+void Player::_SaveDailyQuestStatus()
+{
+ if(!m_DailyQuestChanged)
+ return;
+
+ m_DailyQuestChanged = false;
+
+ // save last daily quest time for all quests: we need only mostly reset time for reset check anyway
+
+ // we don't need transactions here.
+ CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'",GetGUIDLow());
+ for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ if(GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
+ CharacterDatabase.PExecute("INSERT INTO character_queststatus_daily (guid,quest,time) VALUES ('%u', '%u','" I64FMTD "')",
+ GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx),uint64(m_lastDailyQuestTime));
+}
+
+void Player::_SaveReputation()
+{
+ for(FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
+ {
+ if (itr->second.Changed)
+ {
+ CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u' AND faction='%u'", GetGUIDLow(), itr->second.ID);
+ CharacterDatabase.PExecute("INSERT INTO character_reputation (guid,faction,standing,flags) VALUES ('%u', '%u', '%i', '%u')", GetGUIDLow(), itr->second.ID, itr->second.Standing, itr->second.Flags);
+ itr->second.Changed = false;
+ }
+ }
+}
+
+void Player::_SaveSpells()
+{
+ for (PlayerSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
+ {
+ ++next;
+ if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
+ CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u' and spell = '%u'", GetGUIDLow(), itr->first);
+ if (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED)
+ CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,slot,active,disabled) VALUES ('%u', '%u', '%u','%u','%u')", GetGUIDLow(), itr->first, itr->second->slotId,itr->second->active ? 1 : 0,itr->second->disabled ? 1 : 0);
+
+ if (itr->second->state == PLAYERSPELL_REMOVED)
+ _removeSpell(itr->first);
+ else
+ itr->second->state = PLAYERSPELL_UNCHANGED;
+ }
+}
+
+void Player::_SaveTutorials()
+{
+ if(!m_TutorialsChanged)
+ return;
+
+ uint32 Rows=0;
+ // it's better than rebuilding indexes multiple times
+ QueryResult *result = CharacterDatabase.PQuery("SELECT count(*) AS r FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetSession()->GetAccountId(), realmID );
+ if(result)
+ {
+ Rows = result->Fetch()[0].GetUInt32();
+ delete result;
+ }
+
+ if (Rows)
+ {
+ CharacterDatabase.PExecute("UPDATE character_tutorial SET tut0='%u', tut1='%u', tut2='%u', tut3='%u', tut4='%u', tut5='%u', tut6='%u', tut7='%u' WHERE account = '%u' AND realmid = '%u'",
+ m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7], GetSession()->GetAccountId(), realmID );
+ }
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO character_tutorial (account,realmid,tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7) VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", GetSession()->GetAccountId(), realmID, m_Tutorials[0], m_Tutorials[1], m_Tutorials[2], m_Tutorials[3], m_Tutorials[4], m_Tutorials[5], m_Tutorials[6], m_Tutorials[7]);
+ };
+
+ m_TutorialsChanged = false;
+}
+
+void Player::outDebugValues() const
+{
+ if(!sLog.IsOutDebug()) // optimize disabled debug output
+ return;
+
+ sLog.outDebug("HP is: \t\t\t%u\t\tMP is: \t\t\t%u",GetMaxHealth(), GetMaxPower(POWER_MANA));
+ sLog.outDebug("AGILITY is: \t\t%f\t\tSTRENGTH is: \t\t%f",GetStat(STAT_AGILITY), GetStat(STAT_STRENGTH));
+ sLog.outDebug("INTELLECT is: \t\t%f\t\tSPIRIT is: \t\t%f",GetStat(STAT_INTELLECT), GetStat(STAT_SPIRIT));
+ sLog.outDebug("STAMINA is: \t\t%f\t\tSPIRIT is: \t\t%f",GetStat(STAT_STAMINA), GetStat(STAT_SPIRIT));
+ sLog.outDebug("Armor is: \t\t%u\t\tBlock is: \t\t%f",GetArmor(), GetFloatValue(PLAYER_BLOCK_PERCENTAGE));
+ sLog.outDebug("HolyRes is: \t\t%u\t\tFireRes is: \t\t%u",GetResistance(SPELL_SCHOOL_HOLY), GetResistance(SPELL_SCHOOL_FIRE));
+ sLog.outDebug("NatureRes is: \t\t%u\t\tFrostRes is: \t\t%u",GetResistance(SPELL_SCHOOL_NATURE), GetResistance(SPELL_SCHOOL_FROST));
+ sLog.outDebug("ShadowRes is: \t\t%u\t\tArcaneRes is: \t\t%u",GetResistance(SPELL_SCHOOL_SHADOW), GetResistance(SPELL_SCHOOL_ARCANE));
+ sLog.outDebug("MIN_DAMAGE is: \t\t%f\tMAX_DAMAGE is: \t\t%f",GetFloatValue(UNIT_FIELD_MINDAMAGE), GetFloatValue(UNIT_FIELD_MAXDAMAGE));
+ sLog.outDebug("MIN_OFFHAND_DAMAGE is: \t%f\tMAX_OFFHAND_DAMAGE is: \t%f",GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE), GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE));
+ sLog.outDebug("MIN_RANGED_DAMAGE is: \t%f\tMAX_RANGED_DAMAGE is: \t%f",GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE), GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE));
+ sLog.outDebug("ATTACK_TIME is: \t%u\t\tRANGE_ATTACK_TIME is: \t%u",GetAttackTime(BASE_ATTACK), GetAttackTime(RANGED_ATTACK));
+}
+
+/*********************************************************/
+/*** FLOOD FILTER SYSTEM ***/
+/*********************************************************/
+
+void Player::UpdateSpeakTime()
+{
+ // ignore chat spam protection for GMs in any mode
+ if(GetSession()->GetSecurity() > SEC_PLAYER)
+ return;
+
+ time_t current = time (NULL);
+ if(m_speakTime > current)
+ {
+ uint32 max_count = sWorld.getConfig(CONFIG_CHATFLOOD_MESSAGE_COUNT);
+ if(!max_count)
+ return;
+
+ ++m_speakCount;
+ if(m_speakCount >= max_count)
+ {
+ // prevent overwrite mute time, if message send just before mutes set, for example.
+ time_t new_mute = current + sWorld.getConfig(CONFIG_CHATFLOOD_MUTE_TIME);
+ if(GetSession()->m_muteTime < new_mute)
+ GetSession()->m_muteTime = new_mute;
+
+ m_speakCount = 0;
+ }
+ }
+ else
+ m_speakCount = 0;
+
+ m_speakTime = current + sWorld.getConfig(CONFIG_CHATFLOOD_MESSAGE_DELAY);
+}
+
+bool Player::CanSpeak() const
+{
+ return GetSession()->m_muteTime <= time (NULL);
+}
+
+/*********************************************************/
+/*** LOW LEVEL FUNCTIONS:Notifiers ***/
+/*********************************************************/
+
+void Player::SendAttackSwingNotInRange()
+{
+ WorldPacket data(SMSG_ATTACKSWING_NOTINRANGE, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SavePositionInDB(uint32 mapid, float x,float y,float z,float o,uint32 zone,uint64 guid)
+{
+ std::ostringstream ss;
+ ss << "UPDATE characters SET position_x='"<<x<<"',position_y='"<<y
+ << "',position_z='"<<z<<"',orientation='"<<o<<"',map='"<<mapid
+ << "',zone='"<<zone<<"',trans_x='0',trans_y='0',trans_z='0',"
+ << "transguid='0',taxi_path='' WHERE guid='"<< GUID_LOPART(guid) <<"'";
+ sLog.outDebug(ss.str().c_str());
+ CharacterDatabase.Execute(ss.str().c_str());
+}
+
+bool Player::SaveValuesArrayInDB(Tokens const& tokens, uint64 guid)
+{
+ std::ostringstream ss2;
+ ss2<<"UPDATE characters SET data='";
+ int i=0;
+ for (Tokens::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter, ++i)
+ {
+ ss2<<tokens[i]<<" ";
+ }
+ ss2<<"' WHERE guid='"<< GUID_LOPART(guid) <<"'";
+
+ return CharacterDatabase.Execute(ss2.str().c_str());
+}
+
+void Player::SetUInt32ValueInArray(Tokens& tokens,uint16 index, uint32 value)
+{
+ char buf[11];
+ snprintf(buf,11,"%u",value);
+
+ if(index >= tokens.size())
+ return;
+
+ tokens[index] = buf;
+}
+
+void Player::SetUInt32ValueInDB(uint16 index, uint32 value, uint64 guid)
+{
+ Tokens tokens;
+ if(!LoadValuesArrayFromDB(tokens,guid))
+ return;
+
+ if(index >= tokens.size())
+ return;
+
+ char buf[11];
+ snprintf(buf,11,"%u",value);
+ tokens[index] = buf;
+
+ SaveValuesArrayInDB(tokens,guid);
+}
+
+void Player::SetFloatValueInDB(uint16 index, float value, uint64 guid)
+{
+ uint32 temp;
+ memcpy(&temp, &value, sizeof(value));
+ Player::SetUInt32ValueInDB(index, temp, guid);
+}
+
+void Player::SendAttackSwingNotStanding()
+{
+ WorldPacket data(SMSG_ATTACKSWING_NOTSTANDING, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendAttackSwingDeadTarget()
+{
+ WorldPacket data(SMSG_ATTACKSWING_DEADTARGET, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendAttackSwingCantAttack()
+{
+ WorldPacket data(SMSG_ATTACKSWING_CANT_ATTACK, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendAttackSwingCancelAttack()
+{
+ WorldPacket data(SMSG_CANCEL_COMBAT, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendAttackSwingBadFacingAttack()
+{
+ WorldPacket data(SMSG_ATTACKSWING_BADFACING, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendAutoRepeatCancel()
+{
+ WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, 0);
+ GetSession()->SendPacket( &data );
+}
+
+void Player::SendExplorationExperience(uint32 Area, uint32 Experience)
+{
+ WorldPacket data( SMSG_EXPLORATION_EXPERIENCE, 8 );
+ data << Area;
+ data << Experience;
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendDungeonDifficulty(bool IsInGroup)
+{
+ uint8 val = 0x00000001;
+ WorldPacket data(MSG_SET_DUNGEON_DIFFICULTY, 12);
+ data << (uint32)GetDifficulty();
+ data << uint32(val);
+ data << uint32(IsInGroup);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendResetFailedNotify(uint32 mapid)
+{
+ WorldPacket data(SMSG_RESET_FAILED_NOTIFY, 4);
+ data << uint32(mapid);
+ GetSession()->SendPacket(&data);
+}
+
+/// Reset all solo instances and optionally send a message on success for each
+void Player::ResetInstances(uint8 method)
+{
+ // method can be INSTANCE_RESET_ALL, INSTANCE_RESET_CHANGE_DIFFICULTY, INSTANCE_RESET_GROUP_JOIN
+
+ // 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())
+ {
+ ++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;
+ }
+ }
+
+ // if the map is loaded, reset it
+ Map *map = MapManager::Instance().FindMap(p->GetMapId(), p->GetInstanceId());
+ if(map && map->IsDungeon())
+ ((InstanceMap*)map)->Reset(method);
+
+ // since this is a solo instance there should not be any players inside
+ if(method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
+ SendResetInstanceSuccess(p->GetMapId());
+
+ p->DeleteFromDB();
+ m_boundInstances[dif].erase(itr++);
+
+ // the following should remove the instance save from the manager and delete it as well
+ p->RemovePlayer(this);
+ }
+}
+
+void Player::SendResetInstanceSuccess(uint32 MapId)
+{
+ WorldPacket data(SMSG_INSTANCE_RESET, 4);
+ data << MapId;
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendResetInstanceFailed(uint32 reason, uint32 MapId)
+{
+ // TODO: find what other fail reasons there are besides players in the instance
+ WorldPacket data(SMSG_INSTANCE_RESET_FAILED, 4);
+ data << reason;
+ data << MapId;
+ GetSession()->SendPacket(&data);
+}
+
+/*********************************************************/
+/*** Update timers ***/
+/*********************************************************/
+
+///checks the 15 afk reports per 5 minutes limit
+void Player::UpdateAfkReport(time_t currTime)
+{
+ if(m_bgAfkReportedTimer <= currTime)
+ {
+ m_bgAfkReportedCount = 0;
+ m_bgAfkReportedTimer = currTime+5*MINUTE;
+ }
+}
+
+void Player::UpdateContestedPvP(uint32 diff)
+{
+ if(!m_contestedPvPTimer||isInCombat())
+ return;
+ if(m_contestedPvPTimer <= diff)
+ {
+ ResetContestedPvP();
+ }
+ else
+ m_contestedPvPTimer -= diff;
+}
+
+void Player::UpdatePvPFlag(time_t currTime)
+{
+ if(!IsPvP())
+ return;
+ if(pvpInfo.endTimer == 0 || currTime < (pvpInfo.endTimer + 300))
+ return;
+
+ UpdatePvP(false);
+}
+
+void Player::UpdateDuelFlag(time_t currTime)
+{
+ if(!duel || duel->startTimer == 0 ||currTime < duel->startTimer + 3)
+ return;
+
+ SetUInt32Value(PLAYER_DUEL_TEAM, 1);
+ duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 2);
+
+ duel->startTimer = 0;
+ duel->startTime = currTime;
+ duel->opponent->duel->startTimer = 0;
+ duel->opponent->duel->startTime = currTime;
+}
+
+void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
+{
+ if(!pet)
+ pet = GetPet();
+
+ if(returnreagent && (pet || m_temporaryUnsummonedPetNumber))
+ {
+ //returning of reagents only for players, so best done here
+ uint32 spellId = pet ? pet->GetUInt32Value(UNIT_CREATED_BY_SPELL) : m_oldpetspell;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+
+ if(spellInfo)
+ {
+ for(uint32 i = 0; i < 7; ++i)
+ {
+ if(spellInfo->Reagent[i] > 0)
+ {
+ ItemPosCountVec dest; //for succubus, voidwalker, felhunter and felguard credit soulshard when despawn reason other than death (out of range, logout)
+ uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->Reagent[i], spellInfo->ReagentCount[i] );
+ if( msg == EQUIP_ERR_OK )
+ {
+ Item* item = StoreNewItem( dest, spellInfo->Reagent[i], true);
+ if(IsInWorld())
+ SendNewItem(item,spellInfo->ReagentCount[i],true,false);
+ }
+ }
+ }
+ }
+ m_temporaryUnsummonedPetNumber = 0;
+ }
+
+ if(!pet || pet->GetOwnerGUID()!=GetGUID())
+ return;
+
+ // only if current pet in slot
+ switch(pet->getPetType())
+ {
+ case MINI_PET:
+ m_miniPet = 0;
+ break;
+ case GUARDIAN_PET:
+ m_guardianPets.erase(pet->GetGUID());
+ break;
+ default:
+ if(GetPetGUID()==pet->GetGUID())
+ SetPet(0);
+ break;
+ }
+
+ pet->CombatStop();
+
+ if(returnreagent)
+ {
+ switch(pet->GetEntry())
+ {
+ //warlock pets except imp are removed(?) when logging out
+ case 1860:
+ case 1863:
+ case 417:
+ case 17252:
+ mode = PET_SAVE_NOT_IN_SLOT;
+ break;
+ }
+ }
+
+ pet->SavePetToDB(mode);
+
+ pet->CleanupsBeforeDelete();
+ pet->AddObjectToRemoveList();
+ pet->m_removed = true;
+
+ if(pet->isControlled())
+ {
+ WorldPacket data(SMSG_PET_SPELLS, 8);
+ data << uint64(0);
+ GetSession()->SendPacket(&data);
+
+ if(GetGroup())
+ SetGroupUpdateFlag(GROUP_UPDATE_PET);
+ }
+}
+
+
+void Player::RemoveMiniPet()
+{
+ if(Pet* pet = GetMiniPet())
+ {
+ pet->Remove(PET_SAVE_AS_DELETED);
+ m_miniPet = 0;
+ }
+}
+
+Pet* Player::GetMiniPet()
+{
+ if(!m_miniPet)
+ return NULL;
+ return ObjectAccessor::GetPet(m_miniPet);
+}
+
+void Player::RemoveGuardians()
+{
+ while(!m_guardianPets.empty())
+ {
+ uint64 guid = *m_guardianPets.begin();
+ if(Pet* pet = ObjectAccessor::GetPet(guid))
+ pet->Remove(PET_SAVE_AS_DELETED);
+
+ m_guardianPets.erase(guid);
+ }
+}
+
+bool Player::HasGuardianWithEntry(uint32 entry)
+{
+ // pet guid middle part is entry (and creature also)
+ // and in guardian list must be guardians with same entry _always_
+ for(GuardianPetList::const_iterator itr = m_guardianPets.begin(); itr != m_guardianPets.end(); ++itr)
+ if(GUID_ENPART(*itr)==entry)
+ return true;
+
+ return false;
+}
+
+void Player::Uncharm()
+{
+ Unit* charm = GetCharm();
+ if(!charm)
+ return;
+
+ charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_CHARM);
+ charm->RemoveSpellsCausingAura(SPELL_AURA_MOD_POSSESS);
+}
+
+void Player::BuildPlayerChat(WorldPacket *data, uint8 msgtype, std::string text, uint32 language) const
+{
+ *data << (uint8)msgtype;
+ *data << (uint32)language;
+ *data << (uint64)GetGUID();
+ *data << (uint32)language; //language 2.1.0 ?
+ *data << (uint64)GetGUID();
+ *data << (uint32)(text.length()+1);
+ *data << text;
+ *data << (uint8)chatTag();
+}
+
+void Player::Say(const std::string text, const uint32 language)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildPlayerChat(&data, CHAT_MSG_SAY, text, language);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),true);
+}
+
+void Player::Yell(const std::string text, const uint32 language)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildPlayerChat(&data, CHAT_MSG_YELL, text, language);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL),true);
+}
+
+void Player::TextEmote(const std::string text)
+{
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildPlayerChat(&data, CHAT_MSG_EMOTE, text, LANG_UNIVERSAL);
+ SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true, !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) );
+}
+
+void Player::Whisper(std::string text, uint32 language,uint64 receiver)
+{
+ if (language != LANG_ADDON) // if not addon data
+ language = LANG_UNIVERSAL; // whispers should always be readable
+
+ Player *rPlayer = objmgr.GetPlayer(receiver);
+
+ // when player you are whispering to is dnd, he cannot receive your message, unless you are in gm mode
+ if(!rPlayer->isDND() || isGameMaster())
+ {
+ WorldPacket data(SMSG_MESSAGECHAT, 200);
+ BuildPlayerChat(&data, CHAT_MSG_WHISPER, text, language);
+ rPlayer->GetSession()->SendPacket(&data);
+
+ data.Initialize(SMSG_MESSAGECHAT, 200);
+ rPlayer->BuildPlayerChat(&data, CHAT_MSG_REPLY, text, language);
+ GetSession()->SendPacket(&data);
+ }
+ else
+ {
+ // announce to player that player he is whispering to is dnd and cannot receive his message
+ ChatHandler(this).PSendSysMessage(LANG_PLAYER_DND, rPlayer->GetName(), rPlayer->dndMsg.c_str());
+ }
+
+ if(!isAcceptWhispers())
+ {
+ SetAcceptWhispers(true);
+ ChatHandler(this).SendSysMessage(LANG_COMMAND_WHISPERON);
+ }
+
+ // announce to player that player he is whispering to is afk
+ if(rPlayer->isAFK())
+ ChatHandler(this).PSendSysMessage(LANG_PLAYER_AFK, rPlayer->GetName(), rPlayer->afkMsg.c_str());
+
+ // if player whisper someone, auto turn of dnd to be able to receive an answer
+ if(isDND() && !rPlayer->isGameMaster())
+ ToggleDND();
+}
+
+void Player::PetSpellInitialize()
+{
+ Pet* pet = GetPet();
+
+ if(pet)
+ {
+ uint8 addlist = 0;
+
+ sLog.outDebug("Pet Spells Groups");
+
+ CreatureInfo const *cinfo = pet->GetCreatureInfo();
+
+ if(pet->isControlled() && (pet->getPetType() == HUNTER_PET || cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK))
+ {
+ for(PetSpellMap::iterator itr = pet->m_spells.begin();itr != pet->m_spells.end();itr++)
+ {
+ if(itr->second->state == PETSPELL_REMOVED)
+ continue;
+ ++addlist;
+ }
+ }
+
+ // first line + actionbar + spellcount + spells + last adds
+ WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);
+
+ CharmInfo *charmInfo = pet->GetCharmInfo();
+
+ //16
+ data << (uint64)pet->GetGUID() << uint32(0x00000000) << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0);
+
+ for(uint32 i = 0; i < 10; i++) //40
+ {
+ data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
+ }
+
+ data << uint8(addlist); //1
+
+ if(addlist && pet->isControlled())
+ {
+ for (PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PETSPELL_REMOVED)
+ continue;
+
+ data << uint16(itr->first);
+ data << uint16(itr->second->active); // pet spell active state isn't boolean
+ }
+ }
+
+ //data << uint8(0x01) << uint32(0x6010) << uint32(0x01) << uint32(0x05) << uint16(0x00); //15
+ uint8 count = 3; //1+8+8+8=25
+
+ // if count = 0, then end of packet...
+ data << count;
+ // uint32 value is spell id...
+ // uint64 value is constant 0, unknown...
+ data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3
+ //data << uint32(0x5fd1) << uint64(0); // if count = 2
+ data << uint32(0x8e8c) << uint64(0); // if count = 3
+ data << uint32(0x8e8b) << uint64(0); // if count = 3
+
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::PossessSpellInitialize()
+{
+ Unit* charm = GetCharm();
+
+ if(!charm)
+ return;
+
+ CharmInfo *charmInfo = charm->GetCharmInfo();
+
+ if(!charmInfo)
+ {
+ sLog.outError("Player::PossessSpellInitialize(): charm ("I64FMTD") has no charminfo!", charm->GetGUID());
+ return;
+ }
+
+ uint8 addlist = 0;
+ WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);// first line + actionbar + spellcount + spells + last adds
+
+ //16
+ data << (uint64)charm->GetGUID() << uint32(0x00000000) << uint8(0) << uint8(0) << uint16(0);
+
+ for(uint32 i = 0; i < 10; i++) //40
+ {
+ data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
+ }
+
+ data << uint8(addlist); //1
+
+ uint8 count = 3;
+ data << count;
+ data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3
+ data << uint32(0x8e8c) << uint64(0); // if count = 3
+ data << uint32(0x8e8b) << uint64(0); // if count = 3
+
+ GetSession()->SendPacket(&data);
+}
+
+void Player::CharmSpellInitialize()
+{
+ Unit* charm = GetCharm();
+
+ if(!charm)
+ return;
+
+ CharmInfo *charmInfo = charm->GetCharmInfo();
+ if(!charmInfo)
+ {
+ sLog.outError("Player::CharmSpellInitialize(): the player's charm ("I64FMTD") has no charminfo!", charm->GetGUID());
+ return;
+ }
+
+ uint8 addlist = 0;
+
+ if(charm->GetTypeId() != TYPEID_PLAYER)
+ {
+ CreatureInfo const *cinfo = ((Creature*)charm)->GetCreatureInfo();
+
+ if(cinfo && cinfo->type == CREATURE_TYPE_DEMON && getClass() == CLASS_WARLOCK)
+ {
+ for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ {
+ if(charmInfo->GetCharmSpell(i)->spellId)
+ ++addlist;
+ }
+ }
+ }
+
+ WorldPacket data(SMSG_PET_SPELLS, 16+40+1+4*addlist+25);// first line + actionbar + spellcount + spells + last adds
+
+ data << (uint64)charm->GetGUID() << uint32(0x00000000);
+
+ if(charm->GetTypeId() != TYPEID_PLAYER)
+ data << uint8(charmInfo->GetReactState()) << uint8(charmInfo->GetCommandState());
+ else
+ data << uint8(0) << uint8(0);
+
+ data << uint16(0);
+
+ for(uint32 i = 0; i < 10; i++) //40
+ {
+ data << uint16(charmInfo->GetActionBarEntry(i)->SpellOrAction) << uint16(charmInfo->GetActionBarEntry(i)->Type);
+ }
+
+ data << uint8(addlist); //1
+
+ if(addlist)
+ {
+ for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ {
+ CharmSpellEntry *cspell = charmInfo->GetCharmSpell(i);
+ if(cspell->spellId)
+ {
+ data << uint16(cspell->spellId);
+ data << uint16(cspell->active);
+ }
+ }
+ }
+
+ uint8 count = 3;
+ data << count;
+ data << uint32(0x6010) << uint64(0); // if count = 1, 2 or 3
+ data << uint32(0x8e8c) << uint64(0); // if count = 3
+ data << uint32(0x8e8b) << uint64(0); // if count = 3
+
+ GetSession()->SendPacket(&data);
+}
+
+int32 Player::GetTotalFlatMods(uint32 spellId, SpellModOp op)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if (!spellInfo) return 0;
+ int32 total = 0;
+ for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr)
+ {
+ SpellModifier *mod = *itr;
+
+ if(!IsAffectedBySpellmod(spellInfo,mod))
+ continue;
+
+ if (mod->type == SPELLMOD_FLAT)
+ total += mod->value;
+ }
+ return total;
+}
+
+int32 Player::GetTotalPctMods(uint32 spellId, SpellModOp op)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if (!spellInfo) return 0;
+ int32 total = 0;
+ for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr)
+ {
+ SpellModifier *mod = *itr;
+
+ if(!IsAffectedBySpellmod(spellInfo,mod))
+ continue;
+
+ if (mod->type == SPELLMOD_PCT)
+ total += mod->value;
+ }
+ return total;
+}
+
+bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell)
+{
+ if (!mod || !spellInfo)
+ return false;
+
+ if(mod->charges == -1 && mod->lastAffected ) // marked as expired but locked until spell casting finish
+ {
+ // prevent apply to any spell except spell that trigger expire
+ if(spell)
+ {
+ if(mod->lastAffected != spell)
+ return false;
+ }
+ else if(mod->lastAffected != FindCurrentSpellBySpellId(spellInfo->Id))
+ return false;
+ }
+
+ return spellmgr.IsAffectedBySpell(spellInfo,mod->spellId,mod->effectId,mod->mask);
+}
+
+void Player::AddSpellMod(SpellModifier* mod, bool apply)
+{
+ uint16 Opcode= (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER;
+
+ for(int eff=0;eff<64;++eff)
+ {
+ uint64 _mask = uint64(1) << eff;
+ if ( mod->mask & _mask)
+ {
+ int32 val = 0;
+ for (SpellModList::iterator itr = m_spellMods[mod->op].begin(); itr != m_spellMods[mod->op].end(); ++itr)
+ {
+ if ((*itr)->type == mod->type && (*itr)->mask & _mask)
+ val += (*itr)->value;
+ }
+ val += apply ? mod->value : -(mod->value);
+ WorldPacket data(Opcode, (1+1+4));
+ data << uint8(eff);
+ data << uint8(mod->op);
+ data << int32(val);
+ SendDirectMessage(&data);
+ }
+ }
+
+ if (apply)
+ m_spellMods[mod->op].push_back(mod);
+ else
+ {
+ if (mod->charges == -1)
+ --m_SpellModRemoveCount;
+ m_spellMods[mod->op].remove(mod);
+ delete mod;
+ }
+}
+
+void Player::RemoveSpellMods(Spell const* spell)
+{
+ if(!spell || (m_SpellModRemoveCount == 0))
+ return;
+
+ for(int i=0;i<MAX_SPELLMOD;++i)
+ {
+ for (SpellModList::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();)
+ {
+ SpellModifier *mod = *itr;
+ ++itr;
+
+ if (mod && mod->charges == -1 && (mod->lastAffected == spell || mod->lastAffected==NULL))
+ {
+ RemoveAurasDueToSpell(mod->spellId);
+ if (m_spellMods[i].empty())
+ break;
+ else
+ itr = m_spellMods[i].begin();
+ }
+ }
+ }
+}
+
+// send Proficiency
+void Player::SendProficiency(uint8 pr1, uint32 pr2)
+{
+ WorldPacket data(SMSG_SET_PROFICIENCY, 8);
+ data << pr1 << pr2;
+ GetSession()->SendPacket (&data);
+}
+
+void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type)
+{
+ QueryResult *result = NULL;
+ if(type==10)
+ result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid));
+ else
+ result = CharacterDatabase.PQuery("SELECT ownerguid,petitionguid FROM petition_sign WHERE playerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
+ if(result)
+ {
+ do // this part effectively does nothing, since the deletion / modification only takes place _after_ the PetitionQuery. Though I don't know if the result remains intact if I execute the delete query beforehand.
+ { // and SendPetitionQueryOpcode reads data from the DB
+ Field *fields = result->Fetch();
+ uint64 ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+ uint64 petitionguid = MAKE_NEW_GUID(fields[1].GetUInt32(), 0, HIGHGUID_ITEM);
+
+ // send update if charter owner in game
+ Player* owner = objmgr.GetPlayer(ownerguid);
+ if(owner)
+ owner->GetSession()->SendPetitionQueryOpcode(petitionguid);
+
+ } while ( result->NextRow() );
+
+ delete result;
+
+ if(type==10)
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u'", GUID_LOPART(guid));
+ else
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE playerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
+ }
+
+ CharacterDatabase.BeginTransaction();
+ if(type == 10)
+ {
+ CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u'", GUID_LOPART(guid));
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u'", GUID_LOPART(guid));
+ }
+ else
+ {
+ CharacterDatabase.PExecute("DELETE FROM petition WHERE ownerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
+ CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE ownerguid = '%u' AND type = '%u'", GUID_LOPART(guid), type);
+ }
+ CharacterDatabase.CommitTransaction();
+}
+
+void Player::SetRestBonus (float rest_bonus_new)
+{
+ // Prevent resting on max level
+ if(getLevel() >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ rest_bonus_new = 0;
+
+ if(rest_bonus_new < 0)
+ rest_bonus_new = 0;
+
+ float rest_bonus_max = (float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)*1.5/2;
+
+ if(rest_bonus_new > rest_bonus_max)
+ m_rest_bonus = rest_bonus_max;
+ else
+ m_rest_bonus = rest_bonus_new;
+
+ // update data for client
+ if(m_rest_bonus>10)
+ SetByteValue(PLAYER_BYTES_2, 3, 0x01); // Set Reststate = Rested
+ else if(m_rest_bonus<=1)
+ SetByteValue(PLAYER_BYTES_2, 3, 0x02); // Set Reststate = Normal
+
+ //RestTickUpdate
+ SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus));
+}
+
+void Player::HandleStealthedUnitsDetection()
+{
+ std::list<Unit*> stealthedUnits;
+
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::AnyStealthedCheck u_check;
+ MaNGOS::UnitListSearcher<MaNGOS::AnyStealthedCheck > searcher(stealthedUnits, u_check);
+
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyStealthedCheck >, WorldTypeMapContainer > world_unit_searcher(searcher);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyStealthedCheck >, GridTypeMapContainer > grid_unit_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
+
+ for (std::list<Unit*>::iterator i = stealthedUnits.begin(); i != stealthedUnits.end();)
+ {
+ if((*i)==this)
+ {
+ i = stealthedUnits.erase(i);
+ continue;
+ }
+
+ if ((*i)->isVisibleForOrDetect(this,true))
+ {
+
+ (*i)->SendUpdateToPlayer(this);
+ m_clientGUIDs.insert((*i)->GetGUID());
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
+ sLog.outDebug("Object %u (Type: %u) is detected in stealth by player %u. Distance = %f",(*i)->GetGUIDLow(),(*i)->GetTypeId(),GetGUIDLow(),GetDistance(*i));
+ #endif
+
+ // target aura duration for caster show only if target exist at caster client
+ // send data at target visibility change (adding to client)
+ if((*i)!=this && (*i)->isType(TYPEMASK_UNIT))
+ SendAuraDurationsForTarget(*i);
+
+ i = stealthedUnits.erase(i);
+ continue;
+ }
+
+ ++i;
+ }
+}
+
+bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, uint32 mount_id, Creature* npc)
+{
+ if(nodes.size() < 2)
+ return false;
+
+ // not let cheating with start flight mounted
+ if(IsMounted())
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIPLAYERALREADYMOUNTED);
+ GetSession()->SendPacket(&data);
+ return false;
+ }
+
+ if( m_ShapeShiftFormSpellId && m_form != FORM_BATTLESTANCE && m_form != FORM_BERSERKERSTANCE && m_form != FORM_DEFENSIVESTANCE && m_form != FORM_SHADOW )
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIPLAYERSHAPESHIFTED);
+ GetSession()->SendPacket(&data);
+ return false;
+ }
+
+ // not let cheating with start flight in time of logout process || if casting not finished || while in combat || if not use Spell's with EffectSendTaxi
+ if(GetSession()->isLogingOut() ||
+ (!m_currentSpells[CURRENT_GENERIC_SPELL] ||
+ m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Effect[0] != SPELL_EFFECT_SEND_TAXI)&&
+ IsNonMeleeSpellCasted(false) ||
+ isInCombat())
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIPLAYERBUSY);
+ GetSession()->SendPacket(&data);
+ return false;
+ }
+
+ if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
+ return false;
+
+ uint32 sourcenode = nodes[0];
+
+ // starting node too far away (cheat?)
+ TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(sourcenode);
+ if( !node || node->map_id != GetMapId() ||
+ (node->x - GetPositionX())*(node->x - GetPositionX())+
+ (node->y - GetPositionY())*(node->y - GetPositionY())+
+ (node->z - GetPositionZ())*(node->z - GetPositionZ()) >
+ (2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE)*(2*INTERACTION_DISTANCE) )
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR);
+ GetSession()->SendPacket(&data);
+ return false;
+ }
+
+ // Prepare to flight start now
+
+ // stop combat at start taxi flight if any
+ CombatStop();
+
+ // stop trade (client cancel trade at taxi map open but cheating tools can be used for reopen it)
+ TradeCancel(true);
+
+ // clean not finished taxi path if any
+ m_taxi.ClearTaxiDestinations();
+
+ // 0 element current node
+ m_taxi.AddTaxiDestination(sourcenode);
+
+ // fill destinations path tail
+ uint32 sourcepath = 0;
+ uint32 totalcost = 0;
+
+ uint32 prevnode = sourcenode;
+ uint32 lastnode = 0;
+
+ for(uint32 i = 1; i < nodes.size(); ++i)
+ {
+ uint32 path, cost;
+
+ lastnode = nodes[i];
+ objmgr.GetTaxiPath(prevnode, lastnode, path, cost);
+
+ if(!path)
+ {
+ m_taxi.ClearTaxiDestinations();
+ return false;
+ }
+
+ totalcost += cost;
+
+ if(prevnode == sourcenode)
+ sourcepath = path;
+
+ m_taxi.AddTaxiDestination(lastnode);
+
+ prevnode = lastnode;
+ }
+
+ if(!mount_id) // if not provide then attempt use default.
+ mount_id = objmgr.GetTaxiMount(sourcenode, GetTeam());
+
+ if (mount_id == 0 || sourcepath == 0)
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIUNSPECIFIEDSERVERERROR);
+ GetSession()->SendPacket(&data);
+ m_taxi.ClearTaxiDestinations();
+ return false;
+ }
+
+ uint32 money = GetMoney();
+
+ if(npc)
+ {
+ totalcost = (uint32)ceil(totalcost*GetReputationPriceDiscount(npc));
+ }
+
+ if(money < totalcost)
+ {
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXINOTENOUGHMONEY);
+ GetSession()->SendPacket(&data);
+ m_taxi.ClearTaxiDestinations();
+ return false;
+ }
+
+ //Checks and preparations done, DO FLIGHT
+ ModifyMoney(-(int32)totalcost);
+
+ // prevent stealth flight
+ RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+
+ WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
+ data << uint32(ERR_TAXIOK);
+ GetSession()->SendPacket(&data);
+
+ sLog.outDebug("WORLD: Sent SMSG_ACTIVATETAXIREPLY");
+
+ GetSession()->SendDoFlight(mount_id, sourcepath);
+
+ return true;
+}
+
+void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs )
+{
+ // last check 2.0.10
+ WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+m_spells.size()*8);
+ data << GetGUID();
+ data << uint8(0x0);
+ time_t curTime = time(NULL);
+ for(PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if (itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+ uint32 unSpellId = itr->first;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(unSpellId);
+ if (!spellInfo)
+ {
+ ASSERT(spellInfo);
+ continue;
+ }
+
+ // Not send cooldown for this spells
+ if (spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
+ continue;
+
+ if((idSchoolMask & GetSpellSchoolMask(spellInfo)) && GetSpellCooldownDelay(unSpellId) < unTimeMs )
+ {
+ data << unSpellId;
+ data << unTimeMs; // in m.secs
+ AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/1000);
+ }
+ }
+ GetSession()->SendPacket(&data);
+}
+
+void Player::InitDataForForm(bool reapplyMods)
+{
+ SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(m_form);
+ if(ssEntry && ssEntry->attackSpeed)
+ {
+ SetAttackTime(BASE_ATTACK,ssEntry->attackSpeed);
+ SetAttackTime(OFF_ATTACK,ssEntry->attackSpeed);
+ SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
+ }
+ else
+ SetRegularAttackTime();
+
+ switch(m_form)
+ {
+ case FORM_CAT:
+ {
+ if(getPowerType()!=POWER_ENERGY)
+ setPowerType(POWER_ENERGY);
+ break;
+ }
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ {
+ if(getPowerType()!=POWER_RAGE)
+ setPowerType(POWER_RAGE);
+ break;
+ }
+ default: // 0, for example
+ {
+ ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(getClass());
+ if(cEntry && cEntry->powerType < MAX_POWERS && uint32(getPowerType()) != cEntry->powerType)
+ setPowerType(Powers(cEntry->powerType));
+ break;
+ }
+ }
+
+ // update auras at form change, ignore this at mods reapply (.reset stats/etc) when form not change.
+ if (!reapplyMods)
+ UpdateEquipSpellsAtFormChange();
+
+ UpdateAttackPowerAndDamage();
+ UpdateAttackPowerAndDamage(true);
+}
+
+// Return true is the bought item has a max count to force refresh of window by caller
+bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint64 bagguid, uint8 slot)
+{
+ // cheating attempt
+ if(count < 1) count = 1;
+
+ if(!isAlive())
+ return false;
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
+ if( !pProto )
+ {
+ SendBuyError( BUY_ERR_CANT_FIND_ITEM, NULL, item, 0);
+ return false;
+ }
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*this, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: BuyItemFromVendor - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ SendBuyError( BUY_ERR_DISTANCE_TOO_FAR, NULL, item, 0);
+ return false;
+ }
+
+ VendorItemData const* vItems = pCreature->GetVendorItems();
+ if(!vItems || vItems->Empty())
+ {
+ SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
+ return false;
+ }
+
+ 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 )
+ {
+ if(pCreature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count )
+ {
+ SendBuyError( BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0);
+ return false;
+ }
+ }
+
+ if( uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank)
+ {
+ SendBuyError( BUY_ERR_REPUTATION_REQUIRE, pCreature, item, 0);
+ return false;
+ }
+
+ if(crItem->ExtendedCost)
+ {
+ ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
+ if(!iece)
+ {
+ sLog.outError("Item %u have wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost);
+ return false;
+ }
+
+ // honor points price
+ if(GetHonorPoints() < (iece->reqhonorpoints * count))
+ {
+ SendEquipError(EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS, NULL, NULL);
+ return false;
+ }
+
+ // arena points price
+ if(GetArenaPoints() < (iece->reqarenapoints * count))
+ {
+ SendEquipError(EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS, NULL, NULL);
+ return false;
+ }
+
+ // item base price
+ for (uint8 i = 0; i < 5; ++i)
+ {
+ if(iece->reqitem[i] && !HasItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count)))
+ {
+ SendEquipError(EQUIP_ERR_VENDOR_MISSING_TURNINS, NULL, NULL);
+ return false;
+ }
+ }
+
+ // check for personal arena rating requirement
+ if( GetMaxPersonalArenaRatingRequirement() < iece->reqpersonalarenarating )
+ {
+ // probably not the proper equip err
+ SendEquipError(EQUIP_ERR_CANT_EQUIP_RANK,NULL,NULL);
+ return false;
+ }
+ }
+
+ uint32 price = pProto->BuyPrice * count;
+
+ // reputation discount
+ price = uint32(floor(price * GetReputationPriceDiscount(pCreature)));
+
+ if( GetMoney() < price )
+ {
+ SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, item, 0);
+ return false;
+ }
+
+ uint8 bag = 0; // init for case invalid bagGUID
+
+ if (bagguid != NULL_BAG && slot != NULL_SLOT)
+ {
+ Bag *pBag;
+ if( bagguid == GetGUID() )
+ {
+ bag = INVENTORY_SLOT_BAG_0;
+ }
+ else
+ {
+ for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END;i++)
+ {
+ pBag = (Bag*)GetItemByPos(INVENTORY_SLOT_BAG_0,i);
+ if( pBag )
+ {
+ if( bagguid == pBag->GetGUID() )
+ {
+ bag = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if( IsInventoryPos( bag, slot ) || (bagguid == NULL_BAG && slot == NULL_SLOT) )
+ {
+ ItemPosCountVec dest;
+ uint8 msg = CanStoreNewItem( bag, slot, dest, item, pProto->BuyCount * count );
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, NULL, NULL );
+ return false;
+ }
+
+ ModifyMoney( -(int32)price );
+ if(crItem->ExtendedCost) // case for new honor system
+ {
+ ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
+ if(iece->reqhonorpoints)
+ ModifyHonorPoints( - int32(iece->reqhonorpoints * count));
+ if(iece->reqarenapoints)
+ ModifyArenaPoints( - int32(iece->reqarenapoints * count));
+ for (uint8 i = 0; i < 5; ++i)
+ {
+ if(iece->reqitem[i])
+ DestroyItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count), true);
+ }
+ }
+
+ if(Item *it = StoreNewItem( dest, item, true ))
+ {
+ uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count);
+
+ WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
+ data << pCreature->GetGUID();
+ data << (uint32)(vendor_slot+1); // numbered from 1 at client
+ data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
+ data << (uint32)count;
+ GetSession()->SendPacket(&data);
+
+ SendNewItem(it, pProto->BuyCount*count, true, false, false);
+ }
+ }
+ else if( IsEquipmentPos( bag, slot ) )
+ {
+ uint16 dest;
+ uint8 msg = CanEquipNewItem( slot, dest, item, pProto->BuyCount * count, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ SendEquipError( msg, NULL, NULL );
+ return false;
+ }
+
+ ModifyMoney( -(int32)price );
+ if(crItem->ExtendedCost) // case for new honor system
+ {
+ ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
+ if(iece->reqhonorpoints)
+ ModifyHonorPoints( - int32(iece->reqhonorpoints));
+ if(iece->reqarenapoints)
+ ModifyArenaPoints( - int32(iece->reqarenapoints));
+ for (uint8 i = 0; i < 5; ++i)
+ {
+ if(iece->reqitem[i])
+ DestroyItemCount(iece->reqitem[i], iece->reqitemcount[i], true);
+ }
+ }
+
+ if(Item *it = EquipNewItem( dest, item, pProto->BuyCount * count, true ))
+ {
+ uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count);
+
+ WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
+ data << pCreature->GetGUID();
+ data << (uint32)(vendor_slot+1); // numbered from 1 at client
+ data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
+ data << (uint32)count;
+ GetSession()->SendPacket(&data);
+
+ SendNewItem(it, pProto->BuyCount*count, true, false, false);
+
+ AutoUnequipOffhandIfNeed();
+ }
+ }
+ else
+ {
+ SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
+ return false;
+ }
+
+ return crItem->maxcount!=0;
+}
+
+uint32 Player::GetMaxPersonalArenaRatingRequirement()
+{
+ // returns the maximal personal arena rating that can be used to purchase items requiring this condition
+ // the personal rating of the arena team must match the required limit as well
+ // so return max[in arenateams](min(personalrating[teamtype], teamrating[teamtype]))
+ uint32 max_personal_rating = 0;
+ for(int i = 0; i < MAX_ARENA_SLOT; ++i)
+ {
+ if(ArenaTeam * at = objmgr.GetArenaTeamById(GetArenaTeamId(i)))
+ {
+ uint32 p_rating = GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (i * 6) + 5);
+ uint32 t_rating = at->GetRating();
+ p_rating = p_rating<t_rating? p_rating : t_rating;
+ if(max_personal_rating < p_rating)
+ max_personal_rating = p_rating;
+ }
+ }
+ return max_personal_rating;
+}
+
+void Player::UpdateHomebindTime(uint32 time)
+{
+ // GMs never get homebind timer online
+ if (m_InstanceValid || isGameMaster())
+ {
+ if(m_HomebindTimer) // instance valid, but timer not reset
+ {
+ // hide reminder
+ WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
+ data << uint32(0);
+ data << uint32(0);
+ GetSession()->SendPacket(&data);
+ }
+ // instance is valid, reset homebind timer
+ m_HomebindTimer = 0;
+ }
+ else if (m_HomebindTimer > 0)
+ {
+ if (time >= m_HomebindTimer)
+ {
+ // teleport to homebind location
+ TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation());
+ }
+ else
+ m_HomebindTimer -= time;
+ }
+ else
+ {
+ // instance is invalid, start homebind timer
+ m_HomebindTimer = 60000;
+ // send message to player
+ WorldPacket data(SMSG_RAID_GROUP_ONLY, 4+4);
+ data << m_HomebindTimer;
+ data << uint32(1);
+ GetSession()->SendPacket(&data);
+ sLog.outDebug("PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName(),GetGUIDLow());
+ }
+}
+
+void Player::UpdatePvP(bool state, bool ovrride)
+{
+ if(!state || ovrride)
+ {
+ SetPvP(state);
+ if(Pet* pet = GetPet())
+ pet->SetPvP(state);
+ if(Unit* charmed = GetCharm())
+ charmed->SetPvP(state);
+
+ pvpInfo.endTimer = 0;
+ }
+ else
+ {
+ if(pvpInfo.endTimer != 0)
+ pvpInfo.endTimer = time(NULL);
+ else
+ {
+ SetPvP(state);
+
+ if(Pet* pet = GetPet())
+ pet->SetPvP(state);
+ if(Unit* charmed = GetCharm())
+ charmed->SetPvP(state);
+ }
+ }
+}
+
+void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
+{
+ SpellCooldown sc;
+ sc.end = end_time;
+ sc.itemid = itemid;
+ m_spellCooldowns[spellid] = sc;
+}
+
+void Player::SendCooldownEvent(SpellEntry const *spellInfo)
+{
+ if ( !(spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) )
+ return;
+
+ // Get spell cooldwn
+ int32 cooldown = GetSpellRecoveryTime(spellInfo);
+ // Apply spellmods
+ ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown);
+ if (cooldown < 0)
+ cooldown = 0;
+ // Add cooldown
+ AddSpellCooldown(spellInfo->Id, 0, time(NULL) + cooldown / 1000);
+ // Send activate
+ WorldPacket data(SMSG_COOLDOWN_EVENT, (4+8));
+ data << spellInfo->Id;
+ data << GetGUID();
+ SendDirectMessage(&data);
+}
+ //slot to be excluded while counting
+bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot)
+{
+ if(!enchantmentcondition)
+ return true;
+
+ SpellItemEnchantmentConditionEntry const *Condition = sSpellItemEnchantmentConditionStore.LookupEntry(enchantmentcondition);
+
+ if(!Condition)
+ return true;
+
+ uint8 curcount[4] = {0, 0, 0, 0};
+
+ //counting current equipped gem colors
+ for(uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i)
+ {
+ if(i == slot)
+ continue;
+ Item *pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+ if(pItem2 && pItem2->GetProto()->Socket[0].Color)
+ {
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ {
+ uint32 enchant_id = pItem2->GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id)
+ continue;
+
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ continue;
+
+ uint32 gemid = enchantEntry->GemID;
+ if(!gemid)
+ continue;
+
+ ItemPrototype const* gemProto = sItemStorage.LookupEntry<ItemPrototype>(gemid);
+ if(!gemProto)
+ continue;
+
+ GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties);
+ if(!gemProperty)
+ continue;
+
+ uint8 GemColor = gemProperty->color;
+
+ for(uint8 b = 0, tmpcolormask = 1; b < 4; b++, tmpcolormask <<= 1)
+ {
+ if(tmpcolormask & GemColor)
+ ++curcount[b];
+ }
+ }
+ }
+ }
+
+ bool activate = true;
+
+ for(int i = 0; i < 5; i++)
+ {
+ if(!Condition->Color[i])
+ continue;
+
+ uint32 _cur_gem = curcount[Condition->Color[i] - 1];
+
+ // if have <CompareColor> use them as count, else use <value> from Condition
+ uint32 _cmp_gem = Condition->CompareColor[i] ? curcount[Condition->CompareColor[i] - 1]: Condition->Value[i];
+
+ switch(Condition->Comparator[i])
+ {
+ case 2: // requires less <color> than (<value> || <comparecolor>) gems
+ activate &= (_cur_gem < _cmp_gem) ? true : false;
+ break;
+ case 3: // requires more <color> than (<value> || <comparecolor>) gems
+ activate &= (_cur_gem > _cmp_gem) ? true : false;
+ break;
+ case 5: // requires at least <color> than (<value> || <comparecolor>) gems
+ activate &= (_cur_gem >= _cmp_gem) ? true : false;
+ break;
+ }
+ }
+
+ sLog.outDebug("Checking Condition %u, there are %u Meta Gems, %u Red Gems, %u Yellow Gems and %u Blue Gems, Activate:%s", enchantmentcondition, curcount[0], curcount[1], curcount[2], curcount[3], activate ? "yes" : "no");
+
+ return activate;
+}
+
+void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply)
+{
+ //cycle all equipped items
+ for(uint32 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
+ {
+ //enchants for the slot being socketed are handled by Player::ApplyItemMods
+ if(slot == exceptslot)
+ continue;
+
+ Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot );
+
+ if(!pItem || !pItem->GetProto()->Socket[0].Color)
+ continue;
+
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ {
+ uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id)
+ continue;
+
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ continue;
+
+ uint32 condition = enchantEntry->EnchantmentCondition;
+ if(condition)
+ {
+ //was enchant active with/without item?
+ bool wasactive = EnchantmentFitsRequirements(condition, apply ? exceptslot : -1);
+ //should it now be?
+ if(wasactive ^ EnchantmentFitsRequirements(condition, apply ? -1 : exceptslot))
+ {
+ // ignore item gem conditions
+ //if state changed, (dis)apply enchant
+ ApplyEnchantment(pItem,EnchantmentSlot(enchant_slot),!wasactive,true,true);
+ }
+ }
+ }
+ }
+}
+
+ //if false -> then toggled off if was on| if true -> toggled on if was off AND meets requirements
+void Player::ToggleMetaGemsActive(uint8 exceptslot, bool apply)
+{
+ //cycle all equipped items
+ for(int slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot)
+ {
+ //enchants for the slot being socketed are handled by WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
+ if(slot == exceptslot)
+ continue;
+
+ Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, slot );
+
+ if(!pItem || !pItem->GetProto()->Socket[0].Color) //if item has no sockets or no item is equipped go to next item
+ continue;
+
+ //cycle all (gem)enchants
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ {
+ uint32 enchant_id = pItem->GetEnchantmentId(EnchantmentSlot(enchant_slot));
+ if(!enchant_id) //if no enchant go to next enchant(slot)
+ continue;
+
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!enchantEntry)
+ continue;
+
+ //only metagems to be (de)activated, so only enchants with condition
+ uint32 condition = enchantEntry->EnchantmentCondition;
+ if(condition)
+ ApplyEnchantment(pItem,EnchantmentSlot(enchant_slot), apply);
+ }
+ }
+}
+
+void Player::LeaveBattleground(bool teleportToEntryPoint)
+{
+ if(BattleGround *bg = GetBattleGround())
+ {
+ bool need_debuf = bg->isBattleGround() && (bg->GetStatus() == STATUS_IN_PROGRESS) && sWorld.getConfig(CONFIG_BATTLEGROUND_CAST_DESERTER);
+
+ bg->RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true);
+
+ // call after remove to be sure that player resurrected for correct cast
+ if(need_debuf)
+ CastSpell(this, 26013, true); // Deserter
+ }
+}
+
+bool Player::CanJoinToBattleground() const
+{
+ // check Deserter debuff
+ if(GetDummyAura(26013))
+ return false;
+
+ return true;
+}
+
+bool Player::CanReportAfkDueToLimit()
+{
+ // a player can complain about 15 people per 5 minutes
+ if(m_bgAfkReportedCount >= 15)
+ return false;
+ ++m_bgAfkReportedCount;
+ return true;
+}
+
+///This player has been blamed to be inactive in a battleground
+void Player::ReportedAfkBy(Player* reporter)
+{
+ BattleGround *bg = GetBattleGround();
+ if(!bg || bg != reporter->GetBattleGround() || GetTeam() != reporter->GetTeam())
+ return;
+
+ // check if player has 'Idle' or 'Inactive' debuff
+ if(m_bgAfkReporter.find(reporter->GetGUIDLow())==m_bgAfkReporter.end() && !HasAura(43680,0) && !HasAura(43681,0) && reporter->CanReportAfkDueToLimit())
+ {
+ m_bgAfkReporter.insert(reporter->GetGUIDLow());
+ // 3 players have to complain to apply debuff
+ if(m_bgAfkReporter.size() >= 3)
+ {
+ // cast 'Idle' spell
+ CastSpell(this, 43680, true);
+ m_bgAfkReporter.clear();
+ }
+ }
+}
+
+bool Player::IsVisibleInGridForPlayer( Player* pl ) const
+{
+ // gamemaster in GM mode see all, including ghosts
+ if(pl->isGameMaster() && GetSession()->GetSecurity() <= pl->GetSession()->GetSecurity())
+ return true;
+
+ // It seems in battleground everyone sees everyone, except the enemy-faction ghosts
+ if (InBattleGround())
+ {
+ if (!(isAlive() || m_deathTimer > 0) && !IsFriendlyTo(pl) )
+ return false;
+ return true;
+ }
+
+ // Live player see live player or dead player with not realized corpse
+ if(pl->isAlive() || pl->m_deathTimer > 0)
+ {
+ return isAlive() || m_deathTimer > 0;
+ }
+
+ // Ghost see other friendly ghosts, that's for sure
+ if(!(isAlive() || m_deathTimer > 0) && IsFriendlyTo(pl))
+ return true;
+
+ // Dead player see live players near own corpse
+ if(isAlive())
+ {
+ Corpse *corpse = pl->GetCorpse();
+ if(corpse)
+ {
+ // 20 - aggro distance for same level, 25 - max additional distance if player level less that creature level
+ if(corpse->IsWithinDistInMap(this,(20+25)*sWorld.getRate(RATE_CREATURE_AGGRO)))
+ return true;
+ }
+ }
+
+ // and not see any other
+ return false;
+}
+
+bool Player::IsVisibleGloballyFor( Player* u ) const
+{
+ if(!u)
+ return false;
+
+ // Always can see self
+ if (u==this)
+ return true;
+
+ // Visible units, always are visible for all players
+ if (GetVisibility() == VISIBILITY_ON)
+ return true;
+
+ // GMs are visible for higher gms (or players are visible for gms)
+ if (u->GetSession()->GetSecurity() > SEC_PLAYER)
+ return GetSession()->GetSecurity() <= u->GetSession()->GetSecurity();
+
+ // non faction visibility non-breakable for non-GMs
+ if (GetVisibility() == VISIBILITY_OFF)
+ return false;
+
+ // non-gm stealth/invisibility not hide from global player lists
+ return true;
+}
+
+void Player::UpdateVisibilityOf(WorldObject* target)
+{
+ if(HaveAtClient(target))
+ {
+ if(!target->isVisibleForInState(this,true))
+ {
+ target->DestroyForPlayer(this);
+ m_clientGUIDs.erase(target->GetGUID());
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
+ sLog.outDebug("Object %u (Type: %u) out of range for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),GetGUIDLow(),GetDistance(target));
+ #endif
+ }
+ }
+ else
+ {
+ if(target->isVisibleForInState(this,false))
+ {
+ target->SendUpdateToPlayer(this);
+ if(target->GetTypeId()!=TYPEID_GAMEOBJECT||!((GameObject*)target)->IsTransport())
+ m_clientGUIDs.insert(target->GetGUID());
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
+ sLog.outDebug("Object %u (Type: %u) is visible now for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),GetGUIDLow(),GetDistance(target));
+ #endif
+
+ // target aura duration for caster show only if target exist at caster client
+ // send data at target visibility change (adding to client)
+ if(target!=this && target->isType(TYPEMASK_UNIT))
+ SendAuraDurationsForTarget((Unit*)target);
+
+ if(target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->isAlive())
+ ((Creature*)target)->SendMonsterMoveWithSpeedToCurrentDestination(this);
+ }
+ }
+}
+
+template<class T>
+inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, T* target)
+{
+ s64.insert(target->GetGUID());
+}
+
+template<>
+inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, GameObject* target)
+{
+ if(!target->IsTransport())
+ s64.insert(target->GetGUID());
+}
+
+template<class T>
+void Player::UpdateVisibilityOf(T* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow)
+{
+ if(HaveAtClient(target))
+ {
+ if(!target->isVisibleForInState(this,true))
+ {
+ target->BuildOutOfRangeUpdateBlock(&data);
+ m_clientGUIDs.erase(target->GetGUID());
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
+ sLog.outDebug("Object %u (Type: %u, Entry: %u) is out of range for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),target->GetEntry(),GetGUIDLow(),GetDistance(target));
+ #endif
+ }
+ }
+ else
+ {
+ if(target->isVisibleForInState(this,false))
+ {
+ visibleNow.insert(target);
+ target->BuildUpdate(data_updates);
+ target->BuildCreateUpdateBlockForPlayer(&data, this);
+ UpdateVisibilityOf_helper(m_clientGUIDs,target);
+
+ #ifdef MANGOS_DEBUG
+ if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0)
+ sLog.outDebug("Object %u (Type: %u, Entry: %u) is visible now for player %u. Distance = %f",target->GetGUIDLow(),target->GetTypeId(),target->GetEntry(),GetGUIDLow(),GetDistance(target));
+ #endif
+ }
+ }
+}
+
+template void Player::UpdateVisibilityOf(Player* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+template void Player::UpdateVisibilityOf(Creature* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+template void Player::UpdateVisibilityOf(Corpse* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+template void Player::UpdateVisibilityOf(GameObject* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+template void Player::UpdateVisibilityOf(DynamicObject* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+
+void Player::InitPrimaryProffesions()
+{
+ SetFreePrimaryProffesions(sWorld.getConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL));
+}
+
+void Player::SendComboPoints()
+{
+ Unit *combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget);
+ if (combotarget)
+ {
+ WorldPacket data(SMSG_UPDATE_COMBO_POINTS, combotarget->GetPackGUID().size()+1);
+ data.append(combotarget->GetPackGUID());
+ data << uint8(m_comboPoints);
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::AddComboPoints(Unit* target, int8 count)
+{
+ if(!count)
+ return;
+
+ // without combo points lost (duration checked in aura)
+ RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS);
+
+ if(target->GetGUID() == m_comboTarget)
+ {
+ m_comboPoints += count;
+ }
+ else
+ {
+ if(m_comboTarget)
+ if(Unit* target = ObjectAccessor::GetUnit(*this,m_comboTarget))
+ target->RemoveComboPointHolder(GetGUIDLow());
+
+ m_comboTarget = target->GetGUID();
+ m_comboPoints = count;
+
+ target->AddComboPointHolder(GetGUIDLow());
+ }
+
+ if (m_comboPoints > 5) m_comboPoints = 5;
+ if (m_comboPoints < 0) m_comboPoints = 0;
+
+ SendComboPoints();
+}
+
+void Player::ClearComboPoints()
+{
+ if(!m_comboTarget)
+ return;
+
+ // without combopoints lost (duration checked in aura)
+ RemoveSpellsCausingAura(SPELL_AURA_RETAIN_COMBO_POINTS);
+
+ m_comboPoints = 0;
+
+ SendComboPoints();
+
+ if(Unit* target = ObjectAccessor::GetUnit(*this,m_comboTarget))
+ target->RemoveComboPointHolder(GetGUIDLow());
+
+ m_comboTarget = 0;
+}
+
+void Player::SetGroup(Group *group, int8 subgroup)
+{
+ if(group == NULL) m_group.unlink();
+ else
+ {
+ // never use SetGroup without a subgroup unless you specify NULL for group
+ assert(subgroup >= 0);
+ m_group.link(group, this);
+ m_group.setSubGroup((uint8)subgroup);
+ }
+}
+
+void Player::SendInitialPacketsBeforeAddToMap()
+{
+ WorldPacket data(SMSG_SET_REST_START, 4);
+ data << uint32(0); // unknown, may be rest state time or expirience
+ GetSession()->SendPacket(&data);
+
+ // Homebind
+ data.Initialize(SMSG_BINDPOINTUPDATE, 5*4);
+ data << m_homebindX << m_homebindY << m_homebindZ;
+ data << (uint32) m_homebindMapId;
+ data << (uint32) m_homebindZoneId;
+ GetSession()->SendPacket(&data);
+
+ // SMSG_SET_PROFICIENCY
+ // SMSG_UPDATE_AURA_DURATION
+
+ // tutorial stuff
+ data.Initialize(SMSG_TUTORIAL_FLAGS, 8*4);
+ for (int i = 0; i < 8; ++i)
+ data << uint32( GetTutorialInt(i) );
+ GetSession()->SendPacket(&data);
+
+ SendInitialSpells();
+
+ data.Initialize(SMSG_SEND_UNLEARN_SPELLS, 4);
+ data << uint32(0); // count, for(count) uint32;
+ GetSession()->SendPacket(&data);
+
+ SendInitialActionButtons();
+ SendInitialReputations();
+ UpdateZone(GetZoneId());
+ SendInitWorldStates();
+
+ // SMSG_SET_AURA_SINGLE
+
+ data.Initialize(SMSG_LOGIN_SETTIMESPEED, 8);
+ data << uint32(secsToTimeBitFields(sWorld.GetGameTime()));
+ data << (float)0.01666667f; // game speed
+ GetSession()->SendPacket( &data );
+
+ // set fly flag if in fly form or taxi flight to prevent visually drop at ground in showup moment
+ if(HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) || isInFlight())
+ AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+}
+
+void Player::SendInitialPacketsAfterAddToMap()
+{
+ CastSpell(this, 836, true); // LOGINEFFECT
+
+ // set some aura effects that send packet to player client after add player to map
+ // SendMessageToSet not send it to player not it map, only for aura that not changed anything at re-apply
+ // same auras state lost at far teleport, send it one more time in this case also
+ static const AuraType auratypes[] =
+ {
+ SPELL_AURA_MOD_FEAR, SPELL_AURA_TRANSFORM, SPELL_AURA_WATER_WALK,
+ SPELL_AURA_FEATHER_FALL, SPELL_AURA_HOVER, SPELL_AURA_SAFE_FALL,
+ SPELL_AURA_FLY, SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED, SPELL_AURA_NONE
+ };
+ for(AuraType const* itr = &auratypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr)
+ {
+ Unit::AuraList const& auraList = GetAurasByType(*itr);
+ if(!auraList.empty())
+ auraList.front()->ApplyModifier(true,true);
+ }
+
+ if(HasAuraType(SPELL_AURA_MOD_STUN))
+ SetMovement(MOVE_ROOT);
+
+ // manual send package (have code in ApplyModifier(true,true); that don't must be re-applied.
+ if(HasAuraType(SPELL_AURA_MOD_ROOT))
+ {
+ WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10);
+ data.append(GetPackGUID());
+ data << (uint32)2;
+ SendMessageToSet(&data,true);
+ }
+
+ SendEnchantmentDurations(); // must be after add to map
+ SendItemDurations(); // must be after add to map
+}
+
+void Player::SendUpdateToOutOfRangeGroupMembers()
+{
+ if (m_groupUpdateMask == GROUP_UPDATE_FLAG_NONE)
+ return;
+ if(Group* group = GetGroup())
+ group->UpdatePlayerOutOfRange(this);
+
+ m_groupUpdateMask = GROUP_UPDATE_FLAG_NONE;
+ m_auraUpdateMask = 0;
+ if(Pet *pet = GetPet())
+ pet->ResetAuraUpdateMask();
+}
+
+void Player::SendTransferAborted(uint32 mapid, uint16 reason)
+{
+ WorldPacket data(SMSG_TRANSFER_ABORTED, 4+2);
+ data << uint32(mapid);
+ data << uint16(reason); // transfer abort reason
+ GetSession()->SendPacket(&data);
+}
+
+void Player::SendInstanceResetWarning(uint32 mapid, uint32 time)
+{
+ // type of warning, based on the time remaining until reset
+ uint32 type;
+ if(time > 3600)
+ type = RAID_INSTANCE_WELCOME;
+ else if(time > 900 && time <= 3600)
+ type = RAID_INSTANCE_WARNING_HOURS;
+ else if(time > 300 && time <= 900)
+ type = RAID_INSTANCE_WARNING_MIN;
+ else
+ type = RAID_INSTANCE_WARNING_MIN_SOON;
+ WorldPacket data(SMSG_RAID_INSTANCE_MESSAGE, 4+4+4);
+ data << uint32(type);
+ data << uint32(mapid);
+ data << uint32(time);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::ApplyEquipCooldown( Item * pItem )
+{
+ for(int i = 0; i <5; ++i)
+ {
+ _Spell const& spellData = pItem->GetProto()->Spells[i];
+
+ // no spell
+ if( !spellData.SpellId )
+ continue;
+
+ // wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown)
+ if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE )
+ continue;
+
+ AddSpellCooldown(spellData.SpellId, pItem->GetEntry(), time(NULL) + 30);
+
+ WorldPacket data(SMSG_ITEM_COOLDOWN, 12);
+ data << pItem->GetGUID();
+ data << uint32(spellData.SpellId);
+ GetSession()->SendPacket(&data);
+ }
+}
+
+void Player::resetSpells()
+{
+ // not need after this call
+ if(HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
+ {
+ m_atLoginFlags = m_atLoginFlags & ~AT_LOGIN_RESET_SPELLS;
+ CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login & ~ %u WHERE guid ='%u'", uint32(AT_LOGIN_RESET_SPELLS), GetGUIDLow());
+ }
+
+ // make full copy of map (spells removed and marked as deleted at another spell remove
+ // and we can't use original map for safe iterative with visit each spell at loop end
+ PlayerSpellMap smap = GetSpellMap();
+
+ for(PlayerSpellMap::const_iterator iter = smap.begin();iter != smap.end(); ++iter)
+ removeSpell(iter->first); // only iter->first can be accessed, object by iter->second can be deleted already
+
+ learnDefaultSpells();
+ learnQuestRewardedSpells();
+}
+
+void Player::learnDefaultSpells(bool loading)
+{
+ // learn default race/class spells
+ PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(),getClass());
+ std::list<CreateSpellPair>::const_iterator spell_itr;
+ for (spell_itr = info->spell.begin(); spell_itr!=info->spell.end(); ++spell_itr)
+ {
+ uint16 tspell = spell_itr->first;
+ if (tspell)
+ {
+ sLog.outDebug("PLAYER: Adding initial spell, id = %u",tspell);
+ if(loading || !spell_itr->second) // not care about passive spells or loading case
+ addSpell(tspell,spell_itr->second);
+ else // but send in normal spell in game learn case
+ learnSpell(tspell);
+ }
+ }
+}
+
+void Player::learnQuestRewardedSpells(Quest const* quest)
+{
+ uint32 spell_id = quest->GetRewSpellCast();
+
+ // skip quests without rewarded spell
+ if( !spell_id )
+ return;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+ if(!spellInfo)
+ return;
+
+ // check learned spells state
+ bool found = false;
+ for(int i=0; i < 3; ++i)
+ {
+ if(spellInfo->Effect[i] == SPELL_EFFECT_LEARN_SPELL && !HasSpell(spellInfo->EffectTriggerSpell[i]))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ // skip quests with not teaching spell or already known spell
+ if(!found)
+ return;
+
+ // prevent learn non first rank unknown profession and second specialization for same profession)
+ uint32 learned_0 = spellInfo->EffectTriggerSpell[0];
+ if( spellmgr.GetSpellRank(learned_0) > 1 && !HasSpell(learned_0) )
+ {
+ // not have first rank learned (unlearned prof?)
+ uint32 first_spell = spellmgr.GetFirstSpellInChain(learned_0);
+ if( !HasSpell(first_spell) )
+ return;
+
+ SpellEntry const *learnedInfo = sSpellStore.LookupEntry(learned_0);
+ if(!learnedInfo)
+ return;
+
+ // specialization
+ if(learnedInfo->Effect[0]==SPELL_EFFECT_TRADE_SKILL && learnedInfo->Effect[1]==0)
+ {
+ // search other specialization for same prof
+ for(PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PLAYERSPELL_REMOVED || itr->first==learned_0)
+ continue;
+
+ SpellEntry const *itrInfo = sSpellStore.LookupEntry(itr->first);
+ if(!itrInfo)
+ return;
+
+ // compare only specializations
+ if(itrInfo->Effect[0]!=SPELL_EFFECT_TRADE_SKILL || itrInfo->Effect[1]!=0)
+ continue;
+
+ // compare same chain spells
+ if(spellmgr.GetFirstSpellInChain(itr->first) != first_spell)
+ continue;
+
+ // now we have 2 specialization, learn possible only if found is lesser specialization rank
+ if(!spellmgr.IsHighRankOfSpell(learned_0,itr->first))
+ return;
+ }
+ }
+ }
+
+ CastSpell( this, spell_id, true);
+}
+
+void Player::learnQuestRewardedSpells()
+{
+ // learn spells received from quest completing
+ for(QuestStatusMap::const_iterator itr = mQuestStatus.begin(); itr != mQuestStatus.end(); ++itr)
+ {
+ // skip no rewarded quests
+ if(!itr->second.m_rewarded)
+ continue;
+
+ Quest const* quest = objmgr.GetQuestTemplate(itr->first);
+ if( !quest )
+ continue;
+
+ learnQuestRewardedSpells(quest);
+ }
+}
+
+void Player::learnSkillRewardedSpells(uint32 skill_id )
+{
+ uint32 raceMask = getRaceMask();
+ uint32 classMask = getClassMask();
+ for (uint32 j=0; j<sSkillLineAbilityStore.GetNumRows(); ++j)
+ {
+ SkillLineAbilityEntry const *pAbility = sSkillLineAbilityStore.LookupEntry(j);
+ if (!pAbility || pAbility->skillId!=skill_id || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
+ continue;
+ // Check race if set
+ if (pAbility->racemask && !(pAbility->racemask & raceMask))
+ continue;
+ // Check class if set
+ if (pAbility->classmask && !(pAbility->classmask & classMask))
+ continue;
+
+ if (SpellEntry const* spellentry = sSpellStore.LookupEntry(pAbility->spellId))
+ {
+ // Ok need learn spell
+ learnSpell(pAbility->spellId);
+ }
+ }
+}
+
+void Player::learnSkillRewardedSpells()
+{
+ for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
+ {
+ if(!GetUInt32Value(PLAYER_SKILL_INDEX(i)))
+ continue;
+
+ uint32 pskill = GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF;
+
+ learnSkillRewardedSpells(pskill);
+ }
+}
+
+void Player::SendAuraDurationsForTarget(Unit* target)
+{
+ for(Unit::AuraMap::const_iterator itr = target->GetAuras().begin(); itr != target->GetAuras().end(); ++itr)
+ {
+ Aura* aura = itr->second;
+ if(aura->GetAuraSlot() >= MAX_AURAS || aura->IsPassive() || aura->GetCasterGUID()!=GetGUID())
+ continue;
+
+ aura->SendAuraDurationForCaster(this);
+ }
+}
+
+void Player::SetDailyQuestStatus( uint32 quest_id )
+{
+ for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ {
+ if(!GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx))
+ {
+ SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,quest_id);
+ m_lastDailyQuestTime = time(NULL); // last daily quest time
+ m_DailyQuestChanged = true;
+ break;
+ }
+ }
+}
+
+void Player::ResetDailyQuestStatus()
+{
+ for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
+ SetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx,0);
+
+ // DB data deleted in caller
+ m_DailyQuestChanged = false;
+ m_lastDailyQuestTime = 0;
+}
+
+BattleGround* Player::GetBattleGround() const
+{
+ if(GetBattleGroundId()==0)
+ return NULL;
+
+ return sBattleGroundMgr.GetBattleGround(GetBattleGroundId());
+}
+
+bool Player::InArena() const
+{
+ BattleGround *bg = GetBattleGround();
+ if(!bg || !bg->isArena())
+ return false;
+
+ return true;
+}
+
+bool Player::GetBGAccessByLevel(uint32 bgTypeId) const
+{
+ // get a template bg instead of running one
+ BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
+ if(!bg)
+ return false;
+
+ if(getLevel() < bg->GetMinLevel() || getLevel() > bg->GetMaxLevel())
+ return false;
+
+ return true;
+}
+
+uint32 Player::GetMinLevelForBattleGroundQueueId(uint32 queue_id)
+{
+ if(queue_id < 1)
+ return 0;
+
+ if(queue_id >=6)
+ queue_id = 6;
+
+ return 10*(queue_id+1);
+}
+
+uint32 Player::GetMaxLevelForBattleGroundQueueId(uint32 queue_id)
+{
+ if(queue_id >=6)
+ return 255; // hardcoded max level
+
+ return 10*(queue_id+2)-1;
+}
+
+uint32 Player::GetBattleGroundQueueIdFromLevel() const
+{
+ uint32 level = getLevel();
+ if(level <= 19)
+ return 0;
+ else if (level > 69)
+ return 6;
+ else
+ return level/10 - 1; // 20..29 -> 1, 30-39 -> 2, ...
+}
+
+float Player::GetReputationPriceDiscount( Creature const* pCreature ) const
+{
+ FactionTemplateEntry const* vendor_faction = pCreature->getFactionTemplateEntry();
+ if(!vendor_faction)
+ return 1.0f;
+
+ ReputationRank rank = GetReputationRank(vendor_faction->faction);
+ if(rank <= REP_NEUTRAL)
+ return 1.0f;
+
+ return 1.0f - 0.05f* (rank - REP_NEUTRAL);
+}
+
+bool Player::IsSpellFitByClassAndRace( uint32 spell_id ) const
+{
+ uint32 racemask = getRaceMask();
+ uint32 classmask = getClassMask();
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spell_id);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spell_id);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ // skip wrong race skills
+ if( _spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0)
+ return false;
+
+ // skip wrong class skills
+ if( _spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0)
+ return false;
+ }
+ return true;
+}
+
+bool Player::HasQuestForGO(int32 GOId)
+{
+ for( QuestStatusMap::iterator i = mQuestStatus.begin( ); i != mQuestStatus.end( ); ++i )
+ {
+ QuestStatusData qs=i->second;
+ if (qs.m_status == QUEST_STATUS_INCOMPLETE)
+ {
+ Quest const* qinfo = objmgr.GetQuestTemplate(i->first);
+ if(!qinfo)
+ continue;
+
+ if(GetGroup() && GetGroup()->isRaidGroup() && qinfo->GetType() != QUEST_TYPE_RAID)
+ continue;
+
+ for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
+ {
+ if (qinfo->ReqCreatureOrGOId[j]>=0) //skip non GO case
+ continue;
+
+ if((-1)*GOId == qinfo->ReqCreatureOrGOId[j] && qs.m_creatureOrGOcount[j] < qinfo->ReqCreatureOrGOCount[j])
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void Player::UpdateForQuestsGO()
+{
+ if(m_clientGUIDs.empty())
+ return;
+
+ UpdateData udata;
+ WorldPacket packet;
+ for(ClientGUIDs::iterator itr=m_clientGUIDs.begin(); itr!=m_clientGUIDs.end(); ++itr)
+ {
+ if(IS_GAMEOBJECT_GUID(*itr))
+ {
+ GameObject *obj = HashMapHolder<GameObject>::Find(*itr);
+ if(obj)
+ obj->BuildValuesUpdateBlockForPlayer(&udata,this);
+ }
+ }
+ udata.BuildPacket(&packet);
+ GetSession()->SendPacket(&packet);
+}
+
+void Player::SummonIfPossible(bool agree)
+{
+ if(!agree)
+ {
+ m_summon_expire = 0;
+ return;
+ }
+
+ // expire and auto declined
+ if(m_summon_expire < time(NULL))
+ return;
+
+ // stop taxi flight at summon
+ if(isInFlight())
+ {
+ GetMotionMaster()->MovementExpired();
+ m_taxi.ClearTaxiDestinations();
+ }
+
+ // drop flag at summon
+ if(BattleGround *bg = GetBattleGround())
+ bg->EventPlayerDroppedFlag(this);
+
+ m_summon_expire = 0;
+
+ TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z,GetOrientation());
+}
+
+void Player::RemoveItemDurations( Item *item )
+{
+ for(ItemDurationList::iterator itr = m_itemDuration.begin();itr != m_itemDuration.end(); ++itr)
+ {
+ if(*itr==item)
+ {
+ m_itemDuration.erase(itr);
+ break;
+ }
+ }
+}
+
+void Player::AddItemDurations( Item *item )
+{
+ if(item->GetUInt32Value(ITEM_FIELD_DURATION))
+ {
+ m_itemDuration.push_back(item);
+ item->SendTimeUpdate(this);
+ }
+}
+
+void Player::AutoUnequipOffhandIfNeed()
+{
+ Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND );
+ if(!offItem)
+ return;
+
+ Item *mainItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND );
+
+ if(!mainItem || mainItem->GetProto()->InventoryType != INVTYPE_2HWEAPON)
+ return;
+
+ ItemPosCountVec off_dest;
+ uint8 off_msg = CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false );
+ if( off_msg == EQUIP_ERR_OK )
+ {
+ RemoveItem(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
+ StoreItem( off_dest, offItem, true );
+ }
+ else
+ {
+ sLog.outError("Player::EquipItem: Can's store offhand item at 2hand item equip for player (GUID: %u).",GetGUIDLow());
+ }
+}
+
+bool Player::HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem)
+{
+ if(spellInfo->EquippedItemClass < 0)
+ return true;
+
+ // scan other equipped items for same requirements (mostly 2 daggers/etc)
+ // for optimize check 2 used cases only
+ switch(spellInfo->EquippedItemClass)
+ {
+ case ITEM_CLASS_WEAPON:
+ {
+ for(int i= EQUIPMENT_SLOT_MAINHAND; i < EQUIPMENT_SLOT_TABARD; ++i)
+ if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
+ return true;
+ break;
+ }
+ case ITEM_CLASS_ARMOR:
+ {
+ // tabard not have dependent spells
+ for(int i= EQUIPMENT_SLOT_START; i< EQUIPMENT_SLOT_MAINHAND; ++i)
+ if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
+ if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
+ return true;
+
+ // shields can be equipped to offhand slot
+ if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
+ if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
+ return true;
+
+ // ranged slot can have some armor subclasses
+ if(Item *item = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
+ if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
+ return true;
+
+ break;
+ }
+ default:
+ sLog.outError("HasItemFitToSpellReqirements: Not handeled spell reqirement for item class %u",spellInfo->EquippedItemClass);
+ break;
+ }
+
+ return false;
+}
+
+void Player::RemoveItemDependentAurasAndCasts( Item * pItem )
+{
+ AuraMap& auras = GetAuras();
+ for(AuraMap::iterator itr = auras.begin(); itr != auras.end(); )
+ {
+ Aura* aura = itr->second;
+
+ // skip passive (passive item dependent spells work in another way) and not self applied auras
+ SpellEntry const* spellInfo = aura->GetSpellProto();
+ if(aura->IsPassive() || aura->GetCasterGUID()!=GetGUID())
+ {
+ ++itr;
+ continue;
+ }
+
+ // skip if not item dependent or have alternative item
+ if(HasItemFitToSpellReqirements(spellInfo,pItem))
+ {
+ ++itr;
+ continue;
+ }
+
+ // no alt item, remove aura, restart check
+ RemoveAurasDueToSpell(aura->GetId());
+ itr = auras.begin();
+ }
+
+ // currently casted spells can be dependent from item
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ {
+ if( m_currentSpells[i] && m_currentSpells[i]->getState()!=SPELL_STATE_DELAYED &&
+ !HasItemFitToSpellReqirements(m_currentSpells[i]->m_spellInfo,pItem) )
+ InterruptSpell(i);
+ }
+}
+
+uint32 Player::GetResurrectionSpellId()
+{
+ // search priceless resurrection possabilities
+ uint32 prio = 0;
+ uint32 spell_id = 0;
+ AuraList const& dummyAuras = GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr)
+ {
+ // Soulstone Resurrection // prio: 3 (max, non death persistent)
+ if( prio < 2 && (*itr)->GetSpellProto()->SpellVisual == 99 && (*itr)->GetSpellProto()->SpellIconID == 92 )
+ {
+ switch((*itr)->GetId())
+ {
+ case 20707: spell_id = 3026; break; // rank 1
+ case 20762: spell_id = 20758; break; // rank 2
+ case 20763: spell_id = 20759; break; // rank 3
+ case 20764: spell_id = 20760; break; // rank 4
+ case 20765: spell_id = 20761; break; // rank 5
+ case 27239: spell_id = 27240; break; // rank 6
+ default:
+ sLog.outError("Unhandled spell %%u: S.Resurrection",(*itr)->GetId());
+ continue;
+ }
+
+ prio = 3;
+ }
+ // Twisting Nether // prio: 2 (max)
+ else if((*itr)->GetId()==23701 && roll_chance_i(10))
+ {
+ prio = 2;
+ spell_id = 23700;
+ }
+ }
+
+ // Reincarnation (passive spell) // prio: 1
+ if(prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169) && HasItemCount(17030,1))
+ spell_id = 21169;
+
+ return spell_id;
+}
+
+bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim)
+{
+ bool PvP = pVictim->isCharmedOwnedByPlayerOrPlayer();
+
+ // prepare data for near group iteration (PvP and !PvP cases)
+ uint32 xp = 0;
+ bool honored_kill = false;
+
+ if(Group *pGroup = GetGroup())
+ {
+ uint32 count = 0;
+ uint32 sum_level = 0;
+ Player* member_with_max_level = NULL;
+
+ pGroup->GetDataForXPAtKill(pVictim,count,sum_level,member_with_max_level);
+
+ if(member_with_max_level)
+ {
+ xp = PvP ? 0 : MaNGOS::XP::Gain(member_with_max_level, pVictim);
+
+ // skip in check PvP case (for speed, not used)
+ bool is_raid = PvP ? false : sMapStore.LookupEntry(GetMapId())->IsRaid() && pGroup->isRaidGroup();
+ bool is_dungeon = PvP ? false : sMapStore.LookupEntry(GetMapId())->IsDungeon();
+ float group_rate = MaNGOS::XP::xp_in_group_rate(count,is_raid);
+
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* pGroupGuy = itr->getSource();
+ if(!pGroupGuy)
+ continue;
+
+ if(!pGroupGuy->IsAtGroupRewardDistance(pVictim))
+ continue; // member (alive or dead) or his corpse at req. distance
+
+ // honor can be in PvP and !PvP (racial leader) cases (for alive)
+ if(pGroupGuy->isAlive() && pGroupGuy->RewardHonor(pVictim,count, -1, true) && pGroupGuy==this)
+ honored_kill = true;
+
+ // xp and reputation only in !PvP case
+ if(!PvP)
+ {
+ float rate = group_rate * float(pGroupGuy->getLevel()) / sum_level;
+
+ // if is in dungeon then all receive full reputation at kill
+ // rewarded any alive/dead/near_corpse group member
+ pGroupGuy->RewardReputation(pVictim,is_dungeon ? 1.0f : rate);
+
+ // XP updated only for alive group member
+ if(pGroupGuy->isAlive())
+ {
+ uint32 itr_xp = uint32(xp*rate);
+
+ pGroupGuy->GiveXP(itr_xp, pVictim);
+ if(Pet* pet = pGroupGuy->GetPet())
+ pet->GivePetXP(itr_xp/2);
+ }
+
+ // quest objectives updated only for alive group member or dead but with not released body
+ if(pGroupGuy->isAlive()|| !pGroupGuy->GetCorpse())
+ {
+ // normal creature (not pet/etc) can be only in !PvP case
+ if(pVictim->GetTypeId()==TYPEID_UNIT)
+ pGroupGuy->KilledMonster(pVictim->GetEntry(), pVictim->GetGUID());
+ }
+ }
+ }
+ }
+ }
+ else // if (!pGroup)
+ {
+ xp = PvP ? 0 : MaNGOS::XP::Gain(this, pVictim);
+
+ // honor can be in PvP and !PvP (racial leader) cases
+ if(RewardHonor(pVictim,1, -1, true))
+ honored_kill = true;
+
+ // xp and reputation only in !PvP case
+ if(!PvP)
+ {
+ RewardReputation(pVictim,1);
+ GiveXP(xp, pVictim);
+
+ if(Pet* pet = GetPet())
+ pet->GivePetXP(xp);
+
+ // normal creature (not pet/etc) can be only in !PvP case
+ if(pVictim->GetTypeId()==TYPEID_UNIT)
+ KilledMonster(pVictim->GetEntry(),pVictim->GetGUID());
+ }
+ }
+ return xp || honored_kill;
+}
+
+bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const
+{
+ if(pRewardSource->GetDistance(this) <= sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE))
+ return true;
+
+ if(isAlive())
+ return false;
+
+ Corpse* corpse = GetCorpse();
+ if(!corpse)
+ return false;
+
+ return pRewardSource->GetDistance(corpse) <= sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE);
+}
+
+uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
+{
+ Item* item = GetWeaponForAttack(attType,true);
+
+ // unarmmed only with base attack
+ if(attType != BASE_ATTACK && !item)
+ return 0;
+
+ // weapon skill or (unarmed for base attack)
+ uint32 skill = item ? item->GetSkill() : SKILL_UNARMED;
+ return GetBaseSkillValue(skill);
+}
+
+void Player::ResurectUsingRequestData()
+{
+ ResurrectPlayer(0.0f,false);
+
+ if(GetMaxHealth() > m_resurrectHealth)
+ SetHealth( m_resurrectHealth );
+ else
+ SetHealth( GetMaxHealth() );
+
+ if(GetMaxPower(POWER_MANA) > m_resurrectMana)
+ SetPower(POWER_MANA, m_resurrectMana );
+ else
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA) );
+
+ SetPower(POWER_RAGE, 0 );
+
+ SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY) );
+
+ SpawnCorpseBones();
+
+ TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation());
+}
+
+void Player::SetClientControl(Unit* target, uint8 allowMove)
+{
+ WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size()+1);
+ data.append(target->GetPackGUID());
+ data << uint8(allowMove);
+ GetSession()->SendPacket(&data);
+}
+
+void Player::UpdateZoneDependentAuras( uint32 newZone )
+{
+ // remove new continent flight forms
+ if( !isGameMaster() &&
+ GetVirtualMapForMapAndZone(GetMapId(),newZone) != 530)
+ {
+ RemoveSpellsCausingAura(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED);
+ RemoveSpellsCausingAura(SPELL_AURA_FLY);
+ }
+
+ // Some spells applied at enter into zone (with subzones)
+ // Human Illusion
+ // NOTE: these are removed by RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP);
+ if ( newZone == 2367 ) // Old Hillsbrad Foothills
+ {
+ uint32 spellid = 0;
+ // all horde races
+ if( GetTeam() == HORDE )
+ spellid = getGender() == GENDER_FEMALE ? 35481 : 35480;
+ // and some alliance races
+ else if( getRace() == RACE_NIGHTELF || getRace() == RACE_DRAENEI )
+ spellid = getGender() == GENDER_FEMALE ? 35483 : 35482;
+
+ if(spellid && !HasAura(spellid,0) )
+ CastSpell(this,spellid,true);
+ }
+}
+
+void Player::UpdateAreaDependentAuras( uint32 newArea )
+{
+ // remove auras from spells with area limitations
+ for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
+ {
+ // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date
+ if(!IsSpellAllowedInLocation(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea))
+ RemoveAura(iter);
+ else
+ ++iter;
+ }
+
+ // unmount if enter in this subzone
+ if( newArea == 35)
+ RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+ // Dragonmaw Illusion
+ else if( newArea == 3759 || newArea == 3966 || newArea == 3939 )
+ {
+ if( GetDummyAura(40214) )
+ {
+ if( !HasAura(40216,0) )
+ CastSpell(this,40216,true);
+ if( !HasAura(42016,0) )
+ CastSpell(this,42016,true);
+ }
+ }
+}
+
+uint32 Player::GetCorpseReclaimDelay(bool pvp) const
+{
+ if( pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
+ !pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
+ {
+ return copseReclaimDelay[0];
+ }
+
+ time_t now = time(NULL);
+ // 0..2 full period
+ uint32 count = (now < m_deathExpireTime) ? (m_deathExpireTime - now)/DEATH_EXPIRE_STEP : 0;
+ return copseReclaimDelay[count];
+}
+
+void Player::UpdateCorpseReclaimDelay()
+{
+ bool pvp = m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH;
+
+ if( pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
+ !pvp && !sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
+ return;
+
+ time_t now = time(NULL);
+ if(now < m_deathExpireTime)
+ {
+ // full and partly periods 1..3
+ uint32 count = (m_deathExpireTime - now)/DEATH_EXPIRE_STEP +1;
+ if(count < MAX_DEATH_COUNT)
+ m_deathExpireTime = now+(count+1)*DEATH_EXPIRE_STEP;
+ else
+ m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP;
+ }
+ else
+ m_deathExpireTime = now+DEATH_EXPIRE_STEP;
+}
+
+void Player::SendCorpseReclaimDelay(bool load)
+{
+ Corpse* corpse = GetCorpse();
+ if(!corpse)
+ return;
+
+ uint32 delay;
+ if(load)
+ {
+ if(corpse->GetGhostTime() > m_deathExpireTime)
+ return;
+
+ bool pvp = corpse->GetType()==CORPSE_RESURRECTABLE_PVP;
+
+ uint32 count;
+ if( pvp && sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
+ !pvp && sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
+ {
+ count = (m_deathExpireTime-corpse->GetGhostTime())/DEATH_EXPIRE_STEP;
+ if(count>=MAX_DEATH_COUNT)
+ count = MAX_DEATH_COUNT-1;
+ }
+ else
+ count=0;
+
+ time_t expected_time = corpse->GetGhostTime()+copseReclaimDelay[count];
+
+ time_t now = time(NULL);
+ if(now >= expected_time)
+ return;
+
+ delay = expected_time-now;
+ }
+ else
+ delay = GetCorpseReclaimDelay(corpse->GetType()==CORPSE_RESURRECTABLE_PVP);
+
+ //! corpse reclaim delay 30 * 1000ms or longer at often deaths
+ WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4);
+ data << uint32(delay*1000);
+ GetSession()->SendPacket( &data );
+}
+
+Player* Player::GetNextRandomRaidMember(float radius)
+{
+ Group *pGroup = GetGroup();
+ if(!pGroup)
+ return NULL;
+
+ std::vector<Player*> nearMembers;
+ nearMembers.reserve(pGroup->GetMembersCount());
+
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+
+ // IsHostileTo check duel and controlled by enemy
+ if( Target && Target != this && IsWithinDistInMap(Target, radius) &&
+ !Target->HasInvisibilityAura() && !IsHostileTo(Target) )
+ nearMembers.push_back(Target);
+ }
+
+ if (nearMembers.empty())
+ return NULL;
+
+ uint32 randTarget = urand(0,nearMembers.size()-1);
+ return nearMembers[randTarget];
+}
+
+void Player::UpdateUnderwaterState( Map* m, float x, float y, float z )
+{
+ float water_z = m->GetWaterLevel(x,y);
+ float height_z = m->GetHeight(x,y,z, false); // use .map base surface height
+ uint8 flag1 = m->GetTerrainType(x,y);
+
+ //!Underwater check, not in water if underground or above water level
+ if (height_z <= INVALID_HEIGHT || z < (height_z-2) || z > (water_z - 2) )
+ m_isunderwater &= 0x7A;
+ else if ((z < (water_z - 2)) && (flag1 & 0x01))
+ m_isunderwater |= 0x01;
+
+ //!in lava check, anywhere under lava level
+ if ((height_z <= INVALID_HEIGHT || z < (height_z - 0)) && (flag1 == 0x00) && IsInWater())
+ m_isunderwater |= 0x80;
+}
+
+void Player::SetCanParry( bool value )
+{
+ if(m_canParry==value)
+ return;
+
+ m_canParry = value;
+ UpdateParryPercentage();
+}
+
+void Player::SetCanBlock( bool value )
+{
+ if(m_canBlock==value)
+ return;
+
+ m_canBlock = value;
+ UpdateBlockPercentage();
+}
+
+bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const
+{
+ for(ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr)
+ if(itr->pos == this->pos)
+ return true;
+
+ return false;
+}
+
+bool Player::isAllowUseBattleGroundObject()
+{
+ return ( //InBattleGround() && // in battleground - not need, check in other cases
+ !IsMounted() && // not mounted
+ !HasStealthAura() && // not stealthed
+ !HasInvisibilityAura() && // not invisible
+ !HasAura(SPELL_RECENTLY_DROPPED_FLAG, 0) && // can't pickup
+ isAlive() // live player
+ );
+}
diff --git a/src/game/Player.h b/src/game/Player.h
index ea5f5e00d61..4779ce46baa 100644
--- a/src/game/Player.h
+++ b/src/game/Player.h
@@ -1,2336 +1,2336 @@
-/*
- * 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
- */
-
-#ifndef _PLAYER_H
-#define _PLAYER_H
-
-#include "Common.h"
-#include "ItemPrototype.h"
-#include "Unit.h"
-#include "Item.h"
-
-#include "Database/DatabaseEnv.h"
-#include "NPCHandler.h"
-#include "QuestDef.h"
-#include "Group.h"
-#include "Bag.h"
-#include "WorldSession.h"
-#include "Pet.h"
-#include "Util.h" // for Tokens typedef
-
-#include<string>
-#include<vector>
-
-struct Mail;
-class Channel;
-class DynamicObject;
-class Creature;
-class Pet;
-class PlayerMenu;
-class Transport;
-class UpdateMask;
-class PlayerSocial;
-
-typedef std::deque<Mail*> PlayerMails;
-
-#define PLAYER_MAX_SKILLS 127
-#define PLAYER_MAX_DAILY_QUESTS 25
-
-// Note: SPELLMOD_* values is aura types in fact
-enum SpellModType
-{
- SPELLMOD_FLAT = 107, // SPELL_AURA_ADD_FLAT_MODIFIER
- SPELLMOD_PCT = 108 // SPELL_AURA_ADD_PCT_MODIFIER
-};
-
-enum PlayerSpellState
-{
- PLAYERSPELL_UNCHANGED = 0,
- PLAYERSPELL_CHANGED = 1,
- PLAYERSPELL_NEW = 2,
- PLAYERSPELL_REMOVED = 3
-};
-
-struct PlayerSpell
-{
- uint16 slotId : 16;
- PlayerSpellState state : 8;
- bool active : 1;
- bool disabled : 1;
-};
-
-#define SPELL_WITHOUT_SLOT_ID uint16(-1)
-
-struct SpellModifier
-{
- SpellModOp op : 8;
- SpellModType type : 8;
- int16 charges : 16;
- int32 value;
- uint64 mask;
- uint32 spellId;
- uint32 effectId;
- Spell const* lastAffected;
-};
-
-typedef HM_NAMESPACE::hash_map<uint16, PlayerSpell*> PlayerSpellMap;
-typedef std::list<SpellModifier*> SpellModList;
-
-struct SpellCooldown
-{
- time_t end;
- uint16 itemid;
-};
-
-typedef std::map<uint32, SpellCooldown> SpellCooldowns;
-
-enum TrainerSpellState
-{
- TRAINER_SPELL_GREEN = 0,
- TRAINER_SPELL_RED = 1,
- TRAINER_SPELL_GRAY = 2
-};
-
-enum ActionButtonUpdateState
-{
- ACTIONBUTTON_UNCHANGED = 0,
- ACTIONBUTTON_CHANGED = 1,
- ACTIONBUTTON_NEW = 2,
- ACTIONBUTTON_DELETED = 3
-};
-
-struct ActionButton
-{
- ActionButton() : action(0), type(0), misc(0), uState( ACTIONBUTTON_NEW ) {}
- ActionButton(uint16 _action, uint8 _type, uint8 _misc) : action(_action), type(_type), misc(_misc), uState( ACTIONBUTTON_NEW ) {}
-
- uint16 action;
- uint8 type;
- uint8 misc;
- ActionButtonUpdateState uState;
-};
-
-enum ActionButtonType
-{
- ACTION_BUTTON_SPELL = 0,
- ACTION_BUTTON_MACRO = 64,
- ACTION_BUTTON_CMACRO= 65,
- ACTION_BUTTON_ITEM = 128
-};
-
-#define MAX_ACTION_BUTTONS 132 //checked in 2.3.0
-
-typedef std::map<uint8,ActionButton> ActionButtonList;
-
-typedef std::pair<uint16, uint8> CreateSpellPair;
-
-struct PlayerCreateInfoItem
-{
- PlayerCreateInfoItem(uint32 id, uint32 amount) : item_id(id), item_amount(amount) {}
-
- uint32 item_id;
- uint32 item_amount;
-};
-
-typedef std::list<PlayerCreateInfoItem> PlayerCreateInfoItems;
-
-struct PlayerClassLevelInfo
-{
- PlayerClassLevelInfo() : basehealth(0), basemana(0) {}
- uint16 basehealth;
- uint16 basemana;
-};
-
-struct PlayerClassInfo
-{
- PlayerClassInfo() : levelInfo(NULL) { }
-
- PlayerClassLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
-};
-
-struct PlayerLevelInfo
-{
- PlayerLevelInfo() { for(int i=0; i < MAX_STATS; ++i ) stats[i] = 0; }
-
- uint8 stats[MAX_STATS];
-};
-
-struct PlayerInfo
-{
- // existence checked by displayId != 0 // existence checked by displayId != 0
- PlayerInfo() : displayId_m(0),displayId_f(0),levelInfo(NULL)
- {
- }
-
- uint32 mapId;
- uint32 zoneId;
- float positionX;
- float positionY;
- float positionZ;
- uint16 displayId_m;
- uint16 displayId_f;
- PlayerCreateInfoItems item;
- std::list<CreateSpellPair> spell;
- std::list<uint16> action[4];
-
- PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
-};
-
-struct PvPInfo
-{
- PvPInfo() : inHostileArea(false), endTimer(0) {}
-
- bool inHostileArea;
- time_t endTimer;
-};
-
-struct DuelInfo
-{
- DuelInfo() : initiator(NULL), opponent(NULL), startTimer(0), startTime(0), outOfBound(0) {}
-
- Player *initiator;
- Player *opponent;
- time_t startTimer;
- time_t startTime;
- time_t outOfBound;
-};
-
-struct Areas
-{
- uint32 areaID;
- uint32 areaFlag;
- float x1;
- float x2;
- float y1;
- float y2;
-};
-
-enum FactionFlags
-{
- FACTION_FLAG_VISIBLE = 0x01, // makes visible in client (set or can be set at interaction with target of this faction)
- FACTION_FLAG_AT_WAR = 0x02, // enable AtWar-button in client. player controlled (except opposition team always war state), Flag only set on initial creation
- FACTION_FLAG_HIDDEN = 0x04, // hidden faction from reputation pane in client (player can gain reputation, but this update not sent to client)
- FACTION_FLAG_INVISIBLE_FORCED = 0x08, // always overwrite FACTION_FLAG_VISIBLE and hide faction in rep.list, used for hide opposite team factions
- FACTION_FLAG_PEACE_FORCED = 0x10, // always overwrite FACTION_FLAG_AT_WAR, used for prevent war with own team factions
- FACTION_FLAG_INACTIVE = 0x20, // player controlled, state stored in characters.data ( CMSG_SET_FACTION_INACTIVE )
- FACTION_FLAG_RIVAL = 0x40 // flag for the two competing outland factions
-};
-
-typedef uint32 RepListID;
-struct FactionState
-{
- uint32 ID;
- RepListID ReputationListID;
- uint32 Flags;
- int32 Standing;
- bool Changed;
-};
-
-typedef std::map<RepListID,FactionState> FactionStateList;
-
-typedef std::map<uint32,ReputationRank> ForcedReactions;
-
-typedef std::set<uint64> GuardianPetList;
-
-struct EnchantDuration
-{
- EnchantDuration() : item(NULL), slot(MAX_ENCHANTMENT_SLOT), leftduration(0) {};
- EnchantDuration(Item * _item, EnchantmentSlot _slot, uint32 _leftduration) : item(_item), slot(_slot), leftduration(_leftduration) { assert(item); };
-
- Item * item;
- EnchantmentSlot slot;
- uint32 leftduration;
-};
-
-typedef std::list<EnchantDuration> EnchantDurationList;
-typedef std::list<Item*> ItemDurationList;
-
-struct LookingForGroupSlot
-{
- LookingForGroupSlot() : entry(0), type(0) {}
- bool Empty() const { return !entry && !type; }
- void Clear() { entry = 0; type = 0; }
- void Set(uint32 _entry, uint32 _type ) { entry = _entry; type = _type; }
- bool Is(uint32 _entry, uint32 _type) const { return entry==_entry && type==_type; }
- bool canAutoJoin() const { return entry && (type == 1 || type == 5); }
-
- uint32 entry;
- uint32 type;
-};
-
-#define MAX_LOOKING_FOR_GROUP_SLOT 3
-
-struct LookingForGroup
-{
- LookingForGroup() {}
- bool HaveInSlot(LookingForGroupSlot const& slot) const { return HaveInSlot(slot.entry,slot.type); }
- bool HaveInSlot(uint32 _entry, uint32 _type) const
- {
- for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
- if(slots[i].Is(_entry,_type))
- return true;
- return false;
- }
-
- bool canAutoJoin() const
- {
- for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
- if(slots[i].canAutoJoin())
- return true;
- return false;
- }
-
- bool Empty() const
- {
- for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
- if(!slots[i].Empty())
- return false;
- return more.Empty();
- }
-
- LookingForGroupSlot slots[MAX_LOOKING_FOR_GROUP_SLOT];
- LookingForGroupSlot more;
- std::string comment;
-};
-
-enum PlayerMovementType
-{
- MOVE_ROOT = 1,
- MOVE_UNROOT = 2,
- MOVE_WATER_WALK = 3,
- MOVE_LAND_WALK = 4
-};
-
-enum DrunkenState
-{
- DRUNKEN_SOBER = 0,
- DRUNKEN_TIPSY = 1,
- DRUNKEN_DRUNK = 2,
- DRUNKEN_SMASHED = 3
-};
-
-enum PlayerStateType
-{
- /*
- PLAYER_STATE_DANCE
- PLAYER_STATE_SLEEP
- PLAYER_STATE_SIT
- PLAYER_STATE_STAND
- PLAYER_STATE_READYUNARMED
- PLAYER_STATE_WORK
- PLAYER_STATE_POINT(DNR)
- PLAYER_STATE_NONE // not used or just no state, just standing there?
- PLAYER_STATE_STUN
- PLAYER_STATE_DEAD
- PLAYER_STATE_KNEEL
- PLAYER_STATE_USESTANDING
- PLAYER_STATE_STUN_NOSHEATHE
- PLAYER_STATE_USESTANDING_NOSHEATHE
- PLAYER_STATE_WORK_NOSHEATHE
- PLAYER_STATE_SPELLPRECAST
- PLAYER_STATE_READYRIFLE
- PLAYER_STATE_WORK_NOSHEATHE_MINING
- PLAYER_STATE_WORK_NOSHEATHE_CHOPWOOD
- PLAYER_STATE_AT_EASE
- PLAYER_STATE_READY1H
- PLAYER_STATE_SPELLKNEELSTART
- PLAYER_STATE_SUBMERGED
- */
-
- PLAYER_STATE_NONE = 0,
- PLAYER_STATE_SIT = 1,
- PLAYER_STATE_SIT_CHAIR = 2,
- PLAYER_STATE_SLEEP = 3,
- PLAYER_STATE_SIT_LOW_CHAIR = 4,
- PLAYER_STATE_SIT_MEDIUM_CHAIR = 5,
- PLAYER_STATE_SIT_HIGH_CHAIR = 6,
- PLAYER_STATE_DEAD = 7,
- PLAYER_STATE_KNEEL = 8,
-
- PLAYER_STATE_FORM_ALL = 0x00FF0000,
-
- PLAYER_STATE_FLAG_ALWAYS_STAND = 0x01, // byte 4
- PLAYER_STATE_FLAG_CREEP = 0x02000000,
- PLAYER_STATE_FLAG_UNTRACKABLE = 0x04000000,
- PLAYER_STATE_FLAG_ALL = 0xFF000000,
-};
-
-enum PlayerFlags
-{
- PLAYER_FLAGS_GROUP_LEADER = 0x00000001,
- PLAYER_FLAGS_AFK = 0x00000002,
- PLAYER_FLAGS_DND = 0x00000004,
- PLAYER_FLAGS_GM = 0x00000008,
- PLAYER_FLAGS_GHOST = 0x00000010,
- PLAYER_FLAGS_RESTING = 0x00000020,
- PLAYER_FLAGS_FFA_PVP = 0x00000080,
- PLAYER_FLAGS_CONTESTED_PVP = 0x00000100, // Player has been involved in a PvP combat and will be attacked by contested guards
- PLAYER_FLAGS_IN_PVP = 0x00000200,
- PLAYER_FLAGS_HIDE_HELM = 0x00000400,
- PLAYER_FLAGS_HIDE_CLOAK = 0x00000800,
- PLAYER_FLAGS_UNK1 = 0x00001000, // played long time
- PLAYER_FLAGS_UNK2 = 0x00002000, // played too long time
- PLAYER_FLAGS_UNK3 = 0x00008000, // strange visual effect (2.0.1), looks like PLAYER_FLAGS_GHOST flag
- PLAYER_FLAGS_SANCTUARY = 0x00010000, // player entered sanctuary
- PLAYER_FLAGS_UNK4 = 0x00020000, // taxi benchmark mode (on/off) (2.0.1)
- PLAYER_UNK = 0x00040000, // 2.0.8...
-};
-
-// used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1<<bit_index) without (-1)
-// can't use enum for uint64 values
-#define PLAYER_TITLE_DISABLED 0x0000000000000000LL
-#define PLAYER_TITLE_NONE 0x0000000000000001LL
-#define PLAYER_TITLE_PRIVATE 0x0000000000000002LL // 1
-#define PLAYER_TITLE_CORPORAL 0x0000000000000004LL // 2
-#define PLAYER_TITLE_SERGEANT_A 0x0000000000000008LL // 3
-#define PLAYER_TITLE_MASTER_SERGEANT 0x0000000000000010LL // 4
-#define PLAYER_TITLE_SERGEANT_MAJOR 0x0000000000000020LL // 5
-#define PLAYER_TITLE_KNIGHT 0x0000000000000040LL // 6
-#define PLAYER_TITLE_KNIGHT_LIEUTENANT 0x0000000000000080LL // 7
-#define PLAYER_TITLE_KNIGHT_CAPTAIN 0x0000000000000100LL // 8
-#define PLAYER_TITLE_KNIGHT_CHAMPION 0x0000000000000200LL // 9
-#define PLAYER_TITLE_LIEUTENANT_COMMANDER 0x0000000000000400LL // 10
-#define PLAYER_TITLE_COMMANDER 0x0000000000000800LL // 11
-#define PLAYER_TITLE_MARSHAL 0x0000000000001000LL // 12
-#define PLAYER_TITLE_FIELD_MARSHAL 0x0000000000002000LL // 13
-#define PLAYER_TITLE_GRAND_MARSHAL 0x0000000000004000LL // 14
-#define PLAYER_TITLE_SCOUT 0x0000000000008000LL // 15
-#define PLAYER_TITLE_GRUNT 0x0000000000010000LL // 16
-#define PLAYER_TITLE_SERGEANT_H 0x0000000000020000LL // 17
-#define PLAYER_TITLE_SENIOR_SERGEANT 0x0000000000040000LL // 18
-#define PLAYER_TITLE_FIRST_SERGEANT 0x0000000000080000LL // 19
-#define PLAYER_TITLE_STONE_GUARD 0x0000000000100000LL // 20
-#define PLAYER_TITLE_BLOOD_GUARD 0x0000000000200000LL // 21
-#define PLAYER_TITLE_LEGIONNAIRE 0x0000000000400000LL // 22
-#define PLAYER_TITLE_CENTURION 0x0000000000800000LL // 23
-#define PLAYER_TITLE_CHAMPION 0x0000000001000000LL // 24
-#define PLAYER_TITLE_LIEUTENANT_GENERAL 0x0000000002000000LL // 25
-#define PLAYER_TITLE_GENERAL 0x0000000004000000LL // 26
-#define PLAYER_TITLE_WARLORD 0x0000000008000000LL // 27
-#define PLAYER_TITLE_HIGH_WARLORD 0x0000000010000000LL // 28
-#define PLAYER_TITLE_GLADIATOR 0x0000000020000000LL // 29
-#define PLAYER_TITLE_DUELIST 0x0000000040000000LL // 30
-#define PLAYER_TITLE_RIVAL 0x0000000080000000LL // 31
-#define PLAYER_TITLE_CHALLENGER 0x0000000100000000LL // 32
-#define PLAYER_TITLE_SCARAB_LORD 0x0000000200000000LL // 33
-#define PLAYER_TITLE_CONQUEROR 0x0000000400000000LL // 34
-#define PLAYER_TITLE_JUSTICAR 0x0000000800000000LL // 35
-#define PLAYER_TITLE_CHAMPION_OF_THE_NAARU 0x0000001000000000LL // 36
-#define PLAYER_TITLE_MERCILESS_GLADIATOR 0x0000002000000000LL // 37
-#define PLAYER_TITLE_OF_THE_SHATTERED_SUN 0x0000004000000000LL // 38
-#define PLAYER_TITLE_HAND_OF_ADAL 0x0000008000000000LL // 39
-#define PLAYER_TITLE_VENGEFUL_GLADIATOR 0x0000010000000000LL // 40
-
-// used in PLAYER_FIELD_BYTES values
-enum PlayerFieldByteFlags
-{
- PLAYER_FIELD_BYTE_TRACK_STEALTHED = 0x00000002,
- PLAYER_FIELD_BYTE_RELEASE_TIMER = 0x00000008, // Display time till auto release spirit
- PLAYER_FIELD_BYTE_NO_RELEASE_WINDOW = 0x00000010 // Display no "release spirit" window at all
-};
-
-// used in PLAYER_FIELD_BYTES2 values
-enum PlayerFieldByte2Flags
-{
- PLAYER_FIELD_BYTE2_NONE = 0x0000,
- PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW = 0x4000
-};
-
-enum ActivateTaxiReplies
-{
- ERR_TAXIOK = 0,
- ERR_TAXIUNSPECIFIEDSERVERERROR = 1,
- ERR_TAXINOSUCHPATH = 2,
- ERR_TAXINOTENOUGHMONEY = 3,
- ERR_TAXITOOFARAWAY = 4,
- ERR_TAXINOVENDORNEARBY = 5,
- ERR_TAXINOTVISITED = 6,
- ERR_TAXIPLAYERBUSY = 7,
- ERR_TAXIPLAYERALREADYMOUNTED = 8,
- ERR_TAXIPLAYERSHAPESHIFTED = 9,
- ERR_TAXIPLAYERMOVING = 10,
- ERR_TAXISAMENODE = 11,
- ERR_TAXINOTSTANDING = 12
-};
-
-enum LootType
-{
- LOOT_CORPSE = 1,
- LOOT_SKINNING = 2,
- LOOT_FISHING = 3,
- LOOT_PICKPOCKETING = 4, // unsupported by client, sending LOOT_SKINNING instead
- LOOT_DISENCHANTING = 5, // unsupported by client, sending LOOT_SKINNING instead
- LOOT_PROSPECTING = 6, // unsupported by client, sending LOOT_SKINNING instead
- LOOT_INSIGNIA = 7, // unsupported by client, sending LOOT_SKINNING instead
- LOOT_FISHINGHOLE = 8 // unsupported by client, sending LOOT_FISHING instead
-};
-
-enum MirrorTimerType
-{
- FATIGUE_TIMER = 0,
- BREATH_TIMER = 1,
- FIRE_TIMER = 2
-};
-
-// 2^n values
-enum PlayerExtraFlags
-{
- // gm abilities
- PLAYER_EXTRA_GM_ON = 0x0001,
- PLAYER_EXTRA_GM_ACCEPT_TICKETS = 0x0002,
- 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.
-};
-
-// 2^n values
-enum AtLoginFlags
-{
- AT_LOGIN_NONE = 0,
- AT_LOGIN_RENAME = 1,
- AT_LOGIN_RESET_SPELLS = 2,
- AT_LOGIN_RESET_TALENTS = 4
-};
-
-typedef std::map<uint32, QuestStatusData> QuestStatusMap;
-
-enum QuestSlotOffsets
-{
- QUEST_ID_OFFSET = 0,
- QUEST_STATE_OFFSET = 1,
- QUEST_COUNTS_OFFSET = 2,
- QUEST_TIME_OFFSET = 3
-};
-
-#define MAX_QUEST_OFFSET 4
-
-enum QuestSlotStateMask
-{
- QUEST_STATE_NONE = 0x0000,
- QUEST_STATE_COMPLETE = 0x0001,
- QUEST_STATE_FAIL = 0x0002
-};
-
-class Quest;
-class Spell;
-class Item;
-class WorldSession;
-
-enum PlayerSlots
-{
- // first slot for item stored (in any way in player m_items data)
- PLAYER_SLOT_START = 0,
- // last+1 slot for item stored (in any way in player m_items data)
- PLAYER_SLOT_END = 118,
- PLAYER_SLOTS_COUNT = (PLAYER_SLOT_END - PLAYER_SLOT_START)
-};
-
-enum EquipmentSlots
-{
- EQUIPMENT_SLOT_START = 0,
- EQUIPMENT_SLOT_HEAD = 0,
- EQUIPMENT_SLOT_NECK = 1,
- EQUIPMENT_SLOT_SHOULDERS = 2,
- EQUIPMENT_SLOT_BODY = 3,
- EQUIPMENT_SLOT_CHEST = 4,
- EQUIPMENT_SLOT_WAIST = 5,
- EQUIPMENT_SLOT_LEGS = 6,
- EQUIPMENT_SLOT_FEET = 7,
- EQUIPMENT_SLOT_WRISTS = 8,
- EQUIPMENT_SLOT_HANDS = 9,
- EQUIPMENT_SLOT_FINGER1 = 10,
- EQUIPMENT_SLOT_FINGER2 = 11,
- EQUIPMENT_SLOT_TRINKET1 = 12,
- EQUIPMENT_SLOT_TRINKET2 = 13,
- EQUIPMENT_SLOT_BACK = 14,
- EQUIPMENT_SLOT_MAINHAND = 15,
- EQUIPMENT_SLOT_OFFHAND = 16,
- EQUIPMENT_SLOT_RANGED = 17,
- EQUIPMENT_SLOT_TABARD = 18,
- EQUIPMENT_SLOT_END = 19
-};
-
-enum InventorySlots
-{
- INVENTORY_SLOT_BAG_0 = 255,
- INVENTORY_SLOT_BAG_START = 19,
- INVENTORY_SLOT_BAG_1 = 19,
- INVENTORY_SLOT_BAG_2 = 20,
- INVENTORY_SLOT_BAG_3 = 21,
- INVENTORY_SLOT_BAG_4 = 22,
- INVENTORY_SLOT_BAG_END = 23,
-
- INVENTORY_SLOT_ITEM_START = 23,
- INVENTORY_SLOT_ITEM_1 = 23,
- INVENTORY_SLOT_ITEM_2 = 24,
- INVENTORY_SLOT_ITEM_3 = 25,
- INVENTORY_SLOT_ITEM_4 = 26,
- INVENTORY_SLOT_ITEM_5 = 27,
- INVENTORY_SLOT_ITEM_6 = 28,
- INVENTORY_SLOT_ITEM_7 = 29,
- INVENTORY_SLOT_ITEM_8 = 30,
- INVENTORY_SLOT_ITEM_9 = 31,
- INVENTORY_SLOT_ITEM_10 = 32,
- INVENTORY_SLOT_ITEM_11 = 33,
- INVENTORY_SLOT_ITEM_12 = 34,
- INVENTORY_SLOT_ITEM_13 = 35,
- INVENTORY_SLOT_ITEM_14 = 36,
- INVENTORY_SLOT_ITEM_15 = 37,
- INVENTORY_SLOT_ITEM_16 = 38,
- INVENTORY_SLOT_ITEM_END = 39
-};
-
-enum BankSlots
-{
- BANK_SLOT_ITEM_START = 39,
- BANK_SLOT_ITEM_1 = 39,
- BANK_SLOT_ITEM_2 = 40,
- BANK_SLOT_ITEM_3 = 41,
- BANK_SLOT_ITEM_4 = 42,
- BANK_SLOT_ITEM_5 = 43,
- BANK_SLOT_ITEM_6 = 44,
- BANK_SLOT_ITEM_7 = 45,
- BANK_SLOT_ITEM_8 = 46,
- BANK_SLOT_ITEM_9 = 47,
- BANK_SLOT_ITEM_10 = 48,
- BANK_SLOT_ITEM_11 = 49,
- BANK_SLOT_ITEM_12 = 50,
- BANK_SLOT_ITEM_13 = 51,
- BANK_SLOT_ITEM_14 = 52,
- BANK_SLOT_ITEM_15 = 53,
- BANK_SLOT_ITEM_16 = 54,
- BANK_SLOT_ITEM_17 = 55,
- BANK_SLOT_ITEM_18 = 56,
- BANK_SLOT_ITEM_19 = 57,
- BANK_SLOT_ITEM_20 = 58,
- BANK_SLOT_ITEM_21 = 59,
- BANK_SLOT_ITEM_22 = 60,
- BANK_SLOT_ITEM_23 = 61,
- BANK_SLOT_ITEM_24 = 62,
- BANK_SLOT_ITEM_25 = 63,
- BANK_SLOT_ITEM_26 = 64,
- BANK_SLOT_ITEM_27 = 65,
- BANK_SLOT_ITEM_28 = 66,
- BANK_SLOT_ITEM_END = 67,
-
- BANK_SLOT_BAG_START = 67,
- BANK_SLOT_BAG_1 = 67,
- BANK_SLOT_BAG_2 = 68,
- BANK_SLOT_BAG_3 = 69,
- BANK_SLOT_BAG_4 = 70,
- BANK_SLOT_BAG_5 = 71,
- BANK_SLOT_BAG_6 = 72,
- BANK_SLOT_BAG_7 = 73,
- BANK_SLOT_BAG_END = 74
-};
-
-enum BuyBackSlots
-{
- // stored in m_buybackitems
- BUYBACK_SLOT_START = 74,
- BUYBACK_SLOT_1 = 74,
- BUYBACK_SLOT_2 = 75,
- BUYBACK_SLOT_3 = 76,
- BUYBACK_SLOT_4 = 77,
- BUYBACK_SLOT_5 = 78,
- BUYBACK_SLOT_6 = 79,
- BUYBACK_SLOT_7 = 80,
- BUYBACK_SLOT_8 = 81,
- BUYBACK_SLOT_9 = 82,
- BUYBACK_SLOT_10 = 83,
- BUYBACK_SLOT_11 = 84,
- BUYBACK_SLOT_12 = 85,
- BUYBACK_SLOT_END = 86
-};
-
-enum KeyRingSlots
-{
- KEYRING_SLOT_START = 86,
- KEYRING_SLOT_END = 118
-};
-
-struct ItemPosCount
-{
- ItemPosCount(uint16 _pos, uint8 _count) : pos(_pos), count(_count) {}
- bool isContainedIn(std::vector<ItemPosCount> const& vec) const;
- uint16 pos;
- uint8 count;
-};
-typedef std::vector<ItemPosCount> ItemPosCountVec;
-
-enum SwitchWeapon
-{
- DEFAULT_SWITCH_WEAPON = 1500, //cooldown in ms
- ROGUE_SWITCH_WEAPON = 1000
-};
-
-enum TradeSlots
-{
- TRADE_SLOT_COUNT = 7,
- TRADE_SLOT_TRADED_COUNT = 6,
- TRADE_SLOT_NONTRADED = 6
-};
-
-enum TransferAbortReason
-{
- TRANSFER_ABORT_MAX_PLAYERS = 0x0001, // Transfer Aborted: instance is full
- TRANSFER_ABORT_NOT_FOUND = 0x0002, // Transfer Aborted: instance not found
- TRANSFER_ABORT_TOO_MANY_INSTANCES = 0x0003, // You have entered too many instances recently.
- TRANSFER_ABORT_ZONE_IN_COMBAT = 0x0005, // Unable to zone in while an encounter is in progress.
- TRANSFER_ABORT_INSUF_EXPAN_LVL1 = 0x0106, // You must have TBC expansion installed to access this area.
- TRANSFER_ABORT_DIFFICULTY1 = 0x0007, // Normal difficulty mode is not available for %s.
- TRANSFER_ABORT_DIFFICULTY2 = 0x0107, // Heroic difficulty mode is not available for %s.
- TRANSFER_ABORT_DIFFICULTY3 = 0x0207 // Epic difficulty mode is not available for %s.
-};
-
-enum InstanceResetWarningType
-{
- RAID_INSTANCE_WARNING_HOURS = 1, // WARNING! %s is scheduled to reset in %d hour(s).
- RAID_INSTANCE_WARNING_MIN = 2, // WARNING! %s is scheduled to reset in %d minute(s)!
- RAID_INSTANCE_WARNING_MIN_SOON = 3, // WARNING! %s is scheduled to reset in %d minute(s). Please exit the zone or you will be returned to your bind location!
- RAID_INSTANCE_WELCOME = 4 // Welcome to %s. This raid instance is scheduled to reset in %s.
-};
-
-struct MovementInfo
-{
- // common
- //uint32 flags;
- uint8 unk1;
- uint32 time;
- float x, y, z, o;
- // transport
- uint64 t_guid;
- float t_x, t_y, t_z, t_o;
- uint32 t_time;
- // swimming and unk
- float s_pitch;
- // last fall time
- uint32 fallTime;
- // jumping
- float j_unk, j_sinAngle, j_cosAngle, j_xyspeed;
- // spline
- float u_unk1;
-
- MovementInfo()
- {
- //flags =
- time = t_time = fallTime = 0;
- unk1 = 0;
- x = y = z = o = t_x = t_y = t_z = t_o = s_pitch = j_unk = j_sinAngle = j_cosAngle = j_xyspeed = u_unk1 = 0.0f;
- t_guid = 0;
- }
-
- /*void SetMovementFlags(uint32 _flags)
- {
- flags = _flags;
- }*/
-};
-
-// flags that use in movement check for example at spell casting
-MovementFlags const movementFlagsMask = MovementFlags(
- MOVEMENTFLAG_FORWARD |MOVEMENTFLAG_BACKWARD |MOVEMENTFLAG_STRAFE_LEFT|MOVEMENTFLAG_STRAFE_RIGHT|
- MOVEMENTFLAG_PITCH_UP|MOVEMENTFLAG_PITCH_DOWN|MOVEMENTFLAG_FLY_UNK1 |
- MOVEMENTFLAG_JUMPING |MOVEMENTFLAG_FALLING |MOVEMENTFLAG_FLY_UP |
- MOVEMENTFLAG_FLYING |MOVEMENTFLAG_SPLINE
-);
-
-MovementFlags const movementOrTurningFlagsMask = MovementFlags(
- movementFlagsMask | MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT
-);
-class InstanceSave;
-
-enum RestType
-{
- REST_TYPE_NO = 0,
- REST_TYPE_IN_TAVERN = 1,
- REST_TYPE_IN_CITY = 2
-};
-
-enum DuelCompleteType
-{
- DUEL_INTERUPTED = 0,
- DUEL_WON = 1,
- DUEL_FLED = 2
-};
-
-enum TeleportToOptions
-{
- TELE_TO_GM_MODE = 0x01,
- TELE_TO_NOT_LEAVE_TRANSPORT = 0x02,
- TELE_TO_NOT_LEAVE_COMBAT = 0x04,
- TELE_TO_NOT_UNSUMMON_PET = 0x08,
- TELE_TO_SPELL = 0x10,
-};
-
-/// Type of environmental damages
-enum EnviromentalDamage
-{
- DAMAGE_EXHAUSTED = 0,
- DAMAGE_DROWNING = 1,
- DAMAGE_FALL = 2,
- DAMAGE_LAVA = 3,
- DAMAGE_SLIME = 4,
- DAMAGE_FIRE = 5,
- DAMAGE_FALL_TO_VOID = 6 // custom case for fall without durability loss
-};
-
-// used at player loading query list preparing, and later result selection
-enum PlayerLoginQueryIndex
-{
- PLAYER_LOGIN_QUERY_LOADFROM = 0,
- PLAYER_LOGIN_QUERY_LOADGROUP = 1,
- PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES = 2,
- PLAYER_LOGIN_QUERY_LOADAURAS = 3,
- PLAYER_LOGIN_QUERY_LOADSPELLS = 4,
- PLAYER_LOGIN_QUERY_LOADQUESTSTATUS = 5,
- PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS = 6,
- PLAYER_LOGIN_QUERY_LOADTUTORIALS = 7, // common for all characters for some account at specific realm
- PLAYER_LOGIN_QUERY_LOADREPUTATION = 8,
- PLAYER_LOGIN_QUERY_LOADINVENTORY = 9,
- PLAYER_LOGIN_QUERY_LOADACTIONS = 10,
- PLAYER_LOGIN_QUERY_LOADMAILCOUNT = 11,
- PLAYER_LOGIN_QUERY_LOADMAILDATE = 12,
- PLAYER_LOGIN_QUERY_LOADSOCIALLIST = 13,
- PLAYER_LOGIN_QUERY_LOADHOMEBIND = 14,
- PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS = 15,
- PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES = 16,
- PLAYER_LOGIN_QUERY_LOADGUILD = 17,
-};
-
-#define MAX_PLAYER_LOGIN_QUERY 18
-
-// Player summoning auto-decline time (in secs)
-#define MAX_PLAYER_SUMMON_DELAY (2*MINUTE)
-#define MAX_MONEY_AMOUNT (0x7FFFFFFF-1)
-
-struct InstancePlayerBind
-{
- InstanceSave *save;
- bool perm;
- /* permanent PlayerInstanceBinds are created in Raid/Heroic instances for players
- that aren't already permanently bound when they are inside when a boss is killed
- or when they enter an instance that the group leader is permanently bound to. */
- InstancePlayerBind() : save(NULL), perm(false) {}
-};
-
-class MANGOS_DLL_SPEC PlayerTaxi
-{
- public:
- PlayerTaxi();
- ~PlayerTaxi() {}
- // Nodes
- void InitTaxiNodesForLevel(uint32 race, uint32 level);
- void LoadTaxiMask(const char* data);
- void SaveTaxiMask(const char* data);
-
- uint32 GetTaximask( uint8 index ) const { return m_taximask[index]; }
- bool IsTaximaskNodeKnown(uint32 nodeidx) const
- {
- uint8 field = uint8((nodeidx - 1) / 32);
- uint32 submask = 1<<((nodeidx-1)%32);
- return (m_taximask[field] & submask) == submask;
- }
- bool SetTaximaskNode(uint32 nodeidx)
- {
- uint8 field = uint8((nodeidx - 1) / 32);
- uint32 submask = 1<<((nodeidx-1)%32);
- if ((m_taximask[field] & submask) != submask )
- {
- m_taximask[field] |= submask;
- return true;
- }
- else
- return false;
- }
- void AppendTaximaskTo(ByteBuffer& data,bool all);
-
- // Destinations
- bool LoadTaxiDestinationsFromString(std::string values);
- std::string SaveTaxiDestinationsToString();
-
- void ClearTaxiDestinations() { m_TaxiDestinations.clear(); }
- void AddTaxiDestination(uint32 dest) { m_TaxiDestinations.push_back(dest); }
- uint32 GetTaxiSource() const { return m_TaxiDestinations.empty() ? 0 : m_TaxiDestinations.front(); }
- uint32 GetTaxiDestination() const { return m_TaxiDestinations.size() < 2 ? 0 : m_TaxiDestinations[1]; }
- uint32 GetCurrentTaxiPath() const;
- uint32 NextTaxiDestination()
- {
- m_TaxiDestinations.pop_front();
- return GetTaxiDestination();
- }
- bool empty() const { return m_TaxiDestinations.empty(); }
- private:
- TaxiMask m_taximask;
- std::deque<uint32> m_TaxiDestinations;
-};
-
-class MANGOS_DLL_SPEC Player : public Unit
-{
- friend class WorldSession;
- friend void Item::AddToUpdateQueueOf(Player *player);
- friend void Item::RemoveFromUpdateQueueOf(Player *player);
- public:
- explicit Player (WorldSession *session);
- ~Player ( );
-
- void CleanupsBeforeDelete();
-
- static UpdateMask updateVisualBits;
- static void InitVisibleBits();
-
- void AddToWorld();
- void RemoveFromWorld();
-
- bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0);
-
- bool TeleportTo(WorldLocation const &loc, uint32 options = 0)
- {
- return TeleportTo(loc.mapid, loc.x, loc.y, loc.z, options);
- }
-
- void SetSummonPoint(uint32 mapid, float x, float y, float z)
- {
- m_summon_expire = time(NULL) + MAX_PLAYER_SUMMON_DELAY;
- m_summon_mapid = mapid;
- m_summon_x = x;
- m_summon_y = y;
- m_summon_z = z;
- }
- void SummonIfPossible(bool agree);
-
- bool Create( uint32 guidlow, std::string name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId );
-
- void Update( uint32 time );
-
- void BuildEnumData( QueryResult * result, WorldPacket * p_data );
-
- void SetInWater(bool apply);
-
- bool IsInWater() const { return m_isInWater; }
- bool IsUnderWater() const;
-
- void SendInitialPacketsBeforeAddToMap();
- void SendInitialPacketsAfterAddToMap();
- void SendTransferAborted(uint32 mapid, uint16 reason);
- void SendInstanceResetWarning(uint32 mapid, uint32 time);
-
- bool CanInteractWithNPCs(bool alive = true) const;
-
- bool ToggleAFK();
- bool ToggleDND();
- bool isAFK() const { return HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_AFK); };
- bool isDND() const { return HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_DND); };
- uint8 chatTag() const;
- std::string afkMsg;
- std::string dndMsg;
-
- PlayerSocial *GetSocial() { return m_social; }
-
- PlayerTaxi m_taxi;
- void InitTaxiNodesForLevel() { m_taxi.InitTaxiNodesForLevel(getRace(),getLevel()); }
- bool ActivateTaxiPathTo(std::vector<uint32> const& nodes, uint32 mount_id = 0 , Creature* npc = NULL);
- // mount_id can be used in scripting calls
- bool isAcceptTickets() const { return GetSession()->GetSecurity() >= SEC_GAMEMASTER && (m_ExtraFlags & PLAYER_EXTRA_GM_ACCEPT_TICKETS); }
- void SetAcceptTicket(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_GM_ACCEPT_TICKETS; else m_ExtraFlags &= ~PLAYER_EXTRA_GM_ACCEPT_TICKETS; }
- bool isAcceptWhispers() const { return m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS; }
- 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); }
- void SetGMVisible(bool on);
- void SetPvPDeath(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_PVP_DEATH; else m_ExtraFlags &= ~PLAYER_EXTRA_PVP_DEATH; }
-
- void GiveXP(uint32 xp, Unit* victim);
- void GiveLevel(uint32 level);
- void InitStatsForLevel(bool reapplyMods = false);
-
- // Played Time Stuff
- time_t m_logintime;
- time_t m_Last_tick;
- uint32 m_Played_time[2];
- uint32 GetTotalPlayedTime() { return m_Played_time[0]; };
- uint32 GetLevelPlayedTime() { return m_Played_time[1]; };
-
- void setDeathState(DeathState s); // overwrite Unit::setDeathState
-
- void InnEnter (int time,uint32 mapid, float x,float y,float z)
- {
- inn_pos_mapid = mapid;
- inn_pos_x = x;
- inn_pos_y = y;
- inn_pos_z = z;
- time_inn_enter = time;
- };
-
- float GetRestBonus() const { return m_rest_bonus; };
- void SetRestBonus(float rest_bonus_new);
-
- RestType GetRestType() const { return rest_type; };
- void SetRestType(RestType n_r_type) { rest_type = n_r_type; };
-
- uint32 GetInnPosMapId() const { return inn_pos_mapid; };
- float GetInnPosX() const { return inn_pos_x; };
- float GetInnPosY() const { return inn_pos_y; };
- float GetInnPosZ() const { return inn_pos_z; };
-
- int GetTimeInnEnter() const { return time_inn_enter; };
- void UpdateInnerTime (int time) { time_inn_enter = time; };
-
- void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false);
- void RemoveMiniPet();
- Pet* GetMiniPet();
- void SetMiniPet(Pet* pet) { m_miniPet = pet->GetGUID(); }
- void RemoveGuardians();
- bool HasGuardianWithEntry(uint32 entry);
- void AddGuardian(Pet* pet) { m_guardianPets.insert(pet->GetGUID()); }
- GuardianPetList const& GetGuardians() const { return m_guardianPets; }
- void Uncharm();
-
- void Say(std::string text, const uint32 language);
- void Yell(std::string text, const uint32 language);
- void TextEmote(std::string text);
- void Whisper(std::string text, const uint32 language,uint64 receiver);
- void BuildPlayerChat(WorldPacket *data, uint8 msgtype, std::string text, uint32 language) const;
-
- /*********************************************************/
- /*** STORAGE SYSTEM ***/
- /*********************************************************/
-
- void SetVirtualItemSlot( uint8 i, Item* item);
- void SetSheath( uint32 sheathed );
- uint8 FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap ) const;
- uint32 GetItemCount( uint32 item, bool inBankAlso = false, Item* skipItem = NULL ) const;
- Item* GetItemByGuid( uint64 guid ) const;
- Item* GetItemByPos( uint16 pos ) const;
- Item* GetItemByPos( uint8 bag, uint8 slot ) const;
- Item* GetWeaponForAttack(WeaponAttackType attackType, bool useable = false) const;
- Item* GetShield(bool useable = false) const;
- static uint32 GetAttackBySlot( uint8 slot ); // MAX_ATTACK if not weapon slot
- std::vector<Item *> &GetItemUpdateQueue() { return m_itemUpdateQueue; }
- static bool IsInventoryPos( uint16 pos ) { return IsInventoryPos(pos >> 8,pos & 255); }
- static bool IsInventoryPos( uint8 bag, uint8 slot );
- static bool IsEquipmentPos( uint16 pos ) { return IsEquipmentPos(pos >> 8,pos & 255); }
- static bool IsEquipmentPos( uint8 bag, uint8 slot );
- static bool IsBagPos( uint16 pos );
- static bool IsBankPos( uint16 pos ) { return IsBankPos(pos >> 8,pos & 255); }
- static bool IsBankPos( uint8 bag, uint8 slot );
- bool HasBankBagSlot( uint8 slot ) const;
- bool HasItemCount( uint32 item, uint32 count, bool inBankAlso = false ) const;
- bool HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem = NULL);
- Item* GetItemOrItemWithGemEquipped( uint32 item ) const;
- uint8 CanTakeMoreSimilarItems(Item* pItem) const { return _CanTakeMoreSimilarItems(pItem->GetEntry(),pItem->GetCount(),pItem); }
- uint8 CanTakeMoreSimilarItems(uint32 entry, uint32 count) const { return _CanTakeMoreSimilarItems(entry,count,NULL); }
- uint8 CanStoreNewItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count = NULL ) const
- {
- return _CanStoreItem(bag, slot, dest, item, count, NULL, false, no_space_count );
- }
- uint8 CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap = false ) const
- {
- if(!pItem)
- return EQUIP_ERR_ITEM_NOT_FOUND;
- uint32 count = pItem->GetCount();
- return _CanStoreItem( bag, slot, dest, pItem->GetEntry(), count, pItem, swap, NULL );
-
- }
- uint8 CanStoreItems( Item **pItem,int count) const;
- uint8 CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, uint32 count, bool swap ) const;
- uint8 CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading = true ) const;
- uint8 CanUnequipItems( uint32 item, uint32 count ) const;
- uint8 CanUnequipItem( uint16 src, bool swap ) const;
- uint8 CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap, bool not_loading = true ) const;
- uint8 CanUseItem( Item *pItem, bool not_loading = true ) const;
- bool HasItemTotemCategory( uint32 TotemCategory ) const;
- bool CanUseItem( ItemPrototype const *pItem );
- uint8 CanUseAmmo( uint32 item ) const;
- Item* StoreNewItem( ItemPosCountVec const& pos, uint32 item, bool update,int32 randomPropertyId = 0 );
- Item* StoreItem( ItemPosCountVec const& pos, Item *pItem, bool update );
- Item* EquipNewItem( uint16 pos, uint32 item, uint32 count, bool update );
- Item* EquipItem( uint16 pos, Item *pItem, bool update );
- void AutoUnequipOffhandIfNeed();
-
- uint8 _CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL) const;
- uint8 _CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item *pItem = NULL, bool swap = false, uint32* no_space_count = NULL ) const;
-
- void ApplyEquipCooldown( Item * pItem );
- void SetAmmo( uint32 item );
- void RemoveAmmo();
- float GetAmmoDPS() const { return m_ammoDPS; }
- bool CheckAmmoCompatibility(const ItemPrototype *ammo_proto) const;
- void QuickEquipItem( uint16 pos, Item *pItem);
- void VisualizeItem( uint8 slot, Item *pItem);
- void SetVisibleItemSlot(uint8 slot, Item *pItem);
- Item* BankItem( ItemPosCountVec const& dest, Item *pItem, bool update )
- {
- return StoreItem( dest, pItem, update);
- }
- Item* BankItem( uint16 pos, Item *pItem, bool update );
- void RemoveItem( uint8 bag, uint8 slot, bool update );
- void MoveItemFromInventory(uint8 bag, uint8 slot, bool update);
- // in trade, auction, guild bank, mail....
- void MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB = false);
- // in trade, guild bank, mail....
- void RemoveItemDependentAurasAndCasts( Item * pItem );
- void DestroyItem( uint8 bag, uint8 slot, bool update );
- void DestroyItemCount( uint32 item, uint32 count, bool update, bool unequip_check = false);
- void DestroyItemCount( Item* item, uint32& count, bool update );
- void DestroyConjuredItems( bool update );
- void DestroyZoneLimitedItem( bool update, uint32 new_zone );
- void SplitItem( uint16 src, uint16 dst, uint32 count );
- void SwapItem( uint16 src, uint16 dst );
- void AddItemToBuyBackSlot( Item *pItem );
- Item* GetItemFromBuyBackSlot( uint32 slot );
- void RemoveItemFromBuyBackSlot( uint32 slot, bool del );
- uint32 GetMaxKeyringSize() const { return KEYRING_SLOT_END-KEYRING_SLOT_START; }
- void SendEquipError( uint8 msg, Item* pItem, Item *pItem2 );
- void SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 param );
- void SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param );
- void AddWeaponProficiency(uint32 newflag) { m_WeaponProficiency |= newflag; }
- void AddArmorProficiency(uint32 newflag) { m_ArmorProficiency |= newflag; }
- uint32 GetWeaponProficiency() const { return m_WeaponProficiency; }
- uint32 GetArmorProficiency() const { return m_ArmorProficiency; }
- bool IsInFeralForm() const { return m_form == FORM_CAT || m_form == FORM_BEAR || m_form == FORM_DIREBEAR; }
- bool IsUseEquipedWeapon( bool mainhand ) const
- {
- // disarm applied only to mainhand weapon
- return !IsInFeralForm() && (!mainhand || !HasFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISARMED) );
- }
- void SendNewItem( Item *item, uint32 count, bool received, bool created, bool broadcast = false );
- bool BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint64 bagguid, uint8 slot);
-
- float GetReputationPriceDiscount( Creature const* pCreature ) const;
- Player* GetTrader() const { return pTrader; }
- void ClearTrade();
- void TradeCancel(bool sendback);
- uint16 GetItemPosByTradeSlot(uint32 slot) const { return tradeItems[slot]; }
-
- void UpdateEnchantTime(uint32 time);
- void UpdateItemDuration(uint32 time, bool realtimeonly=false);
- void AddEnchantmentDurations(Item *item);
- void RemoveEnchantmentDurations(Item *item);
- void RemoveAllEnchantments(EnchantmentSlot slot);
- void AddEnchantmentDuration(Item *item,EnchantmentSlot slot,uint32 duration);
- void ApplyEnchantment(Item *item,EnchantmentSlot slot,bool apply, bool apply_dur = true, bool ignore_condition = false);
- void ApplyEnchantment(Item *item,bool apply);
- void SendEnchantmentDurations();
- void AddItemDurations(Item *item);
- void RemoveItemDurations(Item *item);
- void SendItemDurations();
- void LoadCorpse();
- void LoadPet();
-
- uint32 m_stableSlots;
-
- /*********************************************************/
- /*** QUEST SYSTEM ***/
- /*********************************************************/
-
- void PrepareQuestMenu( uint64 guid );
- void SendPreparedQuest( uint64 guid );
- bool IsActiveQuest( uint32 quest_id ) const;
- Quest const *GetNextQuest( uint64 guid, Quest const *pQuest );
- bool CanSeeStartQuest( Quest const *pQuest );
- bool CanTakeQuest( Quest const *pQuest, bool msg );
- bool CanAddQuest( Quest const *pQuest, bool msg );
- bool CanCompleteQuest( uint32 quest_id );
- bool CanCompleteRepeatableQuest(Quest const *pQuest);
- bool CanRewardQuest( Quest const *pQuest, bool msg );
- bool CanRewardQuest( Quest const *pQuest, uint32 reward, bool msg );
- void AddQuest( Quest const *pQuest, Object *questGiver );
- void CompleteQuest( uint32 quest_id );
- void IncompleteQuest( uint32 quest_id );
- void RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce = true );
- void FailQuest( uint32 quest_id );
- void FailTimedQuest( uint32 quest_id );
- bool SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg );
- bool SatisfyQuestLevel( Quest const* qInfo, bool msg );
- bool SatisfyQuestLog( bool msg );
- bool SatisfyQuestPreviousQuest( Quest const* qInfo, bool msg );
- bool SatisfyQuestRace( Quest const* qInfo, bool msg );
- bool SatisfyQuestReputation( Quest const* qInfo, bool msg );
- bool SatisfyQuestStatus( Quest const* qInfo, bool msg );
- bool SatisfyQuestTimed( Quest const* qInfo, bool msg );
- bool SatisfyQuestExclusiveGroup( Quest const* qInfo, bool msg );
- bool SatisfyQuestNextChain( Quest const* qInfo, bool msg );
- bool SatisfyQuestPrevChain( Quest const* qInfo, bool msg );
- bool SatisfyQuestDay( Quest const* qInfo, bool msg );
- bool GiveQuestSourceItem( Quest const *pQuest );
- bool TakeQuestSourceItem( uint32 quest_id, bool msg );
- bool GetQuestRewardStatus( uint32 quest_id ) const;
- QuestStatus GetQuestStatus( uint32 quest_id ) const;
- void SetQuestStatus( uint32 quest_id, QuestStatus status );
-
- void SetDailyQuestStatus( uint32 quest_id );
- void ResetDailyQuestStatus();
-
- uint16 FindQuestSlot( uint32 quest_id ) const;
- uint32 GetQuestSlotQuestId(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_ID_OFFSET); }
- uint32 GetQuestSlotState(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET); }
- uint32 GetQuestSlotCounters(uint16 slot)const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET); }
- uint8 GetQuestSlotCounter(uint16 slot,uint8 counter) const { return GetByteValue(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET,counter); }
- uint32 GetQuestSlotTime(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_TIME_OFFSET); }
- void SetQuestSlot(uint16 slot,uint32 quest_id, uint32 timer = 0)
- {
- SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_ID_OFFSET,quest_id);
- SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET,0);
- SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET,0);
- SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_TIME_OFFSET,timer);
- }
- void SetQuestSlotCounter(uint16 slot,uint8 counter,uint8 count) { SetByteValue(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET,counter,count); }
- void SetQuestSlotState(uint16 slot,uint32 state) { SetFlag(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET,state); }
- void RemoveQuestSlotState(uint16 slot,uint32 state) { RemoveFlag(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET,state); }
- void SetQuestSlotTimer(uint16 slot,uint32 timer) { SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_TIME_OFFSET,timer); }
- void SwapQuestSlot(uint16 slot1,uint16 slot2)
- {
- for (int i = 0; i < MAX_QUEST_OFFSET ; ++i )
- {
- uint32 temp1 = GetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot1 + i);
- uint32 temp2 = GetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot2 + i);
-
- SetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot1 + i, temp2);
- SetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot2 + i, temp1);
- }
- }
- uint32 GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry);
- void AdjustQuestReqItemCount( Quest const* pQuest );
- void AreaExploredOrEventHappens( uint32 questId );
- void GroupEventHappens( uint32 questId, WorldObject const* pEventObject );
- void ItemAddedQuestCheck( uint32 entry, uint32 count );
- void ItemRemovedQuestCheck( uint32 entry, uint32 count );
- void KilledMonster( uint32 entry, uint64 guid );
- void CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id );
- void TalkedToCreature( uint32 entry, uint64 guid );
- void MoneyChanged( uint32 value );
- bool HasQuestForItem( uint32 itemid ) const;
- bool HasQuestForGO(int32 GOId);
- void UpdateForQuestsGO();
- bool CanShareQuest(uint32 quest_id) const;
-
- void SendQuestComplete( uint32 quest_id );
- void SendQuestReward( Quest const *pQuest, uint32 XP, Object* questGiver );
- void SendQuestFailed( uint32 quest_id );
- void SendQuestTimerFailed( uint32 quest_id );
- void SendCanTakeQuestResponse( uint32 msg );
- void SendPushToPartyResponse( Player *pPlayer, uint32 msg );
- void SendQuestUpdateAddItem( Quest const* pQuest, uint32 item_idx, uint32 count );
- void SendQuestUpdateAddCreatureOrGo( Quest const* pQuest, uint64 guid, uint32 creatureOrGO_idx, uint32 old_count, uint32 add_count );
-
- uint64 GetDivider() { return m_divider; };
- void SetDivider( uint64 guid ) { m_divider = guid; };
-
- uint32 GetInGameTime() { return m_ingametime; };
-
- void SetInGameTime( uint32 time ) { m_ingametime = time; };
-
- void AddTimedQuest( uint32 quest_id ) { m_timedquests.insert(quest_id); }
-
- /*********************************************************/
- /*** LOAD SYSTEM ***/
- /*********************************************************/
-
- bool LoadFromDB(uint32 guid, SqlQueryHolder *holder);
- bool MinimalLoadFromDB(QueryResult *result, uint32 guid);
- static bool LoadValuesArrayFromDB(Tokens& data,uint64 guid);
- static uint32 GetUInt32ValueFromArray(Tokens const& data, uint16 index);
- static float GetFloatValueFromArray(Tokens const& data, uint16 index);
- static uint32 GetUInt32ValueFromDB(uint16 index, uint64 guid);
- static float GetFloatValueFromDB(uint16 index, uint64 guid);
- static uint32 GetZoneIdFromDB(uint64 guid);
- static bool LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid);
-
- /*********************************************************/
- /*** SAVE SYSTEM ***/
- /*********************************************************/
-
- void SaveToDB();
- void SaveInventoryAndGoldToDB(); // fast save function for item/money cheating preventing
- void SaveGoldToDB() { SetUInt32ValueInDB(PLAYER_FIELD_COINAGE,GetMoney(),GetGUID()); }
- static bool SaveValuesArrayInDB(Tokens const& data,uint64 guid);
- static void SetUInt32ValueInArray(Tokens& data,uint16 index, uint32 value);
- static void SetFloatValueInArray(Tokens& data,uint16 index, float value);
- static void SetUInt32ValueInDB(uint16 index, uint32 value, uint64 guid);
- static void SetFloatValueInDB(uint16 index, float value, uint64 guid);
- static void SavePositionInDB(uint32 mapid, float x,float y,float z,float o,uint32 zone,uint64 guid);
-
- bool m_mailsLoaded;
- bool m_mailsUpdated;
-
- void SetBindPoint(uint64 guid);
- void SendTalentWipeConfirm(uint64 guid);
- void RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker );
- void SendPetSkillWipeConfirm();
- void CalcRage( uint32 damage,bool attacker );
- void RegenerateAll();
- void Regenerate(Powers power);
- void RegenerateHealth();
- void setRegenTimer(uint32 time) {m_regenTimer = time;}
- void setWeaponChangeTimer(uint32 time) {m_weaponChangeTimer = time;}
-
- uint32 GetMoney() { return GetUInt32Value (PLAYER_FIELD_COINAGE); }
- void ModifyMoney( int32 d )
- {
- if(d < 0)
- SetMoney (GetMoney() > uint32(-d) ? GetMoney() + d : 0);
- else
- SetMoney (GetMoney() < MAX_MONEY_AMOUNT - d ? GetMoney() + d : MAX_MONEY_AMOUNT);
-
- // "At Gold Limit"
- if(GetMoney() >= MAX_MONEY_AMOUNT)
- SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD,NULL,NULL);
- }
- void SetMoney( uint32 value )
- {
- SetUInt32Value (PLAYER_FIELD_COINAGE, value);
- MoneyChanged( value );
- }
-
- uint32 GetTutorialInt(uint32 intId )
- {
- ASSERT( (intId < 8) );
- return m_Tutorials[intId];
- }
-
- void SetTutorialInt(uint32 intId, uint32 value)
- {
- ASSERT( (intId < 8) );
- if(m_Tutorials[intId]!=value)
- {
- m_Tutorials[intId] = value;
- m_TutorialsChanged = true;
- }
- }
-
- QuestStatusMap& getQuestStatusMap() { return mQuestStatus; };
-
- const uint64& GetSelection( ) const { return m_curSelection; }
- void SetSelection(const uint64 &guid) { m_curSelection = guid; SetUInt64Value(UNIT_FIELD_TARGET, guid); }
-
- uint8 GetComboPoints() { return m_comboPoints; }
- uint64 GetComboTarget() { return m_comboTarget; }
-
- void AddComboPoints(Unit* target, int8 count);
- void ClearComboPoints();
- void SendComboPoints();
-
- void SendMailResult(uint32 mailId, uint32 mailAction, uint32 mailError, uint32 equipError = 0, uint32 item_guid = 0, uint32 item_count = 0);
- void SendNewMail();
- void UpdateNextMailTimeAndUnreads();
- void AddNewMailDeliverTime(time_t deliver_time);
- bool IsMailsLoaded() const { return m_mailsLoaded; }
-
- //void SetMail(Mail *m);
- void RemoveMail(uint32 id);
-
- void AddMail(Mail* mail) { m_mail.push_front(mail);}// for call from WorldSession::SendMailTo
- uint32 GetMailSize() { return m_mail.size();};
- Mail* GetMail(uint32 id);
-
- PlayerMails::iterator GetmailBegin() { return m_mail.begin();};
- PlayerMails::iterator GetmailEnd() { return m_mail.end();};
-
- /*********************************************************/
- /*** MAILED ITEMS SYSTEM ***/
- /*********************************************************/
-
- uint8 unReadMails;
- time_t m_nextMailDelivereTime;
-
- typedef HM_NAMESPACE::hash_map<uint32, Item*> ItemMap;
-
- ItemMap mMitems; //template defined in objectmgr.cpp
-
- Item* GetMItem(uint32 id)
- {
- ItemMap::const_iterator itr = mMitems.find(id);
- if (itr != mMitems.end())
- return itr->second;
-
- return NULL;
- }
-
- void AddMItem(Item* it)
- {
- ASSERT( it );
- //assert deleted, because items can be added before loading
- mMitems[it->GetGUIDLow()] = it;
- }
-
- bool RemoveMItem(uint32 id)
- {
- ItemMap::iterator i = mMitems.find(id);
- if (i == mMitems.end())
- return false;
-
- mMitems.erase(i);
- return true;
- }
-
- void PetSpellInitialize();
- void CharmSpellInitialize();
- void PossessSpellInitialize();
- bool HasSpell(uint32 spell) const;
- TrainerSpellState GetTrainerSpellState(TrainerSpell const* trainer_spell) const;
- bool IsSpellFitByClassAndRace( uint32 spell_id ) const;
-
- void SendProficiency(uint8 pr1, uint32 pr2);
- void SendInitialSpells();
- bool addSpell(uint32 spell_id, bool active, bool learning = true, bool loading = false, uint16 slot_id=SPELL_WITHOUT_SLOT_ID, bool disabled = false);
- void learnSpell(uint32 spell_id);
- void removeSpell(uint32 spell_id, bool disabled = false);
- void resetSpells();
- void learnDefaultSpells(bool loading = false);
- void learnQuestRewardedSpells();
- void learnQuestRewardedSpells(Quest const* quest);
-
- uint32 GetFreeTalentPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS1); }
- void SetFreeTalentPoints(uint32 points) { SetUInt32Value(PLAYER_CHARACTER_POINTS1,points); }
- bool resetTalents(bool no_cost = false);
- uint32 resetTalentsCost() const;
- void InitTalentForLevel();
-
- uint32 GetFreePrimaryProffesionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS2); }
- void SetFreePrimaryProffesions(uint16 profs) { SetUInt32Value(PLAYER_CHARACTER_POINTS2,profs); }
- void InitPrimaryProffesions();
-
- PlayerSpellMap const& GetSpellMap() const { return m_spells; }
- PlayerSpellMap & GetSpellMap() { return m_spells; }
-
- void AddSpellMod(SpellModifier* mod, bool apply);
- int32 GetTotalFlatMods(uint32 spellId, SpellModOp op);
- int32 GetTotalPctMods(uint32 spellId, SpellModOp op);
- bool IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell = NULL);
- template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell const* spell = NULL);
- void RemoveSpellMods(Spell const* spell);
-
- bool HasSpellCooldown(uint32 spell_id) const
- {
- SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
- return itr != m_spellCooldowns.end() && itr->second.end > time(NULL);
- }
- uint32 GetSpellCooldownDelay(uint32 spell_id) const
- {
- SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
- time_t t = time(NULL);
- return itr != m_spellCooldowns.end() && itr->second.end > t ? itr->second.end - t : 0;
- }
- void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time);
- void SendCooldownEvent(SpellEntry const *spellInfo);
- void ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs );
- void RemoveSpellCooldown(uint32 spell_id) { m_spellCooldowns.erase(spell_id); }
- void RemoveArenaSpellCooldowns();
- void RemoveAllSpellCooldown();
- void _LoadSpellCooldowns(QueryResult *result);
- void _SaveSpellCooldowns();
-
- void setResurrectRequestData(uint64 guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana)
- {
- m_resurrectGUID = guid;
- m_resurrectMap = mapId;
- m_resurrectX = X;
- m_resurrectY = Y;
- m_resurrectZ = Z;
- m_resurrectHealth = health;
- m_resurrectMana = mana;
- };
- void clearResurrectRequestData() { setResurrectRequestData(0,0,0.0f,0.0f,0.0f,0,0); }
- bool isRessurectRequestedBy(uint64 guid) const { return m_resurrectGUID == guid; }
- bool isRessurectRequested() const { return m_resurrectGUID != 0; }
- void ResurectUsingRequestData();
-
- int getCinematic()
- {
- return m_cinematic;
- }
- void setCinematic(int cine)
- {
- m_cinematic = cine;
- }
-
- void addActionButton(uint8 button, uint16 action, uint8 type, uint8 misc);
- void removeActionButton(uint8 button);
- void SendInitialActionButtons();
-
- PvPInfo pvpInfo;
- void UpdatePvP(bool state, bool ovrride=false);
- void UpdateZone(uint32 newZone);
- void UpdateArea(uint32 newArea);
-
- void UpdateZoneDependentAuras( uint32 zone_id ); // zones
- void UpdateAreaDependentAuras( uint32 area_id ); // subzones
-
- void UpdateAfkReport(time_t currTime);
- void UpdatePvPFlag(time_t currTime);
- void UpdateContestedPvP(uint32 currTime);
- void SetContestedPvPTimer(uint32 newTime) {m_contestedPvPTimer = newTime;}
- void ResetContestedPvP()
- {
- clearUnitState(UNIT_STAT_ATTACK_PLAYER);
- RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
- m_contestedPvPTimer = 0;
- }
-
- /** todo: -maybe move UpdateDuelFlag+DuelComplete to independent DuelHandler.. **/
- DuelInfo *duel;
- void UpdateDuelFlag(time_t currTime);
- void CheckDuelDistance(time_t currTime);
- void DuelComplete(DuelCompleteType type);
-
- bool IsGroupVisibleFor(Player* p) const;
- bool IsInSameGroupWith(Player const* p) const;
- bool IsInSameRaidWith(Player const* p) const { return p==this || (GetGroup() != NULL && GetGroup() == p->GetGroup()); }
- void UninviteFromGroup();
- static void RemoveFromGroup(Group* group, uint64 guid);
- void RemoveFromGroup() { RemoveFromGroup(GetGroup(),GetGUID()); }
- void SendUpdateToOutOfRangeGroupMembers();
-
- void SetInGuild(uint32 GuildId) { SetUInt32Value(PLAYER_GUILDID, GuildId); Player::SetUInt32ValueInDB(PLAYER_GUILDID, GuildId, this->GetGUID()); }
- void SetRank(uint32 rankId){ SetUInt32Value(PLAYER_GUILDRANK, rankId); Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, rankId, this->GetGUID()); }
- void SetGuildIdInvited(uint32 GuildId) { m_GuildIdInvited = GuildId; }
- uint32 GetGuildId() { return GetUInt32Value(PLAYER_GUILDID); }
- static uint32 GetGuildIdFromDB(uint64 guid);
- uint32 GetRank(){ return GetUInt32Value(PLAYER_GUILDRANK); }
- static uint32 GetRankFromDB(uint64 guid);
- int GetGuildIdInvited() { return m_GuildIdInvited; }
- static void RemovePetitionsAndSigns(uint64 guid, uint32 type);
-
- // Arena Team
- void SetInArenaTeam(uint32 ArenaTeamId, uint8 slot)
- {
- SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6), ArenaTeamId);
- SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6), ArenaTeamId, this->GetGUID());
- }
- uint32 GetArenaTeamId(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6)); }
- static uint32 GetArenaTeamIdFromDB(uint64 guid, uint8 slot);
- void SetArenaTeamIdInvited(uint32 ArenaTeamId) { m_ArenaTeamIdInvited = ArenaTeamId; }
- uint32 GetArenaTeamIdInvited() { return m_ArenaTeamIdInvited; }
-
- void SetDifficulty(uint32 dungeon_difficulty) { m_dungeonDifficulty = dungeon_difficulty; }
- uint8 GetDifficulty() { return m_dungeonDifficulty; }
-
- bool UpdateSkill(uint32 skill_id, uint32 step);
- bool UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step);
-
- bool UpdateCraftSkill(uint32 spellid);
- bool UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator = 1);
- bool UpdateFishingSkill();
-
- uint32 GetBaseDefenseSkillValue() const { return GetBaseSkillValue(SKILL_DEFENSE); }
- uint32 GetBaseWeaponSkillValue(WeaponAttackType attType) const;
-
- uint32 GetSpellByProto(ItemPrototype *proto);
-
- float GetHealthBonusFromStamina();
- float GetManaBonusFromIntellect();
-
- bool UpdateStats(Stats stat);
- bool UpdateAllStats();
- void UpdateResistances(uint32 school);
- void UpdateArmor();
- void UpdateMaxHealth();
- void UpdateMaxPower(Powers power);
- void UpdateAttackPowerAndDamage(bool ranged = false);
- void UpdateShieldBlockValue();
- void UpdateDamagePhysical(WeaponAttackType attType);
- void UpdateSpellDamageAndHealingBonus();
-
- void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage);
-
- void UpdateDefenseBonusesMod();
- void ApplyRatingMod(CombatRating cr, int32 value, bool apply);
- float GetMeleeCritFromAgility();
- float GetDodgeFromAgility();
- float GetSpellCritFromIntellect();
- float OCTRegenHPPerSpirit();
- float OCTRegenMPPerSpirit();
- float GetRatingCoefficient(CombatRating cr) const;
- float GetRatingBonusValue(CombatRating cr) const;
- uint32 GetMeleeCritDamageReduction(uint32 damage) const;
- uint32 GetRangedCritDamageReduction(uint32 damage) const;
- uint32 GetSpellCritDamageReduction(uint32 damage) const;
- uint32 GetDotDamageReduction(uint32 damage) const;
-
- float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const;
- void UpdateBlockPercentage();
- void UpdateCritPercentage(WeaponAttackType attType);
- void UpdateAllCritPercentages();
- void UpdateParryPercentage();
- void UpdateDodgePercentage();
- void UpdateAllSpellCritChances();
- void UpdateSpellCritChance(uint32 school);
- void UpdateExpertise(WeaponAttackType attType);
- void UpdateManaRegen();
-
- const uint64& GetLootGUID() const { return m_lootGuid; }
- void SetLootGUID(const uint64 &guid) { m_lootGuid = guid; }
-
- void RemovedInsignia(Player* looterPlr);
-
- WorldSession* GetSession() const { return m_session; }
- void SetSession(WorldSession *s) { m_session = s; }
-
- void BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const;
- void DestroyForPlayer( Player *target ) const;
- void SendDelayResponse(const uint32);
- void SendLogXPGain(uint32 GivenXP,Unit* victim,uint32 RestXP);
-
- //notifiers
- void SendAttackSwingCantAttack();
- void SendAttackSwingCancelAttack();
- void SendAttackSwingDeadTarget();
- void SendAttackSwingNotStanding();
- void SendAttackSwingNotInRange();
- void SendAttackSwingBadFacingAttack();
- void SendAutoRepeatCancel();
- void SendExplorationExperience(uint32 Area, uint32 Experience);
-
- void SendDungeonDifficulty(bool IsInGroup);
- void ResetInstances(uint8 method);
- void SendResetInstanceSuccess(uint32 MapId);
- void SendResetInstanceFailed(uint32 reason, uint32 MapId);
- void SendResetFailedNotify(uint32 mapid);
-
- bool SetPosition(float x, float y, float z, float orientation, bool teleport = false);
- void UpdateUnderwaterState( Map * m, float x, float y, float z );
-
- void SendMessageToSet(WorldPacket *data, bool self);// overwrite Object::SendMessageToSet
- void SendMessageToSetInRange(WorldPacket *data, float fist, bool self);
- // overwrite Object::SendMessageToSetInRange
- void SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only);
-
- static void DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars = true);
-
- Corpse *GetCorpse() const;
- void SpawnCorpseBones();
- void CreateCorpse();
- void KillPlayer();
- uint32 GetResurrectionSpellId();
- void ResurrectPlayer(float restore_percent, bool updateToWorld = true, bool applySickness = false);
- void BuildPlayerRepop();
- void RepopAtGraveyard();
-
- void DurabilityLossAll(double percent, bool inventory);
- void DurabilityLoss(Item* item, double percent);
- void DurabilityPointsLossAll(int32 points, bool inventory);
- void DurabilityPointsLoss(Item* item, int32 points);
- void DurabilityPointLossForEquipSlot(EquipmentSlots slot);
- uint32 DurabilityRepairAll(bool cost, float discountMod, bool guildBank);
- uint32 DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank);
-
- void StopMirrorTimers()
- {
- StopMirrorTimer(FATIGUE_TIMER);
- StopMirrorTimer(BREATH_TIMER);
- StopMirrorTimer(FIRE_TIMER);
- }
-
- void SetMovement(PlayerMovementType pType);
-
- void JoinedChannel(Channel *c);
- void LeftChannel(Channel *c);
- void CleanupChannels();
- void UpdateLocalChannels( uint32 newZone );
- void LeaveLFGChannel();
-
- void UpdateDefense();
- void UpdateWeaponSkill (WeaponAttackType attType);
- void UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, MeleeHitOutcome outcome, bool defence);
-
- void SetSkill(uint32 id, uint16 currVal, uint16 maxVal);
- uint16 GetMaxSkillValue(uint32 skill) const; // max + perm. bonus
- uint16 GetPureMaxSkillValue(uint32 skill) const; // max
- uint16 GetSkillValue(uint32 skill) const; // skill value + perm. bonus + temp bonus
- uint16 GetBaseSkillValue(uint32 skill) const; // skill value + perm. bonus
- uint16 GetPureSkillValue(uint32 skill) const; // skill value
- int16 GetSkillTempBonusValue(uint32 skill) const;
- bool HasSkill(uint32 skill) const;
- void learnSkillRewardedSpells( uint32 id );
- void learnSkillRewardedSpells();
-
- void SetDontMove(bool dontMove);
- bool GetDontMove() const { return m_dontMove; }
-
- void CheckExploreSystem(void);
-
- static uint32 TeamForRace(uint8 race);
- uint32 GetTeam() const { return m_team; }
- static uint32 getFactionForRace(uint8 race);
- void setFactionForRace(uint8 race);
-
- bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const;
- bool RewardPlayerAndGroupAtKill(Unit* pVictim);
-
- FactionStateList m_factions;
- ForcedReactions m_forcedReactions;
- uint32 GetDefaultReputationFlags(const FactionEntry *factionEntry) const;
- int32 GetBaseReputation(const FactionEntry *factionEntry) const;
- int32 GetReputation(uint32 faction_id) const;
- int32 GetReputation(const FactionEntry *factionEntry) const;
- ReputationRank GetReputationRank(uint32 faction) const;
- ReputationRank GetReputationRank(const FactionEntry *factionEntry) const;
- ReputationRank GetBaseReputationRank(const FactionEntry *factionEntry) const;
- ReputationRank ReputationToRank(int32 standing) const;
- const static int32 ReputationRank_Length[MAX_REPUTATION_RANK];
- const static int32 Reputation_Cap = 42999;
- const static int32 Reputation_Bottom = -42000;
- bool ModifyFactionReputation(uint32 FactionTemplateId, int32 DeltaReputation);
- bool ModifyFactionReputation(FactionEntry const* factionEntry, int32 standing);
- bool ModifyOneFactionReputation(FactionEntry const* factionEntry, int32 standing);
- bool SetFactionReputation(uint32 FactionTemplateId, int32 standing);
- bool SetFactionReputation(FactionEntry const* factionEntry, int32 standing);
- bool SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing);
- int32 CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, bool for_quest);
- void RewardReputation(Unit *pVictim, float rate);
- void RewardReputation(Quest const *pQuest);
- void SetInitialFactions();
- void UpdateReputation() const;
- void SendFactionState(FactionState const* faction) const;
- void SendInitialReputations();
- FactionState const* GetFactionState( FactionEntry const* factionEntry) const;
- void SetFactionAtWar(FactionState* faction, bool atWar);
- void SetFactionInactive(FactionState* faction, bool inactive);
- void SetFactionVisible(FactionState* faction);
- void SetFactionVisibleForFactionTemplateId(uint32 FactionTemplateId);
- void SetFactionVisibleForFactionId(uint32 FactionId);
- void UpdateMaxSkills();
- void UpdateSkillsToMaxSkillsForLevel(); // for .levelup
- void ModifySkillBonus(uint32 skillid,int32 val, bool talent);
-
- /*********************************************************/
- /*** PVP SYSTEM ***/
- /*********************************************************/
- void UpdateArenaFields();
- void UpdateHonorFields();
- bool RewardHonor(Unit *pVictim, uint32 groupsize, float honor = -1, bool pvptoken = false);
- uint32 GetHonorPoints() { return GetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY); }
- uint32 GetArenaPoints() { return GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY); }
- void ModifyHonorPoints( int32 value );
- void ModifyArenaPoints( int32 value );
- uint32 GetMaxPersonalArenaRatingRequirement();
-
- //End of PvP System
-
- void SetDrunkValue(uint16 newDrunkValue, uint32 itemid=0);
- uint16 GetDrunkValue() const { return m_drunk; }
- static DrunkenState GetDrunkenstateByValue(uint16 value);
-
- uint32 GetDeathTimer() const { return m_deathTimer; }
- uint32 GetCorpseReclaimDelay(bool pvp) const;
- void UpdateCorpseReclaimDelay();
- void SendCorpseReclaimDelay(bool load = false);
-
- uint32 GetShieldBlockValue() const; // overwrite Unit version (virtual)
- bool CanParry() const { return m_canParry; }
- void SetCanParry(bool value);
- bool CanBlock() const { return m_canBlock; }
- void SetCanBlock(bool value);
- bool CanDualWield() const { return m_canDualWield; }
- void SetCanDualWield(bool value) { m_canDualWield = value; }
-
- void SetRegularAttackTime();
- void SetBaseModValue(BaseModGroup modGroup, BaseModType modType, float value) { m_auraBaseMod[modGroup][modType] = value; }
- void HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply, bool affectStats = true);
- float GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const;
- float GetTotalBaseModValue(BaseModGroup modGroup) const;
- float GetTotalPercentageModValue(BaseModGroup modGroup) const { return m_auraBaseMod[modGroup][FLAT_MOD] + m_auraBaseMod[modGroup][PCT_MOD]; }
- void _ApplyAllStatBonuses();
- void _RemoveAllStatBonuses();
-
- void _ApplyWeaponDependentAuraMods(Item *item,WeaponAttackType attackType,bool apply);
- void _ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply);
- void _ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply);
-
- void _ApplyItemMods(Item *item,uint8 slot,bool apply);
- void _RemoveAllItemMods();
- void _ApplyAllItemMods();
- void _ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply);
- void _ApplyAmmoBonuses();
- bool EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot);
- void ToggleMetaGemsActive(uint8 exceptslot, bool apply);
- void CorrectMetaGemEnchants(uint8 slot, bool apply);
- void InitDataForForm(bool reapplyMods = false);
-
- void ApplyItemEquipSpell(Item *item, bool apply, bool form_change = false);
- void ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply, bool form_change = false);
- void UpdateEquipSpellsAtFormChange();
- void CastItemCombatSpell(Item *item,Unit* Target, WeaponAttackType attType);
-
- void SendInitWorldStates();
- void SendUpdateWorldState(uint32 Field, uint32 Value);
- void SendDirectMessage(WorldPacket *data);
-
- void SendAuraDurationsForTarget(Unit* target);
-
- PlayerMenu* PlayerTalkClass;
- std::vector<ItemSetEffect *> ItemSetEff;
-
- void SendLoot(uint64 guid, LootType loot_type);
- void SendLootRelease( uint64 guid );
- void SendNotifyLootItemRemoved(uint8 lootSlot);
- void SendNotifyLootMoneyRemoved();
-
- /*********************************************************/
- /*** BATTLEGROUND SYSTEM ***/
- /*********************************************************/
-
- bool InBattleGround() const { return m_bgBattleGroundID != 0; }
- uint32 GetBattleGroundId() const { return m_bgBattleGroundID; }
- BattleGround* GetBattleGround() const;
- bool InArena() const;
-
- static uint32 GetMinLevelForBattleGroundQueueId(uint32 queue_id);
- static uint32 GetMaxLevelForBattleGroundQueueId(uint32 queue_id);
- uint32 GetBattleGroundQueueIdFromLevel() const;
-
- bool InBattleGroundQueue() const
- {
- for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
- if (m_bgBattleGroundQueueID[i].bgQueueType != 0)
- return true;
- return false;
- }
-
- uint32 GetBattleGroundQueueId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgQueueType; }
- uint32 GetBattleGroundQueueIndex(uint32 bgQueueType) const
- {
- for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
- if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType)
- return i;
- return PLAYER_MAX_BATTLEGROUND_QUEUES;
- }
- bool IsInvitedForBattleGroundQueueType(uint32 bgQueueType) const
- {
- for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
- if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType)
- return m_bgBattleGroundQueueID[i].invitedToInstance != 0;
- return PLAYER_MAX_BATTLEGROUND_QUEUES;
- }
- bool InBattleGroundQueueForBattleGroundQueueType(uint32 bgQueueType) const
- {
- return GetBattleGroundQueueIndex(bgQueueType) < PLAYER_MAX_BATTLEGROUND_QUEUES;
- }
-
- void SetBattleGroundId(uint32 val) { m_bgBattleGroundID = val; }
- uint32 AddBattleGroundQueueId(uint32 val)
- {
- for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
- {
- if (m_bgBattleGroundQueueID[i].bgQueueType == 0 || m_bgBattleGroundQueueID[i].bgQueueType == val)
- {
- m_bgBattleGroundQueueID[i].bgQueueType = val;
- m_bgBattleGroundQueueID[i].invitedToInstance = 0;
- return i;
- }
- }
- return PLAYER_MAX_BATTLEGROUND_QUEUES;
- }
- bool HasFreeBattleGroundQueueId()
- {
- for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
- if (m_bgBattleGroundQueueID[i].bgQueueType == 0)
- return true;
- return false;
- }
- void RemoveBattleGroundQueueId(uint32 val)
- {
- for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
- {
- if (m_bgBattleGroundQueueID[i].bgQueueType == val)
- {
- m_bgBattleGroundQueueID[i].bgQueueType = 0;
- m_bgBattleGroundQueueID[i].invitedToInstance = 0;
- return;
- }
- }
- }
- void SetInviteForBattleGroundQueueType(uint32 bgQueueType, uint32 instanceId)
- {
- for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
- if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType)
- m_bgBattleGroundQueueID[i].invitedToInstance = instanceId;
- }
- bool IsInvitedForBattleGroundInstance(uint32 instanceId) const
- {
- for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
- if (m_bgBattleGroundQueueID[i].invitedToInstance == instanceId)
- return true;
- return false;
- }
- uint32 GetBattleGroundEntryPointMap() const { return m_bgEntryPointMap; }
- float GetBattleGroundEntryPointX() const { return m_bgEntryPointX; }
- float GetBattleGroundEntryPointY() const { return m_bgEntryPointY; }
- float GetBattleGroundEntryPointZ() const { return m_bgEntryPointZ; }
- float GetBattleGroundEntryPointO() const { return m_bgEntryPointO; }
- void SetBattleGroundEntryPoint(uint32 Map, float PosX, float PosY, float PosZ, float PosO )
- {
- m_bgEntryPointMap = Map;
- m_bgEntryPointX = PosX;
- m_bgEntryPointY = PosY;
- m_bgEntryPointZ = PosZ;
- m_bgEntryPointO = PosO;
- }
-
- void SetBGTeam(uint32 team) { m_bgTeam = team; }
- uint32 GetBGTeam() const { return m_bgTeam ? m_bgTeam : GetTeam(); }
-
- void LeaveBattleground(bool teleportToEntryPoint = true);
- bool CanJoinToBattleground() const;
- bool CanReportAfkDueToLimit();
- void ReportedAfkBy(Player* reporter);
- void ClearAfkReports() { m_bgAfkReporter.clear(); }
-
- bool GetBGAccessByLevel(uint32 bgTypeId) const;
- bool isAllowUseBattleGroundObject();
-
- /*********************************************************/
- /*** REST SYSTEM ***/
- /*********************************************************/
-
- bool isRested() const { return GetRestTime() >= 10000; }
- uint32 GetXPRestBonus(uint32 xp);
- uint32 GetRestTime() const { return m_restTime;};
- void SetRestTime(uint32 v) { m_restTime = v;};
-
- /*********************************************************/
- /*** ENVIROMENTAL SYSTEM ***/
- /*********************************************************/
-
- void EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 damage);
-
- /*********************************************************/
- /*** FLOOD FILTER SYSTEM ***/
- /*********************************************************/
-
- void UpdateSpeakTime();
- bool CanSpeak() const;
- void ChangeSpeakTime(int utime);
-
- /*********************************************************/
- /*** VARIOUS SYSTEMS ***/
- /*********************************************************/
- MovementInfo m_movementInfo;
- bool isMoving() const { return HasUnitMovementFlag(movementFlagsMask); }
- bool isMovingOrTurning() const { return HasUnitMovementFlag(movementOrTurningFlagsMask); }
-
- bool CanFly() const { return HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); }
- bool IsFlying() const { return HasUnitMovementFlag(MOVEMENTFLAG_FLYING); }
-
- void HandleDrowning();
-
- void SetClientControl(Unit* target, uint8 allowMove);
-
- // Transports
- Transport * GetTransport() const { return m_transport; }
- void SetTransport(Transport * t) { m_transport = t; }
-
- float GetTransOffsetX() const { return m_movementInfo.t_x; }
- float GetTransOffsetY() const { return m_movementInfo.t_y; }
- float GetTransOffsetZ() const { return m_movementInfo.t_z; }
- float GetTransOffsetO() const { return m_movementInfo.t_o; }
- uint32 GetTransTime() const { return m_movementInfo.t_time; }
-
- uint32 GetSaveTimer() const { return m_nextSave; }
- void SetSaveTimer(uint32 timer) { m_nextSave = timer; }
-
- // Recall position
- uint32 m_recallMap;
- float m_recallX;
- float m_recallY;
- float m_recallZ;
- float m_recallO;
- void SaveRecallPosition();
-
- // Homebind coordinates
- uint32 m_homebindMapId;
- uint16 m_homebindZoneId;
- float m_homebindX;
- float m_homebindY;
- float m_homebindZ;
-
- // currently visible objects at player client
- typedef std::set<uint64> ClientGUIDs;
- ClientGUIDs m_clientGUIDs;
-
- bool HaveAtClient(WorldObject const* u) { return u==this || m_clientGUIDs.find(u->GetGUID())!=m_clientGUIDs.end(); }
-
- bool IsVisibleInGridForPlayer(Player* pl) const;
- bool IsVisibleGloballyFor(Player* pl) const;
-
- void UpdateVisibilityOf(WorldObject* target);
-
- template<class T>
- void UpdateVisibilityOf(T* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
-
- // Stealth detection system
- uint32 m_DetectInvTimer;
- void HandleStealthedUnitsDetection();
-
- uint8 m_forced_speed_changes[MAX_MOVE_TYPE];
-
- bool HasAtLoginFlag(AtLoginFlags f) const { return m_atLoginFlags & f; }
- void SetAtLoginFlag(AtLoginFlags f) { m_atLoginFlags |= f; }
-
- LookingForGroup m_lookingForGroup;
-
- // Temporarily removed pet cache
- uint32 GetTemporaryUnsummonedPetNumber() const { return m_temporaryUnsummonedPetNumber; }
- void SetTemporaryUnsummonedPetNumber(uint32 petnumber) { m_temporaryUnsummonedPetNumber = petnumber; }
- uint32 GetOldPetSpell() const { return m_oldpetspell; }
- void SetOldPetSpell(uint32 petspell) { m_oldpetspell = petspell; }
-
- /*********************************************************/
- /*** INSTANCE SYSTEM ***/
- /*********************************************************/
-
- typedef HM_NAMESPACE::hash_map< uint32 /*mapId*/, InstancePlayerBind > BoundInstancesMap;
-
- void UpdateHomebindTime(uint32 time);
-
- uint32 m_HomebindTimer;
- bool m_InstanceValid;
- // permanent binds and solo binds by difficulty
- BoundInstancesMap m_boundInstances[TOTAL_DIFFICULTIES];
- InstancePlayerBind* GetBoundInstance(uint32 mapid, uint8 difficulty);
- BoundInstancesMap& GetBoundInstances(uint8 difficulty) { return m_boundInstances[difficulty]; }
- void UnbindInstance(uint32 mapid, uint8 difficulty, bool unload = false);
- void UnbindInstance(BoundInstancesMap::iterator &itr, uint8 difficulty, bool unload = false);
- InstancePlayerBind* BindToInstance(InstanceSave *save, bool permanent, bool load = false);
- void SendRaidInfo();
- void SendSavedInstances();
- static void ConvertInstancesToGroup(Player *player, Group *group = NULL, uint64 player_guid = 0);
-
- /*********************************************************/
- /*** GROUP SYSTEM ***/
- /*********************************************************/
-
- Group * GetGroupInvite() { return m_groupInvite; }
- void SetGroupInvite(Group *group) { m_groupInvite = group; }
- Group * GetGroup() { return m_group.getTarget(); }
- const Group * GetGroup() const { return (const Group*)m_group.getTarget(); }
- GroupReference& GetGroupRef() { return m_group; }
- void SetGroup(Group *group, int8 subgroup = -1);
- uint8 GetSubGroup() const { return m_group.getSubGroup(); }
- uint32 GetGroupUpdateFlag() { return m_groupUpdateMask; }
- void SetGroupUpdateFlag(uint32 flag) { m_groupUpdateMask |= flag; }
- uint64 GetAuraUpdateMask() { return m_auraUpdateMask; }
- void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); }
- Player* GetNextRandomRaidMember(float radius);
-
- GridReference<Player> &GetGridRef() { return m_gridRef; }
- bool isAllowedToLoot(Creature* creature);
-
- WorldLocation& GetTeleportDest() { return m_teleport_dest; }
-
- DeclinedName const* GetDeclinedNames() const { return m_declinedname; }
-
- protected:
-
- /*********************************************************/
- /*** BATTLEGROUND SYSTEM ***/
- /*********************************************************/
-
- /* this variable is set to bg->m_InstanceID, when player is teleported to BG - (it is battleground's GUID)*/
- uint32 m_bgBattleGroundID;
- /*
- this is an array of BG queues (BgTypeIDs) in which is player
- */
- struct BgBattleGroundQueueID_Rec
- {
- uint32 bgQueueType;
- uint32 invitedToInstance;
- };
- BgBattleGroundQueueID_Rec m_bgBattleGroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES];
- uint32 m_bgEntryPointMap;
- float m_bgEntryPointX;
- float m_bgEntryPointY;
- float m_bgEntryPointZ;
- float m_bgEntryPointO;
-
- std::set<uint32> m_bgAfkReporter;
- uint8 m_bgAfkReportedCount;
- time_t m_bgAfkReportedTimer;
- uint32 m_contestedPvPTimer;
-
- uint32 m_bgTeam; // what side the player will be added to
-
- /*********************************************************/
- /*** QUEST SYSTEM ***/
- /*********************************************************/
-
- std::set<uint32> m_timedquests;
-
- uint64 m_divider;
- uint32 m_ingametime;
-
- /*********************************************************/
- /*** LOAD SYSTEM ***/
- /*********************************************************/
-
- void _LoadActions(QueryResult *result);
- void _LoadAuras(QueryResult *result, uint32 timediff);
- void _LoadBoundInstances(QueryResult *result);
- void _LoadInventory(QueryResult *result, uint32 timediff);
- void _LoadMailInit(QueryResult *resultUnread, QueryResult *resultDelivery);
- void _LoadMail();
- void _LoadMailedItems(Mail *mail);
- void _LoadQuestStatus(QueryResult *result);
- void _LoadDailyQuestStatus(QueryResult *result);
- void _LoadGroup(QueryResult *result);
- void _LoadReputation(QueryResult *result);
- void _LoadSpells(QueryResult *result);
- void _LoadTutorials(QueryResult *result);
- void _LoadFriendList(QueryResult *result);
- bool _LoadHomeBind(QueryResult *result);
- void _LoadDeclinedNames(QueryResult *result);
-
- /*********************************************************/
- /*** SAVE SYSTEM ***/
- /*********************************************************/
-
- void _SaveActions();
- void _SaveAuras();
- void _SaveInventory();
- void _SaveMail();
- void _SaveQuestStatus();
- void _SaveDailyQuestStatus();
- void _SaveReputation();
- void _SaveSpells();
- void _SaveTutorials();
-
- void _SetCreateBits(UpdateMask *updateMask, Player *target) const;
- void _SetUpdateBits(UpdateMask *updateMask, Player *target) const;
-
- /*********************************************************/
- /*** ENVIRONMENTAL SYSTEM ***/
- /*********************************************************/
- void HandleLava();
- void HandleSobering();
- void StartMirrorTimer(MirrorTimerType Type, uint32 MaxValue);
- void ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, uint32 Regen);
- void StopMirrorTimer(MirrorTimerType Type);
- uint8 m_isunderwater;
- bool m_isInWater;
-
- /*********************************************************/
- /*** HONOR SYSTEM ***/
- /*********************************************************/
- time_t m_lastHonorUpdateTime;
-
- void outDebugValues() const;
- bool _removeSpell(uint16 spell_id);
- uint64 m_lootGuid;
-
- uint32 m_race;
- uint32 m_class;
- uint32 m_team;
- uint32 m_nextSave;
- time_t m_speakTime;
- uint32 m_speakCount;
- uint32 m_dungeonDifficulty;
-
- uint32 m_atLoginFlags;
-
- Item* m_items[PLAYER_SLOTS_COUNT];
- uint32 m_currentBuybackSlot;
-
- std::vector<Item*> m_itemUpdateQueue;
- bool m_itemUpdateQueueBlocked;
-
- uint32 m_ExtraFlags;
- uint64 m_curSelection;
-
- uint64 m_comboTarget;
- int8 m_comboPoints;
-
- QuestStatusMap mQuestStatus;
-
- uint32 m_GuildIdInvited;
- uint32 m_ArenaTeamIdInvited;
-
- PlayerMails m_mail;
- PlayerSpellMap m_spells;
- SpellCooldowns m_spellCooldowns;
-
- ActionButtonList m_actionButtons;
-
- float m_auraBaseMod[BASEMOD_END][MOD_END];
-
- SpellModList m_spellMods[MAX_SPELLMOD];
- int32 m_SpellModRemoveCount;
- EnchantDurationList m_enchantDuration;
- ItemDurationList m_itemDuration;
-
- uint64 m_resurrectGUID;
- uint32 m_resurrectMap;
- float m_resurrectX, m_resurrectY, m_resurrectZ;
- uint32 m_resurrectHealth, m_resurrectMana;
-
- WorldSession *m_session;
-
- typedef std::list<Channel*> JoinedChannelsList;
- JoinedChannelsList m_channels;
-
- bool m_dontMove;
-
- int m_cinematic;
-
- Player *pTrader;
- bool acceptTrade;
- uint16 tradeItems[TRADE_SLOT_COUNT];
- uint32 tradeGold;
-
- time_t m_nextThinkTime;
-
- uint32 m_Tutorials[8];
- bool m_TutorialsChanged;
-
- bool m_DailyQuestChanged;
- time_t m_lastDailyQuestTime;
-
- uint32 m_regenTimer;
- uint32 m_breathTimer;
- uint32 m_drunkTimer;
- uint16 m_drunk;
- uint32 m_weaponChangeTimer;
-
- uint32 m_zoneUpdateId;
- uint32 m_zoneUpdateTimer;
- uint32 m_areaUpdateId;
-
- uint32 m_deathTimer;
- time_t m_deathExpireTime;
-
- uint32 m_restTime;
-
- uint32 m_WeaponProficiency;
- uint32 m_ArmorProficiency;
- bool m_canParry;
- bool m_canBlock;
- bool m_canDualWield;
- uint8 m_swingErrorMsg;
- float m_ammoDPS;
- ////////////////////Rest System/////////////////////
- int time_inn_enter;
- uint32 inn_pos_mapid;
- float inn_pos_x;
- float inn_pos_y;
- float inn_pos_z;
- float m_rest_bonus;
- RestType rest_type;
- ////////////////////Rest System/////////////////////
-
- // Transports
- Transport * m_transport;
-
- uint32 m_resetTalentsCost;
- time_t m_resetTalentsTime;
- uint32 m_usedTalentCount;
-
- // Social
- PlayerSocial *m_social;
-
- // Groups
- GroupReference m_group;
- Group *m_groupInvite;
- uint32 m_groupUpdateMask;
- uint64 m_auraUpdateMask;
-
- // Temporarily removed pet cache
- uint32 m_temporaryUnsummonedPetNumber;
- uint32 m_oldpetspell;
-
- uint64 m_miniPet;
- GuardianPetList m_guardianPets;
-
- // Player summoning
- time_t m_summon_expire;
- uint32 m_summon_mapid;
- float m_summon_x;
- float m_summon_y;
- float m_summon_z;
-
- // Far Teleport
- WorldLocation m_teleport_dest;
-
- DeclinedName *m_declinedname;
- private:
- // internal common parts for CanStore/StoreItem functions
- uint8 _CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool swap, Item *pSrcItem ) const;
- uint8 _CanStoreItem_InBag( uint8 bag, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot ) const;
- uint8 _CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot ) const;
- Item* _StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, bool update );
-
- GridReference<Player> m_gridRef;
-};
-
-void AddItemsSetItem(Player*player,Item *item);
-void RemoveItemsSetItem(Player*player,ItemPrototype const *proto);
-
-// "the bodies of template functions must be made available in a header file"
-template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell const* spell)
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
- if (!spellInfo) return 0;
- int32 totalpct = 0;
- int32 totalflat = 0;
- for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr)
- {
- SpellModifier *mod = *itr;
-
- if(!IsAffectedBySpellmod(spellInfo,mod,spell))
- continue;
- if (mod->type == SPELLMOD_FLAT)
- totalflat += mod->value;
- else if (mod->type == SPELLMOD_PCT)
- {
- // skip percent mods for null basevalue (most important for spell mods with charges )
- if(basevalue == T(0))
- continue;
-
- // special case (skip >10sec spell casts for instant cast setting)
- if( mod->op==SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100)
- continue;
-
- totalpct += mod->value;
- }
-
- if (mod->charges > 0 )
- {
- --mod->charges;
- if (mod->charges == 0)
- {
- mod->charges = -1;
- mod->lastAffected = spell;
- if(!mod->lastAffected)
- mod->lastAffected = FindCurrentSpellBySpellId(spellId);
- ++m_SpellModRemoveCount;
- }
- }
- }
-
- float diff = (float)basevalue*(float)totalpct/100.0f + (float)totalflat;
- basevalue = T((float)basevalue + diff);
- return T(diff);
-}
-#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
+ */
+
+#ifndef _PLAYER_H
+#define _PLAYER_H
+
+#include "Common.h"
+#include "ItemPrototype.h"
+#include "Unit.h"
+#include "Item.h"
+
+#include "Database/DatabaseEnv.h"
+#include "NPCHandler.h"
+#include "QuestDef.h"
+#include "Group.h"
+#include "Bag.h"
+#include "WorldSession.h"
+#include "Pet.h"
+#include "Util.h" // for Tokens typedef
+
+#include<string>
+#include<vector>
+
+struct Mail;
+class Channel;
+class DynamicObject;
+class Creature;
+class Pet;
+class PlayerMenu;
+class Transport;
+class UpdateMask;
+class PlayerSocial;
+
+typedef std::deque<Mail*> PlayerMails;
+
+#define PLAYER_MAX_SKILLS 127
+#define PLAYER_MAX_DAILY_QUESTS 25
+
+// Note: SPELLMOD_* values is aura types in fact
+enum SpellModType
+{
+ SPELLMOD_FLAT = 107, // SPELL_AURA_ADD_FLAT_MODIFIER
+ SPELLMOD_PCT = 108 // SPELL_AURA_ADD_PCT_MODIFIER
+};
+
+enum PlayerSpellState
+{
+ PLAYERSPELL_UNCHANGED = 0,
+ PLAYERSPELL_CHANGED = 1,
+ PLAYERSPELL_NEW = 2,
+ PLAYERSPELL_REMOVED = 3
+};
+
+struct PlayerSpell
+{
+ uint16 slotId : 16;
+ PlayerSpellState state : 8;
+ bool active : 1;
+ bool disabled : 1;
+};
+
+#define SPELL_WITHOUT_SLOT_ID uint16(-1)
+
+struct SpellModifier
+{
+ SpellModOp op : 8;
+ SpellModType type : 8;
+ int16 charges : 16;
+ int32 value;
+ uint64 mask;
+ uint32 spellId;
+ uint32 effectId;
+ Spell const* lastAffected;
+};
+
+typedef HM_NAMESPACE::hash_map<uint16, PlayerSpell*> PlayerSpellMap;
+typedef std::list<SpellModifier*> SpellModList;
+
+struct SpellCooldown
+{
+ time_t end;
+ uint16 itemid;
+};
+
+typedef std::map<uint32, SpellCooldown> SpellCooldowns;
+
+enum TrainerSpellState
+{
+ TRAINER_SPELL_GREEN = 0,
+ TRAINER_SPELL_RED = 1,
+ TRAINER_SPELL_GRAY = 2
+};
+
+enum ActionButtonUpdateState
+{
+ ACTIONBUTTON_UNCHANGED = 0,
+ ACTIONBUTTON_CHANGED = 1,
+ ACTIONBUTTON_NEW = 2,
+ ACTIONBUTTON_DELETED = 3
+};
+
+struct ActionButton
+{
+ ActionButton() : action(0), type(0), misc(0), uState( ACTIONBUTTON_NEW ) {}
+ ActionButton(uint16 _action, uint8 _type, uint8 _misc) : action(_action), type(_type), misc(_misc), uState( ACTIONBUTTON_NEW ) {}
+
+ uint16 action;
+ uint8 type;
+ uint8 misc;
+ ActionButtonUpdateState uState;
+};
+
+enum ActionButtonType
+{
+ ACTION_BUTTON_SPELL = 0,
+ ACTION_BUTTON_MACRO = 64,
+ ACTION_BUTTON_CMACRO= 65,
+ ACTION_BUTTON_ITEM = 128
+};
+
+#define MAX_ACTION_BUTTONS 132 //checked in 2.3.0
+
+typedef std::map<uint8,ActionButton> ActionButtonList;
+
+typedef std::pair<uint16, uint8> CreateSpellPair;
+
+struct PlayerCreateInfoItem
+{
+ PlayerCreateInfoItem(uint32 id, uint32 amount) : item_id(id), item_amount(amount) {}
+
+ uint32 item_id;
+ uint32 item_amount;
+};
+
+typedef std::list<PlayerCreateInfoItem> PlayerCreateInfoItems;
+
+struct PlayerClassLevelInfo
+{
+ PlayerClassLevelInfo() : basehealth(0), basemana(0) {}
+ uint16 basehealth;
+ uint16 basemana;
+};
+
+struct PlayerClassInfo
+{
+ PlayerClassInfo() : levelInfo(NULL) { }
+
+ PlayerClassLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
+};
+
+struct PlayerLevelInfo
+{
+ PlayerLevelInfo() { for(int i=0; i < MAX_STATS; ++i ) stats[i] = 0; }
+
+ uint8 stats[MAX_STATS];
+};
+
+struct PlayerInfo
+{
+ // existence checked by displayId != 0 // existence checked by displayId != 0
+ PlayerInfo() : displayId_m(0),displayId_f(0),levelInfo(NULL)
+ {
+ }
+
+ uint32 mapId;
+ uint32 zoneId;
+ float positionX;
+ float positionY;
+ float positionZ;
+ uint16 displayId_m;
+ uint16 displayId_f;
+ PlayerCreateInfoItems item;
+ std::list<CreateSpellPair> spell;
+ std::list<uint16> action[4];
+
+ PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
+};
+
+struct PvPInfo
+{
+ PvPInfo() : inHostileArea(false), endTimer(0) {}
+
+ bool inHostileArea;
+ time_t endTimer;
+};
+
+struct DuelInfo
+{
+ DuelInfo() : initiator(NULL), opponent(NULL), startTimer(0), startTime(0), outOfBound(0) {}
+
+ Player *initiator;
+ Player *opponent;
+ time_t startTimer;
+ time_t startTime;
+ time_t outOfBound;
+};
+
+struct Areas
+{
+ uint32 areaID;
+ uint32 areaFlag;
+ float x1;
+ float x2;
+ float y1;
+ float y2;
+};
+
+enum FactionFlags
+{
+ FACTION_FLAG_VISIBLE = 0x01, // makes visible in client (set or can be set at interaction with target of this faction)
+ FACTION_FLAG_AT_WAR = 0x02, // enable AtWar-button in client. player controlled (except opposition team always war state), Flag only set on initial creation
+ FACTION_FLAG_HIDDEN = 0x04, // hidden faction from reputation pane in client (player can gain reputation, but this update not sent to client)
+ FACTION_FLAG_INVISIBLE_FORCED = 0x08, // always overwrite FACTION_FLAG_VISIBLE and hide faction in rep.list, used for hide opposite team factions
+ FACTION_FLAG_PEACE_FORCED = 0x10, // always overwrite FACTION_FLAG_AT_WAR, used for prevent war with own team factions
+ FACTION_FLAG_INACTIVE = 0x20, // player controlled, state stored in characters.data ( CMSG_SET_FACTION_INACTIVE )
+ FACTION_FLAG_RIVAL = 0x40 // flag for the two competing outland factions
+};
+
+typedef uint32 RepListID;
+struct FactionState
+{
+ uint32 ID;
+ RepListID ReputationListID;
+ uint32 Flags;
+ int32 Standing;
+ bool Changed;
+};
+
+typedef std::map<RepListID,FactionState> FactionStateList;
+
+typedef std::map<uint32,ReputationRank> ForcedReactions;
+
+typedef std::set<uint64> GuardianPetList;
+
+struct EnchantDuration
+{
+ EnchantDuration() : item(NULL), slot(MAX_ENCHANTMENT_SLOT), leftduration(0) {};
+ EnchantDuration(Item * _item, EnchantmentSlot _slot, uint32 _leftduration) : item(_item), slot(_slot), leftduration(_leftduration) { assert(item); };
+
+ Item * item;
+ EnchantmentSlot slot;
+ uint32 leftduration;
+};
+
+typedef std::list<EnchantDuration> EnchantDurationList;
+typedef std::list<Item*> ItemDurationList;
+
+struct LookingForGroupSlot
+{
+ LookingForGroupSlot() : entry(0), type(0) {}
+ bool Empty() const { return !entry && !type; }
+ void Clear() { entry = 0; type = 0; }
+ void Set(uint32 _entry, uint32 _type ) { entry = _entry; type = _type; }
+ bool Is(uint32 _entry, uint32 _type) const { return entry==_entry && type==_type; }
+ bool canAutoJoin() const { return entry && (type == 1 || type == 5); }
+
+ uint32 entry;
+ uint32 type;
+};
+
+#define MAX_LOOKING_FOR_GROUP_SLOT 3
+
+struct LookingForGroup
+{
+ LookingForGroup() {}
+ bool HaveInSlot(LookingForGroupSlot const& slot) const { return HaveInSlot(slot.entry,slot.type); }
+ bool HaveInSlot(uint32 _entry, uint32 _type) const
+ {
+ for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
+ if(slots[i].Is(_entry,_type))
+ return true;
+ return false;
+ }
+
+ bool canAutoJoin() const
+ {
+ for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
+ if(slots[i].canAutoJoin())
+ return true;
+ return false;
+ }
+
+ bool Empty() const
+ {
+ for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
+ if(!slots[i].Empty())
+ return false;
+ return more.Empty();
+ }
+
+ LookingForGroupSlot slots[MAX_LOOKING_FOR_GROUP_SLOT];
+ LookingForGroupSlot more;
+ std::string comment;
+};
+
+enum PlayerMovementType
+{
+ MOVE_ROOT = 1,
+ MOVE_UNROOT = 2,
+ MOVE_WATER_WALK = 3,
+ MOVE_LAND_WALK = 4
+};
+
+enum DrunkenState
+{
+ DRUNKEN_SOBER = 0,
+ DRUNKEN_TIPSY = 1,
+ DRUNKEN_DRUNK = 2,
+ DRUNKEN_SMASHED = 3
+};
+
+enum PlayerStateType
+{
+ /*
+ PLAYER_STATE_DANCE
+ PLAYER_STATE_SLEEP
+ PLAYER_STATE_SIT
+ PLAYER_STATE_STAND
+ PLAYER_STATE_READYUNARMED
+ PLAYER_STATE_WORK
+ PLAYER_STATE_POINT(DNR)
+ PLAYER_STATE_NONE // not used or just no state, just standing there?
+ PLAYER_STATE_STUN
+ PLAYER_STATE_DEAD
+ PLAYER_STATE_KNEEL
+ PLAYER_STATE_USESTANDING
+ PLAYER_STATE_STUN_NOSHEATHE
+ PLAYER_STATE_USESTANDING_NOSHEATHE
+ PLAYER_STATE_WORK_NOSHEATHE
+ PLAYER_STATE_SPELLPRECAST
+ PLAYER_STATE_READYRIFLE
+ PLAYER_STATE_WORK_NOSHEATHE_MINING
+ PLAYER_STATE_WORK_NOSHEATHE_CHOPWOOD
+ PLAYER_STATE_AT_EASE
+ PLAYER_STATE_READY1H
+ PLAYER_STATE_SPELLKNEELSTART
+ PLAYER_STATE_SUBMERGED
+ */
+
+ PLAYER_STATE_NONE = 0,
+ PLAYER_STATE_SIT = 1,
+ PLAYER_STATE_SIT_CHAIR = 2,
+ PLAYER_STATE_SLEEP = 3,
+ PLAYER_STATE_SIT_LOW_CHAIR = 4,
+ PLAYER_STATE_SIT_MEDIUM_CHAIR = 5,
+ PLAYER_STATE_SIT_HIGH_CHAIR = 6,
+ PLAYER_STATE_DEAD = 7,
+ PLAYER_STATE_KNEEL = 8,
+
+ PLAYER_STATE_FORM_ALL = 0x00FF0000,
+
+ PLAYER_STATE_FLAG_ALWAYS_STAND = 0x01, // byte 4
+ PLAYER_STATE_FLAG_CREEP = 0x02000000,
+ PLAYER_STATE_FLAG_UNTRACKABLE = 0x04000000,
+ PLAYER_STATE_FLAG_ALL = 0xFF000000,
+};
+
+enum PlayerFlags
+{
+ PLAYER_FLAGS_GROUP_LEADER = 0x00000001,
+ PLAYER_FLAGS_AFK = 0x00000002,
+ PLAYER_FLAGS_DND = 0x00000004,
+ PLAYER_FLAGS_GM = 0x00000008,
+ PLAYER_FLAGS_GHOST = 0x00000010,
+ PLAYER_FLAGS_RESTING = 0x00000020,
+ PLAYER_FLAGS_FFA_PVP = 0x00000080,
+ PLAYER_FLAGS_CONTESTED_PVP = 0x00000100, // Player has been involved in a PvP combat and will be attacked by contested guards
+ PLAYER_FLAGS_IN_PVP = 0x00000200,
+ PLAYER_FLAGS_HIDE_HELM = 0x00000400,
+ PLAYER_FLAGS_HIDE_CLOAK = 0x00000800,
+ PLAYER_FLAGS_UNK1 = 0x00001000, // played long time
+ PLAYER_FLAGS_UNK2 = 0x00002000, // played too long time
+ PLAYER_FLAGS_UNK3 = 0x00008000, // strange visual effect (2.0.1), looks like PLAYER_FLAGS_GHOST flag
+ PLAYER_FLAGS_SANCTUARY = 0x00010000, // player entered sanctuary
+ PLAYER_FLAGS_UNK4 = 0x00020000, // taxi benchmark mode (on/off) (2.0.1)
+ PLAYER_UNK = 0x00040000, // 2.0.8...
+};
+
+// used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1<<bit_index) without (-1)
+// can't use enum for uint64 values
+#define PLAYER_TITLE_DISABLED 0x0000000000000000LL
+#define PLAYER_TITLE_NONE 0x0000000000000001LL
+#define PLAYER_TITLE_PRIVATE 0x0000000000000002LL // 1
+#define PLAYER_TITLE_CORPORAL 0x0000000000000004LL // 2
+#define PLAYER_TITLE_SERGEANT_A 0x0000000000000008LL // 3
+#define PLAYER_TITLE_MASTER_SERGEANT 0x0000000000000010LL // 4
+#define PLAYER_TITLE_SERGEANT_MAJOR 0x0000000000000020LL // 5
+#define PLAYER_TITLE_KNIGHT 0x0000000000000040LL // 6
+#define PLAYER_TITLE_KNIGHT_LIEUTENANT 0x0000000000000080LL // 7
+#define PLAYER_TITLE_KNIGHT_CAPTAIN 0x0000000000000100LL // 8
+#define PLAYER_TITLE_KNIGHT_CHAMPION 0x0000000000000200LL // 9
+#define PLAYER_TITLE_LIEUTENANT_COMMANDER 0x0000000000000400LL // 10
+#define PLAYER_TITLE_COMMANDER 0x0000000000000800LL // 11
+#define PLAYER_TITLE_MARSHAL 0x0000000000001000LL // 12
+#define PLAYER_TITLE_FIELD_MARSHAL 0x0000000000002000LL // 13
+#define PLAYER_TITLE_GRAND_MARSHAL 0x0000000000004000LL // 14
+#define PLAYER_TITLE_SCOUT 0x0000000000008000LL // 15
+#define PLAYER_TITLE_GRUNT 0x0000000000010000LL // 16
+#define PLAYER_TITLE_SERGEANT_H 0x0000000000020000LL // 17
+#define PLAYER_TITLE_SENIOR_SERGEANT 0x0000000000040000LL // 18
+#define PLAYER_TITLE_FIRST_SERGEANT 0x0000000000080000LL // 19
+#define PLAYER_TITLE_STONE_GUARD 0x0000000000100000LL // 20
+#define PLAYER_TITLE_BLOOD_GUARD 0x0000000000200000LL // 21
+#define PLAYER_TITLE_LEGIONNAIRE 0x0000000000400000LL // 22
+#define PLAYER_TITLE_CENTURION 0x0000000000800000LL // 23
+#define PLAYER_TITLE_CHAMPION 0x0000000001000000LL // 24
+#define PLAYER_TITLE_LIEUTENANT_GENERAL 0x0000000002000000LL // 25
+#define PLAYER_TITLE_GENERAL 0x0000000004000000LL // 26
+#define PLAYER_TITLE_WARLORD 0x0000000008000000LL // 27
+#define PLAYER_TITLE_HIGH_WARLORD 0x0000000010000000LL // 28
+#define PLAYER_TITLE_GLADIATOR 0x0000000020000000LL // 29
+#define PLAYER_TITLE_DUELIST 0x0000000040000000LL // 30
+#define PLAYER_TITLE_RIVAL 0x0000000080000000LL // 31
+#define PLAYER_TITLE_CHALLENGER 0x0000000100000000LL // 32
+#define PLAYER_TITLE_SCARAB_LORD 0x0000000200000000LL // 33
+#define PLAYER_TITLE_CONQUEROR 0x0000000400000000LL // 34
+#define PLAYER_TITLE_JUSTICAR 0x0000000800000000LL // 35
+#define PLAYER_TITLE_CHAMPION_OF_THE_NAARU 0x0000001000000000LL // 36
+#define PLAYER_TITLE_MERCILESS_GLADIATOR 0x0000002000000000LL // 37
+#define PLAYER_TITLE_OF_THE_SHATTERED_SUN 0x0000004000000000LL // 38
+#define PLAYER_TITLE_HAND_OF_ADAL 0x0000008000000000LL // 39
+#define PLAYER_TITLE_VENGEFUL_GLADIATOR 0x0000010000000000LL // 40
+
+// used in PLAYER_FIELD_BYTES values
+enum PlayerFieldByteFlags
+{
+ PLAYER_FIELD_BYTE_TRACK_STEALTHED = 0x00000002,
+ PLAYER_FIELD_BYTE_RELEASE_TIMER = 0x00000008, // Display time till auto release spirit
+ PLAYER_FIELD_BYTE_NO_RELEASE_WINDOW = 0x00000010 // Display no "release spirit" window at all
+};
+
+// used in PLAYER_FIELD_BYTES2 values
+enum PlayerFieldByte2Flags
+{
+ PLAYER_FIELD_BYTE2_NONE = 0x0000,
+ PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW = 0x4000
+};
+
+enum ActivateTaxiReplies
+{
+ ERR_TAXIOK = 0,
+ ERR_TAXIUNSPECIFIEDSERVERERROR = 1,
+ ERR_TAXINOSUCHPATH = 2,
+ ERR_TAXINOTENOUGHMONEY = 3,
+ ERR_TAXITOOFARAWAY = 4,
+ ERR_TAXINOVENDORNEARBY = 5,
+ ERR_TAXINOTVISITED = 6,
+ ERR_TAXIPLAYERBUSY = 7,
+ ERR_TAXIPLAYERALREADYMOUNTED = 8,
+ ERR_TAXIPLAYERSHAPESHIFTED = 9,
+ ERR_TAXIPLAYERMOVING = 10,
+ ERR_TAXISAMENODE = 11,
+ ERR_TAXINOTSTANDING = 12
+};
+
+enum LootType
+{
+ LOOT_CORPSE = 1,
+ LOOT_SKINNING = 2,
+ LOOT_FISHING = 3,
+ LOOT_PICKPOCKETING = 4, // unsupported by client, sending LOOT_SKINNING instead
+ LOOT_DISENCHANTING = 5, // unsupported by client, sending LOOT_SKINNING instead
+ LOOT_PROSPECTING = 6, // unsupported by client, sending LOOT_SKINNING instead
+ LOOT_INSIGNIA = 7, // unsupported by client, sending LOOT_SKINNING instead
+ LOOT_FISHINGHOLE = 8 // unsupported by client, sending LOOT_FISHING instead
+};
+
+enum MirrorTimerType
+{
+ FATIGUE_TIMER = 0,
+ BREATH_TIMER = 1,
+ FIRE_TIMER = 2
+};
+
+// 2^n values
+enum PlayerExtraFlags
+{
+ // gm abilities
+ PLAYER_EXTRA_GM_ON = 0x0001,
+ PLAYER_EXTRA_GM_ACCEPT_TICKETS = 0x0002,
+ 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.
+};
+
+// 2^n values
+enum AtLoginFlags
+{
+ AT_LOGIN_NONE = 0,
+ AT_LOGIN_RENAME = 1,
+ AT_LOGIN_RESET_SPELLS = 2,
+ AT_LOGIN_RESET_TALENTS = 4
+};
+
+typedef std::map<uint32, QuestStatusData> QuestStatusMap;
+
+enum QuestSlotOffsets
+{
+ QUEST_ID_OFFSET = 0,
+ QUEST_STATE_OFFSET = 1,
+ QUEST_COUNTS_OFFSET = 2,
+ QUEST_TIME_OFFSET = 3
+};
+
+#define MAX_QUEST_OFFSET 4
+
+enum QuestSlotStateMask
+{
+ QUEST_STATE_NONE = 0x0000,
+ QUEST_STATE_COMPLETE = 0x0001,
+ QUEST_STATE_FAIL = 0x0002
+};
+
+class Quest;
+class Spell;
+class Item;
+class WorldSession;
+
+enum PlayerSlots
+{
+ // first slot for item stored (in any way in player m_items data)
+ PLAYER_SLOT_START = 0,
+ // last+1 slot for item stored (in any way in player m_items data)
+ PLAYER_SLOT_END = 118,
+ PLAYER_SLOTS_COUNT = (PLAYER_SLOT_END - PLAYER_SLOT_START)
+};
+
+enum EquipmentSlots
+{
+ EQUIPMENT_SLOT_START = 0,
+ EQUIPMENT_SLOT_HEAD = 0,
+ EQUIPMENT_SLOT_NECK = 1,
+ EQUIPMENT_SLOT_SHOULDERS = 2,
+ EQUIPMENT_SLOT_BODY = 3,
+ EQUIPMENT_SLOT_CHEST = 4,
+ EQUIPMENT_SLOT_WAIST = 5,
+ EQUIPMENT_SLOT_LEGS = 6,
+ EQUIPMENT_SLOT_FEET = 7,
+ EQUIPMENT_SLOT_WRISTS = 8,
+ EQUIPMENT_SLOT_HANDS = 9,
+ EQUIPMENT_SLOT_FINGER1 = 10,
+ EQUIPMENT_SLOT_FINGER2 = 11,
+ EQUIPMENT_SLOT_TRINKET1 = 12,
+ EQUIPMENT_SLOT_TRINKET2 = 13,
+ EQUIPMENT_SLOT_BACK = 14,
+ EQUIPMENT_SLOT_MAINHAND = 15,
+ EQUIPMENT_SLOT_OFFHAND = 16,
+ EQUIPMENT_SLOT_RANGED = 17,
+ EQUIPMENT_SLOT_TABARD = 18,
+ EQUIPMENT_SLOT_END = 19
+};
+
+enum InventorySlots
+{
+ INVENTORY_SLOT_BAG_0 = 255,
+ INVENTORY_SLOT_BAG_START = 19,
+ INVENTORY_SLOT_BAG_1 = 19,
+ INVENTORY_SLOT_BAG_2 = 20,
+ INVENTORY_SLOT_BAG_3 = 21,
+ INVENTORY_SLOT_BAG_4 = 22,
+ INVENTORY_SLOT_BAG_END = 23,
+
+ INVENTORY_SLOT_ITEM_START = 23,
+ INVENTORY_SLOT_ITEM_1 = 23,
+ INVENTORY_SLOT_ITEM_2 = 24,
+ INVENTORY_SLOT_ITEM_3 = 25,
+ INVENTORY_SLOT_ITEM_4 = 26,
+ INVENTORY_SLOT_ITEM_5 = 27,
+ INVENTORY_SLOT_ITEM_6 = 28,
+ INVENTORY_SLOT_ITEM_7 = 29,
+ INVENTORY_SLOT_ITEM_8 = 30,
+ INVENTORY_SLOT_ITEM_9 = 31,
+ INVENTORY_SLOT_ITEM_10 = 32,
+ INVENTORY_SLOT_ITEM_11 = 33,
+ INVENTORY_SLOT_ITEM_12 = 34,
+ INVENTORY_SLOT_ITEM_13 = 35,
+ INVENTORY_SLOT_ITEM_14 = 36,
+ INVENTORY_SLOT_ITEM_15 = 37,
+ INVENTORY_SLOT_ITEM_16 = 38,
+ INVENTORY_SLOT_ITEM_END = 39
+};
+
+enum BankSlots
+{
+ BANK_SLOT_ITEM_START = 39,
+ BANK_SLOT_ITEM_1 = 39,
+ BANK_SLOT_ITEM_2 = 40,
+ BANK_SLOT_ITEM_3 = 41,
+ BANK_SLOT_ITEM_4 = 42,
+ BANK_SLOT_ITEM_5 = 43,
+ BANK_SLOT_ITEM_6 = 44,
+ BANK_SLOT_ITEM_7 = 45,
+ BANK_SLOT_ITEM_8 = 46,
+ BANK_SLOT_ITEM_9 = 47,
+ BANK_SLOT_ITEM_10 = 48,
+ BANK_SLOT_ITEM_11 = 49,
+ BANK_SLOT_ITEM_12 = 50,
+ BANK_SLOT_ITEM_13 = 51,
+ BANK_SLOT_ITEM_14 = 52,
+ BANK_SLOT_ITEM_15 = 53,
+ BANK_SLOT_ITEM_16 = 54,
+ BANK_SLOT_ITEM_17 = 55,
+ BANK_SLOT_ITEM_18 = 56,
+ BANK_SLOT_ITEM_19 = 57,
+ BANK_SLOT_ITEM_20 = 58,
+ BANK_SLOT_ITEM_21 = 59,
+ BANK_SLOT_ITEM_22 = 60,
+ BANK_SLOT_ITEM_23 = 61,
+ BANK_SLOT_ITEM_24 = 62,
+ BANK_SLOT_ITEM_25 = 63,
+ BANK_SLOT_ITEM_26 = 64,
+ BANK_SLOT_ITEM_27 = 65,
+ BANK_SLOT_ITEM_28 = 66,
+ BANK_SLOT_ITEM_END = 67,
+
+ BANK_SLOT_BAG_START = 67,
+ BANK_SLOT_BAG_1 = 67,
+ BANK_SLOT_BAG_2 = 68,
+ BANK_SLOT_BAG_3 = 69,
+ BANK_SLOT_BAG_4 = 70,
+ BANK_SLOT_BAG_5 = 71,
+ BANK_SLOT_BAG_6 = 72,
+ BANK_SLOT_BAG_7 = 73,
+ BANK_SLOT_BAG_END = 74
+};
+
+enum BuyBackSlots
+{
+ // stored in m_buybackitems
+ BUYBACK_SLOT_START = 74,
+ BUYBACK_SLOT_1 = 74,
+ BUYBACK_SLOT_2 = 75,
+ BUYBACK_SLOT_3 = 76,
+ BUYBACK_SLOT_4 = 77,
+ BUYBACK_SLOT_5 = 78,
+ BUYBACK_SLOT_6 = 79,
+ BUYBACK_SLOT_7 = 80,
+ BUYBACK_SLOT_8 = 81,
+ BUYBACK_SLOT_9 = 82,
+ BUYBACK_SLOT_10 = 83,
+ BUYBACK_SLOT_11 = 84,
+ BUYBACK_SLOT_12 = 85,
+ BUYBACK_SLOT_END = 86
+};
+
+enum KeyRingSlots
+{
+ KEYRING_SLOT_START = 86,
+ KEYRING_SLOT_END = 118
+};
+
+struct ItemPosCount
+{
+ ItemPosCount(uint16 _pos, uint8 _count) : pos(_pos), count(_count) {}
+ bool isContainedIn(std::vector<ItemPosCount> const& vec) const;
+ uint16 pos;
+ uint8 count;
+};
+typedef std::vector<ItemPosCount> ItemPosCountVec;
+
+enum SwitchWeapon
+{
+ DEFAULT_SWITCH_WEAPON = 1500, //cooldown in ms
+ ROGUE_SWITCH_WEAPON = 1000
+};
+
+enum TradeSlots
+{
+ TRADE_SLOT_COUNT = 7,
+ TRADE_SLOT_TRADED_COUNT = 6,
+ TRADE_SLOT_NONTRADED = 6
+};
+
+enum TransferAbortReason
+{
+ TRANSFER_ABORT_MAX_PLAYERS = 0x0001, // Transfer Aborted: instance is full
+ TRANSFER_ABORT_NOT_FOUND = 0x0002, // Transfer Aborted: instance not found
+ TRANSFER_ABORT_TOO_MANY_INSTANCES = 0x0003, // You have entered too many instances recently.
+ TRANSFER_ABORT_ZONE_IN_COMBAT = 0x0005, // Unable to zone in while an encounter is in progress.
+ TRANSFER_ABORT_INSUF_EXPAN_LVL1 = 0x0106, // You must have TBC expansion installed to access this area.
+ TRANSFER_ABORT_DIFFICULTY1 = 0x0007, // Normal difficulty mode is not available for %s.
+ TRANSFER_ABORT_DIFFICULTY2 = 0x0107, // Heroic difficulty mode is not available for %s.
+ TRANSFER_ABORT_DIFFICULTY3 = 0x0207 // Epic difficulty mode is not available for %s.
+};
+
+enum InstanceResetWarningType
+{
+ RAID_INSTANCE_WARNING_HOURS = 1, // WARNING! %s is scheduled to reset in %d hour(s).
+ RAID_INSTANCE_WARNING_MIN = 2, // WARNING! %s is scheduled to reset in %d minute(s)!
+ RAID_INSTANCE_WARNING_MIN_SOON = 3, // WARNING! %s is scheduled to reset in %d minute(s). Please exit the zone or you will be returned to your bind location!
+ RAID_INSTANCE_WELCOME = 4 // Welcome to %s. This raid instance is scheduled to reset in %s.
+};
+
+struct MovementInfo
+{
+ // common
+ //uint32 flags;
+ uint8 unk1;
+ uint32 time;
+ float x, y, z, o;
+ // transport
+ uint64 t_guid;
+ float t_x, t_y, t_z, t_o;
+ uint32 t_time;
+ // swimming and unk
+ float s_pitch;
+ // last fall time
+ uint32 fallTime;
+ // jumping
+ float j_unk, j_sinAngle, j_cosAngle, j_xyspeed;
+ // spline
+ float u_unk1;
+
+ MovementInfo()
+ {
+ //flags =
+ time = t_time = fallTime = 0;
+ unk1 = 0;
+ x = y = z = o = t_x = t_y = t_z = t_o = s_pitch = j_unk = j_sinAngle = j_cosAngle = j_xyspeed = u_unk1 = 0.0f;
+ t_guid = 0;
+ }
+
+ /*void SetMovementFlags(uint32 _flags)
+ {
+ flags = _flags;
+ }*/
+};
+
+// flags that use in movement check for example at spell casting
+MovementFlags const movementFlagsMask = MovementFlags(
+ MOVEMENTFLAG_FORWARD |MOVEMENTFLAG_BACKWARD |MOVEMENTFLAG_STRAFE_LEFT|MOVEMENTFLAG_STRAFE_RIGHT|
+ MOVEMENTFLAG_PITCH_UP|MOVEMENTFLAG_PITCH_DOWN|MOVEMENTFLAG_FLY_UNK1 |
+ MOVEMENTFLAG_JUMPING |MOVEMENTFLAG_FALLING |MOVEMENTFLAG_FLY_UP |
+ MOVEMENTFLAG_FLYING |MOVEMENTFLAG_SPLINE
+);
+
+MovementFlags const movementOrTurningFlagsMask = MovementFlags(
+ movementFlagsMask | MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT
+);
+class InstanceSave;
+
+enum RestType
+{
+ REST_TYPE_NO = 0,
+ REST_TYPE_IN_TAVERN = 1,
+ REST_TYPE_IN_CITY = 2
+};
+
+enum DuelCompleteType
+{
+ DUEL_INTERUPTED = 0,
+ DUEL_WON = 1,
+ DUEL_FLED = 2
+};
+
+enum TeleportToOptions
+{
+ TELE_TO_GM_MODE = 0x01,
+ TELE_TO_NOT_LEAVE_TRANSPORT = 0x02,
+ TELE_TO_NOT_LEAVE_COMBAT = 0x04,
+ TELE_TO_NOT_UNSUMMON_PET = 0x08,
+ TELE_TO_SPELL = 0x10,
+};
+
+/// Type of environmental damages
+enum EnviromentalDamage
+{
+ DAMAGE_EXHAUSTED = 0,
+ DAMAGE_DROWNING = 1,
+ DAMAGE_FALL = 2,
+ DAMAGE_LAVA = 3,
+ DAMAGE_SLIME = 4,
+ DAMAGE_FIRE = 5,
+ DAMAGE_FALL_TO_VOID = 6 // custom case for fall without durability loss
+};
+
+// used at player loading query list preparing, and later result selection
+enum PlayerLoginQueryIndex
+{
+ PLAYER_LOGIN_QUERY_LOADFROM = 0,
+ PLAYER_LOGIN_QUERY_LOADGROUP = 1,
+ PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES = 2,
+ PLAYER_LOGIN_QUERY_LOADAURAS = 3,
+ PLAYER_LOGIN_QUERY_LOADSPELLS = 4,
+ PLAYER_LOGIN_QUERY_LOADQUESTSTATUS = 5,
+ PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS = 6,
+ PLAYER_LOGIN_QUERY_LOADTUTORIALS = 7, // common for all characters for some account at specific realm
+ PLAYER_LOGIN_QUERY_LOADREPUTATION = 8,
+ PLAYER_LOGIN_QUERY_LOADINVENTORY = 9,
+ PLAYER_LOGIN_QUERY_LOADACTIONS = 10,
+ PLAYER_LOGIN_QUERY_LOADMAILCOUNT = 11,
+ PLAYER_LOGIN_QUERY_LOADMAILDATE = 12,
+ PLAYER_LOGIN_QUERY_LOADSOCIALLIST = 13,
+ PLAYER_LOGIN_QUERY_LOADHOMEBIND = 14,
+ PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS = 15,
+ PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES = 16,
+ PLAYER_LOGIN_QUERY_LOADGUILD = 17,
+};
+
+#define MAX_PLAYER_LOGIN_QUERY 18
+
+// Player summoning auto-decline time (in secs)
+#define MAX_PLAYER_SUMMON_DELAY (2*MINUTE)
+#define MAX_MONEY_AMOUNT (0x7FFFFFFF-1)
+
+struct InstancePlayerBind
+{
+ InstanceSave *save;
+ bool perm;
+ /* permanent PlayerInstanceBinds are created in Raid/Heroic instances for players
+ that aren't already permanently bound when they are inside when a boss is killed
+ or when they enter an instance that the group leader is permanently bound to. */
+ InstancePlayerBind() : save(NULL), perm(false) {}
+};
+
+class MANGOS_DLL_SPEC PlayerTaxi
+{
+ public:
+ PlayerTaxi();
+ ~PlayerTaxi() {}
+ // Nodes
+ void InitTaxiNodesForLevel(uint32 race, uint32 level);
+ void LoadTaxiMask(const char* data);
+ void SaveTaxiMask(const char* data);
+
+ uint32 GetTaximask( uint8 index ) const { return m_taximask[index]; }
+ bool IsTaximaskNodeKnown(uint32 nodeidx) const
+ {
+ uint8 field = uint8((nodeidx - 1) / 32);
+ uint32 submask = 1<<((nodeidx-1)%32);
+ return (m_taximask[field] & submask) == submask;
+ }
+ bool SetTaximaskNode(uint32 nodeidx)
+ {
+ uint8 field = uint8((nodeidx - 1) / 32);
+ uint32 submask = 1<<((nodeidx-1)%32);
+ if ((m_taximask[field] & submask) != submask )
+ {
+ m_taximask[field] |= submask;
+ return true;
+ }
+ else
+ return false;
+ }
+ void AppendTaximaskTo(ByteBuffer& data,bool all);
+
+ // Destinations
+ bool LoadTaxiDestinationsFromString(std::string values);
+ std::string SaveTaxiDestinationsToString();
+
+ void ClearTaxiDestinations() { m_TaxiDestinations.clear(); }
+ void AddTaxiDestination(uint32 dest) { m_TaxiDestinations.push_back(dest); }
+ uint32 GetTaxiSource() const { return m_TaxiDestinations.empty() ? 0 : m_TaxiDestinations.front(); }
+ uint32 GetTaxiDestination() const { return m_TaxiDestinations.size() < 2 ? 0 : m_TaxiDestinations[1]; }
+ uint32 GetCurrentTaxiPath() const;
+ uint32 NextTaxiDestination()
+ {
+ m_TaxiDestinations.pop_front();
+ return GetTaxiDestination();
+ }
+ bool empty() const { return m_TaxiDestinations.empty(); }
+ private:
+ TaxiMask m_taximask;
+ std::deque<uint32> m_TaxiDestinations;
+};
+
+class MANGOS_DLL_SPEC Player : public Unit
+{
+ friend class WorldSession;
+ friend void Item::AddToUpdateQueueOf(Player *player);
+ friend void Item::RemoveFromUpdateQueueOf(Player *player);
+ public:
+ explicit Player (WorldSession *session);
+ ~Player ( );
+
+ void CleanupsBeforeDelete();
+
+ static UpdateMask updateVisualBits;
+ static void InitVisibleBits();
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0);
+
+ bool TeleportTo(WorldLocation const &loc, uint32 options = 0)
+ {
+ return TeleportTo(loc.mapid, loc.x, loc.y, loc.z, options);
+ }
+
+ void SetSummonPoint(uint32 mapid, float x, float y, float z)
+ {
+ m_summon_expire = time(NULL) + MAX_PLAYER_SUMMON_DELAY;
+ m_summon_mapid = mapid;
+ m_summon_x = x;
+ m_summon_y = y;
+ m_summon_z = z;
+ }
+ void SummonIfPossible(bool agree);
+
+ bool Create( uint32 guidlow, std::string name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId );
+
+ void Update( uint32 time );
+
+ void BuildEnumData( QueryResult * result, WorldPacket * p_data );
+
+ void SetInWater(bool apply);
+
+ bool IsInWater() const { return m_isInWater; }
+ bool IsUnderWater() const;
+
+ void SendInitialPacketsBeforeAddToMap();
+ void SendInitialPacketsAfterAddToMap();
+ void SendTransferAborted(uint32 mapid, uint16 reason);
+ void SendInstanceResetWarning(uint32 mapid, uint32 time);
+
+ bool CanInteractWithNPCs(bool alive = true) const;
+
+ bool ToggleAFK();
+ bool ToggleDND();
+ bool isAFK() const { return HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_AFK); };
+ bool isDND() const { return HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_DND); };
+ uint8 chatTag() const;
+ std::string afkMsg;
+ std::string dndMsg;
+
+ PlayerSocial *GetSocial() { return m_social; }
+
+ PlayerTaxi m_taxi;
+ void InitTaxiNodesForLevel() { m_taxi.InitTaxiNodesForLevel(getRace(),getLevel()); }
+ bool ActivateTaxiPathTo(std::vector<uint32> const& nodes, uint32 mount_id = 0 , Creature* npc = NULL);
+ // mount_id can be used in scripting calls
+ bool isAcceptTickets() const { return GetSession()->GetSecurity() >= SEC_GAMEMASTER && (m_ExtraFlags & PLAYER_EXTRA_GM_ACCEPT_TICKETS); }
+ void SetAcceptTicket(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_GM_ACCEPT_TICKETS; else m_ExtraFlags &= ~PLAYER_EXTRA_GM_ACCEPT_TICKETS; }
+ bool isAcceptWhispers() const { return m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS; }
+ 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); }
+ void SetGMVisible(bool on);
+ void SetPvPDeath(bool on) { if(on) m_ExtraFlags |= PLAYER_EXTRA_PVP_DEATH; else m_ExtraFlags &= ~PLAYER_EXTRA_PVP_DEATH; }
+
+ void GiveXP(uint32 xp, Unit* victim);
+ void GiveLevel(uint32 level);
+ void InitStatsForLevel(bool reapplyMods = false);
+
+ // Played Time Stuff
+ time_t m_logintime;
+ time_t m_Last_tick;
+ uint32 m_Played_time[2];
+ uint32 GetTotalPlayedTime() { return m_Played_time[0]; };
+ uint32 GetLevelPlayedTime() { return m_Played_time[1]; };
+
+ void setDeathState(DeathState s); // overwrite Unit::setDeathState
+
+ void InnEnter (int time,uint32 mapid, float x,float y,float z)
+ {
+ inn_pos_mapid = mapid;
+ inn_pos_x = x;
+ inn_pos_y = y;
+ inn_pos_z = z;
+ time_inn_enter = time;
+ };
+
+ float GetRestBonus() const { return m_rest_bonus; };
+ void SetRestBonus(float rest_bonus_new);
+
+ RestType GetRestType() const { return rest_type; };
+ void SetRestType(RestType n_r_type) { rest_type = n_r_type; };
+
+ uint32 GetInnPosMapId() const { return inn_pos_mapid; };
+ float GetInnPosX() const { return inn_pos_x; };
+ float GetInnPosY() const { return inn_pos_y; };
+ float GetInnPosZ() const { return inn_pos_z; };
+
+ int GetTimeInnEnter() const { return time_inn_enter; };
+ void UpdateInnerTime (int time) { time_inn_enter = time; };
+
+ void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false);
+ void RemoveMiniPet();
+ Pet* GetMiniPet();
+ void SetMiniPet(Pet* pet) { m_miniPet = pet->GetGUID(); }
+ void RemoveGuardians();
+ bool HasGuardianWithEntry(uint32 entry);
+ void AddGuardian(Pet* pet) { m_guardianPets.insert(pet->GetGUID()); }
+ GuardianPetList const& GetGuardians() const { return m_guardianPets; }
+ void Uncharm();
+
+ void Say(std::string text, const uint32 language);
+ void Yell(std::string text, const uint32 language);
+ void TextEmote(std::string text);
+ void Whisper(std::string text, const uint32 language,uint64 receiver);
+ void BuildPlayerChat(WorldPacket *data, uint8 msgtype, std::string text, uint32 language) const;
+
+ /*********************************************************/
+ /*** STORAGE SYSTEM ***/
+ /*********************************************************/
+
+ void SetVirtualItemSlot( uint8 i, Item* item);
+ void SetSheath( uint32 sheathed );
+ uint8 FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap ) const;
+ uint32 GetItemCount( uint32 item, bool inBankAlso = false, Item* skipItem = NULL ) const;
+ Item* GetItemByGuid( uint64 guid ) const;
+ Item* GetItemByPos( uint16 pos ) const;
+ Item* GetItemByPos( uint8 bag, uint8 slot ) const;
+ Item* GetWeaponForAttack(WeaponAttackType attackType, bool useable = false) const;
+ Item* GetShield(bool useable = false) const;
+ static uint32 GetAttackBySlot( uint8 slot ); // MAX_ATTACK if not weapon slot
+ std::vector<Item *> &GetItemUpdateQueue() { return m_itemUpdateQueue; }
+ static bool IsInventoryPos( uint16 pos ) { return IsInventoryPos(pos >> 8,pos & 255); }
+ static bool IsInventoryPos( uint8 bag, uint8 slot );
+ static bool IsEquipmentPos( uint16 pos ) { return IsEquipmentPos(pos >> 8,pos & 255); }
+ static bool IsEquipmentPos( uint8 bag, uint8 slot );
+ static bool IsBagPos( uint16 pos );
+ static bool IsBankPos( uint16 pos ) { return IsBankPos(pos >> 8,pos & 255); }
+ static bool IsBankPos( uint8 bag, uint8 slot );
+ bool HasBankBagSlot( uint8 slot ) const;
+ bool HasItemCount( uint32 item, uint32 count, bool inBankAlso = false ) const;
+ bool HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item const* ignoreItem = NULL);
+ Item* GetItemOrItemWithGemEquipped( uint32 item ) const;
+ uint8 CanTakeMoreSimilarItems(Item* pItem) const { return _CanTakeMoreSimilarItems(pItem->GetEntry(),pItem->GetCount(),pItem); }
+ uint8 CanTakeMoreSimilarItems(uint32 entry, uint32 count) const { return _CanTakeMoreSimilarItems(entry,count,NULL); }
+ uint8 CanStoreNewItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count = NULL ) const
+ {
+ return _CanStoreItem(bag, slot, dest, item, count, NULL, false, no_space_count );
+ }
+ uint8 CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap = false ) const
+ {
+ if(!pItem)
+ return EQUIP_ERR_ITEM_NOT_FOUND;
+ uint32 count = pItem->GetCount();
+ return _CanStoreItem( bag, slot, dest, pItem->GetEntry(), count, pItem, swap, NULL );
+
+ }
+ uint8 CanStoreItems( Item **pItem,int count) const;
+ uint8 CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, uint32 count, bool swap ) const;
+ uint8 CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading = true ) const;
+ uint8 CanUnequipItems( uint32 item, uint32 count ) const;
+ uint8 CanUnequipItem( uint16 src, bool swap ) const;
+ uint8 CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, Item *pItem, bool swap, bool not_loading = true ) const;
+ uint8 CanUseItem( Item *pItem, bool not_loading = true ) const;
+ bool HasItemTotemCategory( uint32 TotemCategory ) const;
+ bool CanUseItem( ItemPrototype const *pItem );
+ uint8 CanUseAmmo( uint32 item ) const;
+ Item* StoreNewItem( ItemPosCountVec const& pos, uint32 item, bool update,int32 randomPropertyId = 0 );
+ Item* StoreItem( ItemPosCountVec const& pos, Item *pItem, bool update );
+ Item* EquipNewItem( uint16 pos, uint32 item, uint32 count, bool update );
+ Item* EquipItem( uint16 pos, Item *pItem, bool update );
+ void AutoUnequipOffhandIfNeed();
+
+ uint8 _CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL) const;
+ uint8 _CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item *pItem = NULL, bool swap = false, uint32* no_space_count = NULL ) const;
+
+ void ApplyEquipCooldown( Item * pItem );
+ void SetAmmo( uint32 item );
+ void RemoveAmmo();
+ float GetAmmoDPS() const { return m_ammoDPS; }
+ bool CheckAmmoCompatibility(const ItemPrototype *ammo_proto) const;
+ void QuickEquipItem( uint16 pos, Item *pItem);
+ void VisualizeItem( uint8 slot, Item *pItem);
+ void SetVisibleItemSlot(uint8 slot, Item *pItem);
+ Item* BankItem( ItemPosCountVec const& dest, Item *pItem, bool update )
+ {
+ return StoreItem( dest, pItem, update);
+ }
+ Item* BankItem( uint16 pos, Item *pItem, bool update );
+ void RemoveItem( uint8 bag, uint8 slot, bool update );
+ void MoveItemFromInventory(uint8 bag, uint8 slot, bool update);
+ // in trade, auction, guild bank, mail....
+ void MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool update, bool in_characterInventoryDB = false);
+ // in trade, guild bank, mail....
+ void RemoveItemDependentAurasAndCasts( Item * pItem );
+ void DestroyItem( uint8 bag, uint8 slot, bool update );
+ void DestroyItemCount( uint32 item, uint32 count, bool update, bool unequip_check = false);
+ void DestroyItemCount( Item* item, uint32& count, bool update );
+ void DestroyConjuredItems( bool update );
+ void DestroyZoneLimitedItem( bool update, uint32 new_zone );
+ void SplitItem( uint16 src, uint16 dst, uint32 count );
+ void SwapItem( uint16 src, uint16 dst );
+ void AddItemToBuyBackSlot( Item *pItem );
+ Item* GetItemFromBuyBackSlot( uint32 slot );
+ void RemoveItemFromBuyBackSlot( uint32 slot, bool del );
+ uint32 GetMaxKeyringSize() const { return KEYRING_SLOT_END-KEYRING_SLOT_START; }
+ void SendEquipError( uint8 msg, Item* pItem, Item *pItem2 );
+ void SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 param );
+ void SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param );
+ void AddWeaponProficiency(uint32 newflag) { m_WeaponProficiency |= newflag; }
+ void AddArmorProficiency(uint32 newflag) { m_ArmorProficiency |= newflag; }
+ uint32 GetWeaponProficiency() const { return m_WeaponProficiency; }
+ uint32 GetArmorProficiency() const { return m_ArmorProficiency; }
+ bool IsInFeralForm() const { return m_form == FORM_CAT || m_form == FORM_BEAR || m_form == FORM_DIREBEAR; }
+ bool IsUseEquipedWeapon( bool mainhand ) const
+ {
+ // disarm applied only to mainhand weapon
+ return !IsInFeralForm() && (!mainhand || !HasFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISARMED) );
+ }
+ void SendNewItem( Item *item, uint32 count, bool received, bool created, bool broadcast = false );
+ bool BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint64 bagguid, uint8 slot);
+
+ float GetReputationPriceDiscount( Creature const* pCreature ) const;
+ Player* GetTrader() const { return pTrader; }
+ void ClearTrade();
+ void TradeCancel(bool sendback);
+ uint16 GetItemPosByTradeSlot(uint32 slot) const { return tradeItems[slot]; }
+
+ void UpdateEnchantTime(uint32 time);
+ void UpdateItemDuration(uint32 time, bool realtimeonly=false);
+ void AddEnchantmentDurations(Item *item);
+ void RemoveEnchantmentDurations(Item *item);
+ void RemoveAllEnchantments(EnchantmentSlot slot);
+ void AddEnchantmentDuration(Item *item,EnchantmentSlot slot,uint32 duration);
+ void ApplyEnchantment(Item *item,EnchantmentSlot slot,bool apply, bool apply_dur = true, bool ignore_condition = false);
+ void ApplyEnchantment(Item *item,bool apply);
+ void SendEnchantmentDurations();
+ void AddItemDurations(Item *item);
+ void RemoveItemDurations(Item *item);
+ void SendItemDurations();
+ void LoadCorpse();
+ void LoadPet();
+
+ uint32 m_stableSlots;
+
+ /*********************************************************/
+ /*** QUEST SYSTEM ***/
+ /*********************************************************/
+
+ void PrepareQuestMenu( uint64 guid );
+ void SendPreparedQuest( uint64 guid );
+ bool IsActiveQuest( uint32 quest_id ) const;
+ Quest const *GetNextQuest( uint64 guid, Quest const *pQuest );
+ bool CanSeeStartQuest( Quest const *pQuest );
+ bool CanTakeQuest( Quest const *pQuest, bool msg );
+ bool CanAddQuest( Quest const *pQuest, bool msg );
+ bool CanCompleteQuest( uint32 quest_id );
+ bool CanCompleteRepeatableQuest(Quest const *pQuest);
+ bool CanRewardQuest( Quest const *pQuest, bool msg );
+ bool CanRewardQuest( Quest const *pQuest, uint32 reward, bool msg );
+ void AddQuest( Quest const *pQuest, Object *questGiver );
+ void CompleteQuest( uint32 quest_id );
+ void IncompleteQuest( uint32 quest_id );
+ void RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce = true );
+ void FailQuest( uint32 quest_id );
+ void FailTimedQuest( uint32 quest_id );
+ bool SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg );
+ bool SatisfyQuestLevel( Quest const* qInfo, bool msg );
+ bool SatisfyQuestLog( bool msg );
+ bool SatisfyQuestPreviousQuest( Quest const* qInfo, bool msg );
+ bool SatisfyQuestRace( Quest const* qInfo, bool msg );
+ bool SatisfyQuestReputation( Quest const* qInfo, bool msg );
+ bool SatisfyQuestStatus( Quest const* qInfo, bool msg );
+ bool SatisfyQuestTimed( Quest const* qInfo, bool msg );
+ bool SatisfyQuestExclusiveGroup( Quest const* qInfo, bool msg );
+ bool SatisfyQuestNextChain( Quest const* qInfo, bool msg );
+ bool SatisfyQuestPrevChain( Quest const* qInfo, bool msg );
+ bool SatisfyQuestDay( Quest const* qInfo, bool msg );
+ bool GiveQuestSourceItem( Quest const *pQuest );
+ bool TakeQuestSourceItem( uint32 quest_id, bool msg );
+ bool GetQuestRewardStatus( uint32 quest_id ) const;
+ QuestStatus GetQuestStatus( uint32 quest_id ) const;
+ void SetQuestStatus( uint32 quest_id, QuestStatus status );
+
+ void SetDailyQuestStatus( uint32 quest_id );
+ void ResetDailyQuestStatus();
+
+ uint16 FindQuestSlot( uint32 quest_id ) const;
+ uint32 GetQuestSlotQuestId(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_ID_OFFSET); }
+ uint32 GetQuestSlotState(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET); }
+ uint32 GetQuestSlotCounters(uint16 slot)const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET); }
+ uint8 GetQuestSlotCounter(uint16 slot,uint8 counter) const { return GetByteValue(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET,counter); }
+ uint32 GetQuestSlotTime(uint16 slot) const { return GetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_TIME_OFFSET); }
+ void SetQuestSlot(uint16 slot,uint32 quest_id, uint32 timer = 0)
+ {
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_ID_OFFSET,quest_id);
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET,0);
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET,0);
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_TIME_OFFSET,timer);
+ }
+ void SetQuestSlotCounter(uint16 slot,uint8 counter,uint8 count) { SetByteValue(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_COUNTS_OFFSET,counter,count); }
+ void SetQuestSlotState(uint16 slot,uint32 state) { SetFlag(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET,state); }
+ void RemoveQuestSlotState(uint16 slot,uint32 state) { RemoveFlag(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_STATE_OFFSET,state); }
+ void SetQuestSlotTimer(uint16 slot,uint32 timer) { SetUInt32Value(PLAYER_QUEST_LOG_1_1 + slot*MAX_QUEST_OFFSET + QUEST_TIME_OFFSET,timer); }
+ void SwapQuestSlot(uint16 slot1,uint16 slot2)
+ {
+ for (int i = 0; i < MAX_QUEST_OFFSET ; ++i )
+ {
+ uint32 temp1 = GetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot1 + i);
+ uint32 temp2 = GetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot2 + i);
+
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot1 + i, temp2);
+ SetUInt32Value(PLAYER_QUEST_LOG_1_1 + MAX_QUEST_OFFSET *slot2 + i, temp1);
+ }
+ }
+ uint32 GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry);
+ void AdjustQuestReqItemCount( Quest const* pQuest );
+ void AreaExploredOrEventHappens( uint32 questId );
+ void GroupEventHappens( uint32 questId, WorldObject const* pEventObject );
+ void ItemAddedQuestCheck( uint32 entry, uint32 count );
+ void ItemRemovedQuestCheck( uint32 entry, uint32 count );
+ void KilledMonster( uint32 entry, uint64 guid );
+ void CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id );
+ void TalkedToCreature( uint32 entry, uint64 guid );
+ void MoneyChanged( uint32 value );
+ bool HasQuestForItem( uint32 itemid ) const;
+ bool HasQuestForGO(int32 GOId);
+ void UpdateForQuestsGO();
+ bool CanShareQuest(uint32 quest_id) const;
+
+ void SendQuestComplete( uint32 quest_id );
+ void SendQuestReward( Quest const *pQuest, uint32 XP, Object* questGiver );
+ void SendQuestFailed( uint32 quest_id );
+ void SendQuestTimerFailed( uint32 quest_id );
+ void SendCanTakeQuestResponse( uint32 msg );
+ void SendPushToPartyResponse( Player *pPlayer, uint32 msg );
+ void SendQuestUpdateAddItem( Quest const* pQuest, uint32 item_idx, uint32 count );
+ void SendQuestUpdateAddCreatureOrGo( Quest const* pQuest, uint64 guid, uint32 creatureOrGO_idx, uint32 old_count, uint32 add_count );
+
+ uint64 GetDivider() { return m_divider; };
+ void SetDivider( uint64 guid ) { m_divider = guid; };
+
+ uint32 GetInGameTime() { return m_ingametime; };
+
+ void SetInGameTime( uint32 time ) { m_ingametime = time; };
+
+ void AddTimedQuest( uint32 quest_id ) { m_timedquests.insert(quest_id); }
+
+ /*********************************************************/
+ /*** LOAD SYSTEM ***/
+ /*********************************************************/
+
+ bool LoadFromDB(uint32 guid, SqlQueryHolder *holder);
+ bool MinimalLoadFromDB(QueryResult *result, uint32 guid);
+ static bool LoadValuesArrayFromDB(Tokens& data,uint64 guid);
+ static uint32 GetUInt32ValueFromArray(Tokens const& data, uint16 index);
+ static float GetFloatValueFromArray(Tokens const& data, uint16 index);
+ static uint32 GetUInt32ValueFromDB(uint16 index, uint64 guid);
+ static float GetFloatValueFromDB(uint16 index, uint64 guid);
+ static uint32 GetZoneIdFromDB(uint64 guid);
+ static bool LoadPositionFromDB(uint32& mapid, float& x,float& y,float& z,float& o, bool& in_flight, uint64 guid);
+
+ /*********************************************************/
+ /*** SAVE SYSTEM ***/
+ /*********************************************************/
+
+ void SaveToDB();
+ void SaveInventoryAndGoldToDB(); // fast save function for item/money cheating preventing
+ void SaveGoldToDB() { SetUInt32ValueInDB(PLAYER_FIELD_COINAGE,GetMoney(),GetGUID()); }
+ static bool SaveValuesArrayInDB(Tokens const& data,uint64 guid);
+ static void SetUInt32ValueInArray(Tokens& data,uint16 index, uint32 value);
+ static void SetFloatValueInArray(Tokens& data,uint16 index, float value);
+ static void SetUInt32ValueInDB(uint16 index, uint32 value, uint64 guid);
+ static void SetFloatValueInDB(uint16 index, float value, uint64 guid);
+ static void SavePositionInDB(uint32 mapid, float x,float y,float z,float o,uint32 zone,uint64 guid);
+
+ bool m_mailsLoaded;
+ bool m_mailsUpdated;
+
+ void SetBindPoint(uint64 guid);
+ void SendTalentWipeConfirm(uint64 guid);
+ void RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker );
+ void SendPetSkillWipeConfirm();
+ void CalcRage( uint32 damage,bool attacker );
+ void RegenerateAll();
+ void Regenerate(Powers power);
+ void RegenerateHealth();
+ void setRegenTimer(uint32 time) {m_regenTimer = time;}
+ void setWeaponChangeTimer(uint32 time) {m_weaponChangeTimer = time;}
+
+ uint32 GetMoney() { return GetUInt32Value (PLAYER_FIELD_COINAGE); }
+ void ModifyMoney( int32 d )
+ {
+ if(d < 0)
+ SetMoney (GetMoney() > uint32(-d) ? GetMoney() + d : 0);
+ else
+ SetMoney (GetMoney() < MAX_MONEY_AMOUNT - d ? GetMoney() + d : MAX_MONEY_AMOUNT);
+
+ // "At Gold Limit"
+ if(GetMoney() >= MAX_MONEY_AMOUNT)
+ SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD,NULL,NULL);
+ }
+ void SetMoney( uint32 value )
+ {
+ SetUInt32Value (PLAYER_FIELD_COINAGE, value);
+ MoneyChanged( value );
+ }
+
+ uint32 GetTutorialInt(uint32 intId )
+ {
+ ASSERT( (intId < 8) );
+ return m_Tutorials[intId];
+ }
+
+ void SetTutorialInt(uint32 intId, uint32 value)
+ {
+ ASSERT( (intId < 8) );
+ if(m_Tutorials[intId]!=value)
+ {
+ m_Tutorials[intId] = value;
+ m_TutorialsChanged = true;
+ }
+ }
+
+ QuestStatusMap& getQuestStatusMap() { return mQuestStatus; };
+
+ const uint64& GetSelection( ) const { return m_curSelection; }
+ void SetSelection(const uint64 &guid) { m_curSelection = guid; SetUInt64Value(UNIT_FIELD_TARGET, guid); }
+
+ uint8 GetComboPoints() { return m_comboPoints; }
+ uint64 GetComboTarget() { return m_comboTarget; }
+
+ void AddComboPoints(Unit* target, int8 count);
+ void ClearComboPoints();
+ void SendComboPoints();
+
+ void SendMailResult(uint32 mailId, uint32 mailAction, uint32 mailError, uint32 equipError = 0, uint32 item_guid = 0, uint32 item_count = 0);
+ void SendNewMail();
+ void UpdateNextMailTimeAndUnreads();
+ void AddNewMailDeliverTime(time_t deliver_time);
+ bool IsMailsLoaded() const { return m_mailsLoaded; }
+
+ //void SetMail(Mail *m);
+ void RemoveMail(uint32 id);
+
+ void AddMail(Mail* mail) { m_mail.push_front(mail);}// for call from WorldSession::SendMailTo
+ uint32 GetMailSize() { return m_mail.size();};
+ Mail* GetMail(uint32 id);
+
+ PlayerMails::iterator GetmailBegin() { return m_mail.begin();};
+ PlayerMails::iterator GetmailEnd() { return m_mail.end();};
+
+ /*********************************************************/
+ /*** MAILED ITEMS SYSTEM ***/
+ /*********************************************************/
+
+ uint8 unReadMails;
+ time_t m_nextMailDelivereTime;
+
+ typedef HM_NAMESPACE::hash_map<uint32, Item*> ItemMap;
+
+ ItemMap mMitems; //template defined in objectmgr.cpp
+
+ Item* GetMItem(uint32 id)
+ {
+ ItemMap::const_iterator itr = mMitems.find(id);
+ if (itr != mMitems.end())
+ return itr->second;
+
+ return NULL;
+ }
+
+ void AddMItem(Item* it)
+ {
+ ASSERT( it );
+ //assert deleted, because items can be added before loading
+ mMitems[it->GetGUIDLow()] = it;
+ }
+
+ bool RemoveMItem(uint32 id)
+ {
+ ItemMap::iterator i = mMitems.find(id);
+ if (i == mMitems.end())
+ return false;
+
+ mMitems.erase(i);
+ return true;
+ }
+
+ void PetSpellInitialize();
+ void CharmSpellInitialize();
+ void PossessSpellInitialize();
+ bool HasSpell(uint32 spell) const;
+ TrainerSpellState GetTrainerSpellState(TrainerSpell const* trainer_spell) const;
+ bool IsSpellFitByClassAndRace( uint32 spell_id ) const;
+
+ void SendProficiency(uint8 pr1, uint32 pr2);
+ void SendInitialSpells();
+ bool addSpell(uint32 spell_id, bool active, bool learning = true, bool loading = false, uint16 slot_id=SPELL_WITHOUT_SLOT_ID, bool disabled = false);
+ void learnSpell(uint32 spell_id);
+ void removeSpell(uint32 spell_id, bool disabled = false);
+ void resetSpells();
+ void learnDefaultSpells(bool loading = false);
+ void learnQuestRewardedSpells();
+ void learnQuestRewardedSpells(Quest const* quest);
+
+ uint32 GetFreeTalentPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS1); }
+ void SetFreeTalentPoints(uint32 points) { SetUInt32Value(PLAYER_CHARACTER_POINTS1,points); }
+ bool resetTalents(bool no_cost = false);
+ uint32 resetTalentsCost() const;
+ void InitTalentForLevel();
+
+ uint32 GetFreePrimaryProffesionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS2); }
+ void SetFreePrimaryProffesions(uint16 profs) { SetUInt32Value(PLAYER_CHARACTER_POINTS2,profs); }
+ void InitPrimaryProffesions();
+
+ PlayerSpellMap const& GetSpellMap() const { return m_spells; }
+ PlayerSpellMap & GetSpellMap() { return m_spells; }
+
+ void AddSpellMod(SpellModifier* mod, bool apply);
+ int32 GetTotalFlatMods(uint32 spellId, SpellModOp op);
+ int32 GetTotalPctMods(uint32 spellId, SpellModOp op);
+ bool IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell const* spell = NULL);
+ template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell const* spell = NULL);
+ void RemoveSpellMods(Spell const* spell);
+
+ bool HasSpellCooldown(uint32 spell_id) const
+ {
+ SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
+ return itr != m_spellCooldowns.end() && itr->second.end > time(NULL);
+ }
+ uint32 GetSpellCooldownDelay(uint32 spell_id) const
+ {
+ SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
+ time_t t = time(NULL);
+ return itr != m_spellCooldowns.end() && itr->second.end > t ? itr->second.end - t : 0;
+ }
+ void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time);
+ void SendCooldownEvent(SpellEntry const *spellInfo);
+ void ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs );
+ void RemoveSpellCooldown(uint32 spell_id) { m_spellCooldowns.erase(spell_id); }
+ void RemoveArenaSpellCooldowns();
+ void RemoveAllSpellCooldown();
+ void _LoadSpellCooldowns(QueryResult *result);
+ void _SaveSpellCooldowns();
+
+ void setResurrectRequestData(uint64 guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana)
+ {
+ m_resurrectGUID = guid;
+ m_resurrectMap = mapId;
+ m_resurrectX = X;
+ m_resurrectY = Y;
+ m_resurrectZ = Z;
+ m_resurrectHealth = health;
+ m_resurrectMana = mana;
+ };
+ void clearResurrectRequestData() { setResurrectRequestData(0,0,0.0f,0.0f,0.0f,0,0); }
+ bool isRessurectRequestedBy(uint64 guid) const { return m_resurrectGUID == guid; }
+ bool isRessurectRequested() const { return m_resurrectGUID != 0; }
+ void ResurectUsingRequestData();
+
+ int getCinematic()
+ {
+ return m_cinematic;
+ }
+ void setCinematic(int cine)
+ {
+ m_cinematic = cine;
+ }
+
+ void addActionButton(uint8 button, uint16 action, uint8 type, uint8 misc);
+ void removeActionButton(uint8 button);
+ void SendInitialActionButtons();
+
+ PvPInfo pvpInfo;
+ void UpdatePvP(bool state, bool ovrride=false);
+ void UpdateZone(uint32 newZone);
+ void UpdateArea(uint32 newArea);
+
+ void UpdateZoneDependentAuras( uint32 zone_id ); // zones
+ void UpdateAreaDependentAuras( uint32 area_id ); // subzones
+
+ void UpdateAfkReport(time_t currTime);
+ void UpdatePvPFlag(time_t currTime);
+ void UpdateContestedPvP(uint32 currTime);
+ void SetContestedPvPTimer(uint32 newTime) {m_contestedPvPTimer = newTime;}
+ void ResetContestedPvP()
+ {
+ clearUnitState(UNIT_STAT_ATTACK_PLAYER);
+ RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
+ m_contestedPvPTimer = 0;
+ }
+
+ /** todo: -maybe move UpdateDuelFlag+DuelComplete to independent DuelHandler.. **/
+ DuelInfo *duel;
+ void UpdateDuelFlag(time_t currTime);
+ void CheckDuelDistance(time_t currTime);
+ void DuelComplete(DuelCompleteType type);
+
+ bool IsGroupVisibleFor(Player* p) const;
+ bool IsInSameGroupWith(Player const* p) const;
+ bool IsInSameRaidWith(Player const* p) const { return p==this || (GetGroup() != NULL && GetGroup() == p->GetGroup()); }
+ void UninviteFromGroup();
+ static void RemoveFromGroup(Group* group, uint64 guid);
+ void RemoveFromGroup() { RemoveFromGroup(GetGroup(),GetGUID()); }
+ void SendUpdateToOutOfRangeGroupMembers();
+
+ void SetInGuild(uint32 GuildId) { SetUInt32Value(PLAYER_GUILDID, GuildId); Player::SetUInt32ValueInDB(PLAYER_GUILDID, GuildId, this->GetGUID()); }
+ void SetRank(uint32 rankId){ SetUInt32Value(PLAYER_GUILDRANK, rankId); Player::SetUInt32ValueInDB(PLAYER_GUILDRANK, rankId, this->GetGUID()); }
+ void SetGuildIdInvited(uint32 GuildId) { m_GuildIdInvited = GuildId; }
+ uint32 GetGuildId() { return GetUInt32Value(PLAYER_GUILDID); }
+ static uint32 GetGuildIdFromDB(uint64 guid);
+ uint32 GetRank(){ return GetUInt32Value(PLAYER_GUILDRANK); }
+ static uint32 GetRankFromDB(uint64 guid);
+ int GetGuildIdInvited() { return m_GuildIdInvited; }
+ static void RemovePetitionsAndSigns(uint64 guid, uint32 type);
+
+ // Arena Team
+ void SetInArenaTeam(uint32 ArenaTeamId, uint8 slot)
+ {
+ SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6), ArenaTeamId);
+ SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6), ArenaTeamId, this->GetGUID());
+ }
+ uint32 GetArenaTeamId(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot * 6)); }
+ static uint32 GetArenaTeamIdFromDB(uint64 guid, uint8 slot);
+ void SetArenaTeamIdInvited(uint32 ArenaTeamId) { m_ArenaTeamIdInvited = ArenaTeamId; }
+ uint32 GetArenaTeamIdInvited() { return m_ArenaTeamIdInvited; }
+
+ void SetDifficulty(uint32 dungeon_difficulty) { m_dungeonDifficulty = dungeon_difficulty; }
+ uint8 GetDifficulty() { return m_dungeonDifficulty; }
+
+ bool UpdateSkill(uint32 skill_id, uint32 step);
+ bool UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step);
+
+ bool UpdateCraftSkill(uint32 spellid);
+ bool UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator = 1);
+ bool UpdateFishingSkill();
+
+ uint32 GetBaseDefenseSkillValue() const { return GetBaseSkillValue(SKILL_DEFENSE); }
+ uint32 GetBaseWeaponSkillValue(WeaponAttackType attType) const;
+
+ uint32 GetSpellByProto(ItemPrototype *proto);
+
+ float GetHealthBonusFromStamina();
+ float GetManaBonusFromIntellect();
+
+ bool UpdateStats(Stats stat);
+ bool UpdateAllStats();
+ void UpdateResistances(uint32 school);
+ void UpdateArmor();
+ void UpdateMaxHealth();
+ void UpdateMaxPower(Powers power);
+ void UpdateAttackPowerAndDamage(bool ranged = false);
+ void UpdateShieldBlockValue();
+ void UpdateDamagePhysical(WeaponAttackType attType);
+ void UpdateSpellDamageAndHealingBonus();
+
+ void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage);
+
+ void UpdateDefenseBonusesMod();
+ void ApplyRatingMod(CombatRating cr, int32 value, bool apply);
+ float GetMeleeCritFromAgility();
+ float GetDodgeFromAgility();
+ float GetSpellCritFromIntellect();
+ float OCTRegenHPPerSpirit();
+ float OCTRegenMPPerSpirit();
+ float GetRatingCoefficient(CombatRating cr) const;
+ float GetRatingBonusValue(CombatRating cr) const;
+ uint32 GetMeleeCritDamageReduction(uint32 damage) const;
+ uint32 GetRangedCritDamageReduction(uint32 damage) const;
+ uint32 GetSpellCritDamageReduction(uint32 damage) const;
+ uint32 GetDotDamageReduction(uint32 damage) const;
+
+ float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const;
+ void UpdateBlockPercentage();
+ void UpdateCritPercentage(WeaponAttackType attType);
+ void UpdateAllCritPercentages();
+ void UpdateParryPercentage();
+ void UpdateDodgePercentage();
+ void UpdateAllSpellCritChances();
+ void UpdateSpellCritChance(uint32 school);
+ void UpdateExpertise(WeaponAttackType attType);
+ void UpdateManaRegen();
+
+ const uint64& GetLootGUID() const { return m_lootGuid; }
+ void SetLootGUID(const uint64 &guid) { m_lootGuid = guid; }
+
+ void RemovedInsignia(Player* looterPlr);
+
+ WorldSession* GetSession() const { return m_session; }
+ void SetSession(WorldSession *s) { m_session = s; }
+
+ void BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const;
+ void DestroyForPlayer( Player *target ) const;
+ void SendDelayResponse(const uint32);
+ void SendLogXPGain(uint32 GivenXP,Unit* victim,uint32 RestXP);
+
+ //notifiers
+ void SendAttackSwingCantAttack();
+ void SendAttackSwingCancelAttack();
+ void SendAttackSwingDeadTarget();
+ void SendAttackSwingNotStanding();
+ void SendAttackSwingNotInRange();
+ void SendAttackSwingBadFacingAttack();
+ void SendAutoRepeatCancel();
+ void SendExplorationExperience(uint32 Area, uint32 Experience);
+
+ void SendDungeonDifficulty(bool IsInGroup);
+ void ResetInstances(uint8 method);
+ void SendResetInstanceSuccess(uint32 MapId);
+ void SendResetInstanceFailed(uint32 reason, uint32 MapId);
+ void SendResetFailedNotify(uint32 mapid);
+
+ bool SetPosition(float x, float y, float z, float orientation, bool teleport = false);
+ void UpdateUnderwaterState( Map * m, float x, float y, float z );
+
+ void SendMessageToSet(WorldPacket *data, bool self);// overwrite Object::SendMessageToSet
+ void SendMessageToSetInRange(WorldPacket *data, float fist, bool self);
+ // overwrite Object::SendMessageToSetInRange
+ void SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only);
+
+ static void DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars = true);
+
+ Corpse *GetCorpse() const;
+ void SpawnCorpseBones();
+ void CreateCorpse();
+ void KillPlayer();
+ uint32 GetResurrectionSpellId();
+ void ResurrectPlayer(float restore_percent, bool updateToWorld = true, bool applySickness = false);
+ void BuildPlayerRepop();
+ void RepopAtGraveyard();
+
+ void DurabilityLossAll(double percent, bool inventory);
+ void DurabilityLoss(Item* item, double percent);
+ void DurabilityPointsLossAll(int32 points, bool inventory);
+ void DurabilityPointsLoss(Item* item, int32 points);
+ void DurabilityPointLossForEquipSlot(EquipmentSlots slot);
+ uint32 DurabilityRepairAll(bool cost, float discountMod, bool guildBank);
+ uint32 DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank);
+
+ void StopMirrorTimers()
+ {
+ StopMirrorTimer(FATIGUE_TIMER);
+ StopMirrorTimer(BREATH_TIMER);
+ StopMirrorTimer(FIRE_TIMER);
+ }
+
+ void SetMovement(PlayerMovementType pType);
+
+ void JoinedChannel(Channel *c);
+ void LeftChannel(Channel *c);
+ void CleanupChannels();
+ void UpdateLocalChannels( uint32 newZone );
+ void LeaveLFGChannel();
+
+ void UpdateDefense();
+ void UpdateWeaponSkill (WeaponAttackType attType);
+ void UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, MeleeHitOutcome outcome, bool defence);
+
+ void SetSkill(uint32 id, uint16 currVal, uint16 maxVal);
+ uint16 GetMaxSkillValue(uint32 skill) const; // max + perm. bonus
+ uint16 GetPureMaxSkillValue(uint32 skill) const; // max
+ uint16 GetSkillValue(uint32 skill) const; // skill value + perm. bonus + temp bonus
+ uint16 GetBaseSkillValue(uint32 skill) const; // skill value + perm. bonus
+ uint16 GetPureSkillValue(uint32 skill) const; // skill value
+ int16 GetSkillTempBonusValue(uint32 skill) const;
+ bool HasSkill(uint32 skill) const;
+ void learnSkillRewardedSpells( uint32 id );
+ void learnSkillRewardedSpells();
+
+ void SetDontMove(bool dontMove);
+ bool GetDontMove() const { return m_dontMove; }
+
+ void CheckExploreSystem(void);
+
+ static uint32 TeamForRace(uint8 race);
+ uint32 GetTeam() const { return m_team; }
+ static uint32 getFactionForRace(uint8 race);
+ void setFactionForRace(uint8 race);
+
+ bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const;
+ bool RewardPlayerAndGroupAtKill(Unit* pVictim);
+
+ FactionStateList m_factions;
+ ForcedReactions m_forcedReactions;
+ uint32 GetDefaultReputationFlags(const FactionEntry *factionEntry) const;
+ int32 GetBaseReputation(const FactionEntry *factionEntry) const;
+ int32 GetReputation(uint32 faction_id) const;
+ int32 GetReputation(const FactionEntry *factionEntry) const;
+ ReputationRank GetReputationRank(uint32 faction) const;
+ ReputationRank GetReputationRank(const FactionEntry *factionEntry) const;
+ ReputationRank GetBaseReputationRank(const FactionEntry *factionEntry) const;
+ ReputationRank ReputationToRank(int32 standing) const;
+ const static int32 ReputationRank_Length[MAX_REPUTATION_RANK];
+ const static int32 Reputation_Cap = 42999;
+ const static int32 Reputation_Bottom = -42000;
+ bool ModifyFactionReputation(uint32 FactionTemplateId, int32 DeltaReputation);
+ bool ModifyFactionReputation(FactionEntry const* factionEntry, int32 standing);
+ bool ModifyOneFactionReputation(FactionEntry const* factionEntry, int32 standing);
+ bool SetFactionReputation(uint32 FactionTemplateId, int32 standing);
+ bool SetFactionReputation(FactionEntry const* factionEntry, int32 standing);
+ bool SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing);
+ int32 CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, bool for_quest);
+ void RewardReputation(Unit *pVictim, float rate);
+ void RewardReputation(Quest const *pQuest);
+ void SetInitialFactions();
+ void UpdateReputation() const;
+ void SendFactionState(FactionState const* faction) const;
+ void SendInitialReputations();
+ FactionState const* GetFactionState( FactionEntry const* factionEntry) const;
+ void SetFactionAtWar(FactionState* faction, bool atWar);
+ void SetFactionInactive(FactionState* faction, bool inactive);
+ void SetFactionVisible(FactionState* faction);
+ void SetFactionVisibleForFactionTemplateId(uint32 FactionTemplateId);
+ void SetFactionVisibleForFactionId(uint32 FactionId);
+ void UpdateMaxSkills();
+ void UpdateSkillsToMaxSkillsForLevel(); // for .levelup
+ void ModifySkillBonus(uint32 skillid,int32 val, bool talent);
+
+ /*********************************************************/
+ /*** PVP SYSTEM ***/
+ /*********************************************************/
+ void UpdateArenaFields();
+ void UpdateHonorFields();
+ bool RewardHonor(Unit *pVictim, uint32 groupsize, float honor = -1, bool pvptoken = false);
+ uint32 GetHonorPoints() { return GetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY); }
+ uint32 GetArenaPoints() { return GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY); }
+ void ModifyHonorPoints( int32 value );
+ void ModifyArenaPoints( int32 value );
+ uint32 GetMaxPersonalArenaRatingRequirement();
+
+ //End of PvP System
+
+ void SetDrunkValue(uint16 newDrunkValue, uint32 itemid=0);
+ uint16 GetDrunkValue() const { return m_drunk; }
+ static DrunkenState GetDrunkenstateByValue(uint16 value);
+
+ uint32 GetDeathTimer() const { return m_deathTimer; }
+ uint32 GetCorpseReclaimDelay(bool pvp) const;
+ void UpdateCorpseReclaimDelay();
+ void SendCorpseReclaimDelay(bool load = false);
+
+ uint32 GetShieldBlockValue() const; // overwrite Unit version (virtual)
+ bool CanParry() const { return m_canParry; }
+ void SetCanParry(bool value);
+ bool CanBlock() const { return m_canBlock; }
+ void SetCanBlock(bool value);
+ bool CanDualWield() const { return m_canDualWield; }
+ void SetCanDualWield(bool value) { m_canDualWield = value; }
+
+ void SetRegularAttackTime();
+ void SetBaseModValue(BaseModGroup modGroup, BaseModType modType, float value) { m_auraBaseMod[modGroup][modType] = value; }
+ void HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, float amount, bool apply, bool affectStats = true);
+ float GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const;
+ float GetTotalBaseModValue(BaseModGroup modGroup) const;
+ float GetTotalPercentageModValue(BaseModGroup modGroup) const { return m_auraBaseMod[modGroup][FLAT_MOD] + m_auraBaseMod[modGroup][PCT_MOD]; }
+ void _ApplyAllStatBonuses();
+ void _RemoveAllStatBonuses();
+
+ void _ApplyWeaponDependentAuraMods(Item *item,WeaponAttackType attackType,bool apply);
+ void _ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply);
+ void _ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, Aura* aura, bool apply);
+
+ void _ApplyItemMods(Item *item,uint8 slot,bool apply);
+ void _RemoveAllItemMods();
+ void _ApplyAllItemMods();
+ void _ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply);
+ void _ApplyAmmoBonuses();
+ bool EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot);
+ void ToggleMetaGemsActive(uint8 exceptslot, bool apply);
+ void CorrectMetaGemEnchants(uint8 slot, bool apply);
+ void InitDataForForm(bool reapplyMods = false);
+
+ void ApplyItemEquipSpell(Item *item, bool apply, bool form_change = false);
+ void ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply, bool form_change = false);
+ void UpdateEquipSpellsAtFormChange();
+ void CastItemCombatSpell(Item *item,Unit* Target, WeaponAttackType attType);
+
+ void SendInitWorldStates();
+ void SendUpdateWorldState(uint32 Field, uint32 Value);
+ void SendDirectMessage(WorldPacket *data);
+
+ void SendAuraDurationsForTarget(Unit* target);
+
+ PlayerMenu* PlayerTalkClass;
+ std::vector<ItemSetEffect *> ItemSetEff;
+
+ void SendLoot(uint64 guid, LootType loot_type);
+ void SendLootRelease( uint64 guid );
+ void SendNotifyLootItemRemoved(uint8 lootSlot);
+ void SendNotifyLootMoneyRemoved();
+
+ /*********************************************************/
+ /*** BATTLEGROUND SYSTEM ***/
+ /*********************************************************/
+
+ bool InBattleGround() const { return m_bgBattleGroundID != 0; }
+ uint32 GetBattleGroundId() const { return m_bgBattleGroundID; }
+ BattleGround* GetBattleGround() const;
+ bool InArena() const;
+
+ static uint32 GetMinLevelForBattleGroundQueueId(uint32 queue_id);
+ static uint32 GetMaxLevelForBattleGroundQueueId(uint32 queue_id);
+ uint32 GetBattleGroundQueueIdFromLevel() const;
+
+ bool InBattleGroundQueue() const
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ if (m_bgBattleGroundQueueID[i].bgQueueType != 0)
+ return true;
+ return false;
+ }
+
+ uint32 GetBattleGroundQueueId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgQueueType; }
+ uint32 GetBattleGroundQueueIndex(uint32 bgQueueType) const
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType)
+ return i;
+ return PLAYER_MAX_BATTLEGROUND_QUEUES;
+ }
+ bool IsInvitedForBattleGroundQueueType(uint32 bgQueueType) const
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType)
+ return m_bgBattleGroundQueueID[i].invitedToInstance != 0;
+ return PLAYER_MAX_BATTLEGROUND_QUEUES;
+ }
+ bool InBattleGroundQueueForBattleGroundQueueType(uint32 bgQueueType) const
+ {
+ return GetBattleGroundQueueIndex(bgQueueType) < PLAYER_MAX_BATTLEGROUND_QUEUES;
+ }
+
+ void SetBattleGroundId(uint32 val) { m_bgBattleGroundID = val; }
+ uint32 AddBattleGroundQueueId(uint32 val)
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ if (m_bgBattleGroundQueueID[i].bgQueueType == 0 || m_bgBattleGroundQueueID[i].bgQueueType == val)
+ {
+ m_bgBattleGroundQueueID[i].bgQueueType = val;
+ m_bgBattleGroundQueueID[i].invitedToInstance = 0;
+ return i;
+ }
+ }
+ return PLAYER_MAX_BATTLEGROUND_QUEUES;
+ }
+ bool HasFreeBattleGroundQueueId()
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ if (m_bgBattleGroundQueueID[i].bgQueueType == 0)
+ return true;
+ return false;
+ }
+ void RemoveBattleGroundQueueId(uint32 val)
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ {
+ if (m_bgBattleGroundQueueID[i].bgQueueType == val)
+ {
+ m_bgBattleGroundQueueID[i].bgQueueType = 0;
+ m_bgBattleGroundQueueID[i].invitedToInstance = 0;
+ return;
+ }
+ }
+ }
+ void SetInviteForBattleGroundQueueType(uint32 bgQueueType, uint32 instanceId)
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ if (m_bgBattleGroundQueueID[i].bgQueueType == bgQueueType)
+ m_bgBattleGroundQueueID[i].invitedToInstance = instanceId;
+ }
+ bool IsInvitedForBattleGroundInstance(uint32 instanceId) const
+ {
+ for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++)
+ if (m_bgBattleGroundQueueID[i].invitedToInstance == instanceId)
+ return true;
+ return false;
+ }
+ uint32 GetBattleGroundEntryPointMap() const { return m_bgEntryPointMap; }
+ float GetBattleGroundEntryPointX() const { return m_bgEntryPointX; }
+ float GetBattleGroundEntryPointY() const { return m_bgEntryPointY; }
+ float GetBattleGroundEntryPointZ() const { return m_bgEntryPointZ; }
+ float GetBattleGroundEntryPointO() const { return m_bgEntryPointO; }
+ void SetBattleGroundEntryPoint(uint32 Map, float PosX, float PosY, float PosZ, float PosO )
+ {
+ m_bgEntryPointMap = Map;
+ m_bgEntryPointX = PosX;
+ m_bgEntryPointY = PosY;
+ m_bgEntryPointZ = PosZ;
+ m_bgEntryPointO = PosO;
+ }
+
+ void SetBGTeam(uint32 team) { m_bgTeam = team; }
+ uint32 GetBGTeam() const { return m_bgTeam ? m_bgTeam : GetTeam(); }
+
+ void LeaveBattleground(bool teleportToEntryPoint = true);
+ bool CanJoinToBattleground() const;
+ bool CanReportAfkDueToLimit();
+ void ReportedAfkBy(Player* reporter);
+ void ClearAfkReports() { m_bgAfkReporter.clear(); }
+
+ bool GetBGAccessByLevel(uint32 bgTypeId) const;
+ bool isAllowUseBattleGroundObject();
+
+ /*********************************************************/
+ /*** REST SYSTEM ***/
+ /*********************************************************/
+
+ bool isRested() const { return GetRestTime() >= 10000; }
+ uint32 GetXPRestBonus(uint32 xp);
+ uint32 GetRestTime() const { return m_restTime;};
+ void SetRestTime(uint32 v) { m_restTime = v;};
+
+ /*********************************************************/
+ /*** ENVIROMENTAL SYSTEM ***/
+ /*********************************************************/
+
+ void EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 damage);
+
+ /*********************************************************/
+ /*** FLOOD FILTER SYSTEM ***/
+ /*********************************************************/
+
+ void UpdateSpeakTime();
+ bool CanSpeak() const;
+ void ChangeSpeakTime(int utime);
+
+ /*********************************************************/
+ /*** VARIOUS SYSTEMS ***/
+ /*********************************************************/
+ MovementInfo m_movementInfo;
+ bool isMoving() const { return HasUnitMovementFlag(movementFlagsMask); }
+ bool isMovingOrTurning() const { return HasUnitMovementFlag(movementOrTurningFlagsMask); }
+
+ bool CanFly() const { return HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY); }
+ bool IsFlying() const { return HasUnitMovementFlag(MOVEMENTFLAG_FLYING); }
+
+ void HandleDrowning();
+
+ void SetClientControl(Unit* target, uint8 allowMove);
+
+ // Transports
+ Transport * GetTransport() const { return m_transport; }
+ void SetTransport(Transport * t) { m_transport = t; }
+
+ float GetTransOffsetX() const { return m_movementInfo.t_x; }
+ float GetTransOffsetY() const { return m_movementInfo.t_y; }
+ float GetTransOffsetZ() const { return m_movementInfo.t_z; }
+ float GetTransOffsetO() const { return m_movementInfo.t_o; }
+ uint32 GetTransTime() const { return m_movementInfo.t_time; }
+
+ uint32 GetSaveTimer() const { return m_nextSave; }
+ void SetSaveTimer(uint32 timer) { m_nextSave = timer; }
+
+ // Recall position
+ uint32 m_recallMap;
+ float m_recallX;
+ float m_recallY;
+ float m_recallZ;
+ float m_recallO;
+ void SaveRecallPosition();
+
+ // Homebind coordinates
+ uint32 m_homebindMapId;
+ uint16 m_homebindZoneId;
+ float m_homebindX;
+ float m_homebindY;
+ float m_homebindZ;
+
+ // currently visible objects at player client
+ typedef std::set<uint64> ClientGUIDs;
+ ClientGUIDs m_clientGUIDs;
+
+ bool HaveAtClient(WorldObject const* u) { return u==this || m_clientGUIDs.find(u->GetGUID())!=m_clientGUIDs.end(); }
+
+ bool IsVisibleInGridForPlayer(Player* pl) const;
+ bool IsVisibleGloballyFor(Player* pl) const;
+
+ void UpdateVisibilityOf(WorldObject* target);
+
+ template<class T>
+ void UpdateVisibilityOf(T* target, UpdateData& data, UpdateDataMapType& data_updates, std::set<WorldObject*>& visibleNow);
+
+ // Stealth detection system
+ uint32 m_DetectInvTimer;
+ void HandleStealthedUnitsDetection();
+
+ uint8 m_forced_speed_changes[MAX_MOVE_TYPE];
+
+ bool HasAtLoginFlag(AtLoginFlags f) const { return m_atLoginFlags & f; }
+ void SetAtLoginFlag(AtLoginFlags f) { m_atLoginFlags |= f; }
+
+ LookingForGroup m_lookingForGroup;
+
+ // Temporarily removed pet cache
+ uint32 GetTemporaryUnsummonedPetNumber() const { return m_temporaryUnsummonedPetNumber; }
+ void SetTemporaryUnsummonedPetNumber(uint32 petnumber) { m_temporaryUnsummonedPetNumber = petnumber; }
+ uint32 GetOldPetSpell() const { return m_oldpetspell; }
+ void SetOldPetSpell(uint32 petspell) { m_oldpetspell = petspell; }
+
+ /*********************************************************/
+ /*** INSTANCE SYSTEM ***/
+ /*********************************************************/
+
+ typedef HM_NAMESPACE::hash_map< uint32 /*mapId*/, InstancePlayerBind > BoundInstancesMap;
+
+ void UpdateHomebindTime(uint32 time);
+
+ uint32 m_HomebindTimer;
+ bool m_InstanceValid;
+ // permanent binds and solo binds by difficulty
+ BoundInstancesMap m_boundInstances[TOTAL_DIFFICULTIES];
+ InstancePlayerBind* GetBoundInstance(uint32 mapid, uint8 difficulty);
+ BoundInstancesMap& GetBoundInstances(uint8 difficulty) { return m_boundInstances[difficulty]; }
+ void UnbindInstance(uint32 mapid, uint8 difficulty, bool unload = false);
+ void UnbindInstance(BoundInstancesMap::iterator &itr, uint8 difficulty, bool unload = false);
+ InstancePlayerBind* BindToInstance(InstanceSave *save, bool permanent, bool load = false);
+ void SendRaidInfo();
+ void SendSavedInstances();
+ static void ConvertInstancesToGroup(Player *player, Group *group = NULL, uint64 player_guid = 0);
+
+ /*********************************************************/
+ /*** GROUP SYSTEM ***/
+ /*********************************************************/
+
+ Group * GetGroupInvite() { return m_groupInvite; }
+ void SetGroupInvite(Group *group) { m_groupInvite = group; }
+ Group * GetGroup() { return m_group.getTarget(); }
+ const Group * GetGroup() const { return (const Group*)m_group.getTarget(); }
+ GroupReference& GetGroupRef() { return m_group; }
+ void SetGroup(Group *group, int8 subgroup = -1);
+ uint8 GetSubGroup() const { return m_group.getSubGroup(); }
+ uint32 GetGroupUpdateFlag() { return m_groupUpdateMask; }
+ void SetGroupUpdateFlag(uint32 flag) { m_groupUpdateMask |= flag; }
+ uint64 GetAuraUpdateMask() { return m_auraUpdateMask; }
+ void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); }
+ Player* GetNextRandomRaidMember(float radius);
+
+ GridReference<Player> &GetGridRef() { return m_gridRef; }
+ bool isAllowedToLoot(Creature* creature);
+
+ WorldLocation& GetTeleportDest() { return m_teleport_dest; }
+
+ DeclinedName const* GetDeclinedNames() const { return m_declinedname; }
+
+ protected:
+
+ /*********************************************************/
+ /*** BATTLEGROUND SYSTEM ***/
+ /*********************************************************/
+
+ /* this variable is set to bg->m_InstanceID, when player is teleported to BG - (it is battleground's GUID)*/
+ uint32 m_bgBattleGroundID;
+ /*
+ this is an array of BG queues (BgTypeIDs) in which is player
+ */
+ struct BgBattleGroundQueueID_Rec
+ {
+ uint32 bgQueueType;
+ uint32 invitedToInstance;
+ };
+ BgBattleGroundQueueID_Rec m_bgBattleGroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES];
+ uint32 m_bgEntryPointMap;
+ float m_bgEntryPointX;
+ float m_bgEntryPointY;
+ float m_bgEntryPointZ;
+ float m_bgEntryPointO;
+
+ std::set<uint32> m_bgAfkReporter;
+ uint8 m_bgAfkReportedCount;
+ time_t m_bgAfkReportedTimer;
+ uint32 m_contestedPvPTimer;
+
+ uint32 m_bgTeam; // what side the player will be added to
+
+ /*********************************************************/
+ /*** QUEST SYSTEM ***/
+ /*********************************************************/
+
+ std::set<uint32> m_timedquests;
+
+ uint64 m_divider;
+ uint32 m_ingametime;
+
+ /*********************************************************/
+ /*** LOAD SYSTEM ***/
+ /*********************************************************/
+
+ void _LoadActions(QueryResult *result);
+ void _LoadAuras(QueryResult *result, uint32 timediff);
+ void _LoadBoundInstances(QueryResult *result);
+ void _LoadInventory(QueryResult *result, uint32 timediff);
+ void _LoadMailInit(QueryResult *resultUnread, QueryResult *resultDelivery);
+ void _LoadMail();
+ void _LoadMailedItems(Mail *mail);
+ void _LoadQuestStatus(QueryResult *result);
+ void _LoadDailyQuestStatus(QueryResult *result);
+ void _LoadGroup(QueryResult *result);
+ void _LoadReputation(QueryResult *result);
+ void _LoadSpells(QueryResult *result);
+ void _LoadTutorials(QueryResult *result);
+ void _LoadFriendList(QueryResult *result);
+ bool _LoadHomeBind(QueryResult *result);
+ void _LoadDeclinedNames(QueryResult *result);
+
+ /*********************************************************/
+ /*** SAVE SYSTEM ***/
+ /*********************************************************/
+
+ void _SaveActions();
+ void _SaveAuras();
+ void _SaveInventory();
+ void _SaveMail();
+ void _SaveQuestStatus();
+ void _SaveDailyQuestStatus();
+ void _SaveReputation();
+ void _SaveSpells();
+ void _SaveTutorials();
+
+ void _SetCreateBits(UpdateMask *updateMask, Player *target) const;
+ void _SetUpdateBits(UpdateMask *updateMask, Player *target) const;
+
+ /*********************************************************/
+ /*** ENVIRONMENTAL SYSTEM ***/
+ /*********************************************************/
+ void HandleLava();
+ void HandleSobering();
+ void StartMirrorTimer(MirrorTimerType Type, uint32 MaxValue);
+ void ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, uint32 Regen);
+ void StopMirrorTimer(MirrorTimerType Type);
+ uint8 m_isunderwater;
+ bool m_isInWater;
+
+ /*********************************************************/
+ /*** HONOR SYSTEM ***/
+ /*********************************************************/
+ time_t m_lastHonorUpdateTime;
+
+ void outDebugValues() const;
+ bool _removeSpell(uint16 spell_id);
+ uint64 m_lootGuid;
+
+ uint32 m_race;
+ uint32 m_class;
+ uint32 m_team;
+ uint32 m_nextSave;
+ time_t m_speakTime;
+ uint32 m_speakCount;
+ uint32 m_dungeonDifficulty;
+
+ uint32 m_atLoginFlags;
+
+ Item* m_items[PLAYER_SLOTS_COUNT];
+ uint32 m_currentBuybackSlot;
+
+ std::vector<Item*> m_itemUpdateQueue;
+ bool m_itemUpdateQueueBlocked;
+
+ uint32 m_ExtraFlags;
+ uint64 m_curSelection;
+
+ uint64 m_comboTarget;
+ int8 m_comboPoints;
+
+ QuestStatusMap mQuestStatus;
+
+ uint32 m_GuildIdInvited;
+ uint32 m_ArenaTeamIdInvited;
+
+ PlayerMails m_mail;
+ PlayerSpellMap m_spells;
+ SpellCooldowns m_spellCooldowns;
+
+ ActionButtonList m_actionButtons;
+
+ float m_auraBaseMod[BASEMOD_END][MOD_END];
+
+ SpellModList m_spellMods[MAX_SPELLMOD];
+ int32 m_SpellModRemoveCount;
+ EnchantDurationList m_enchantDuration;
+ ItemDurationList m_itemDuration;
+
+ uint64 m_resurrectGUID;
+ uint32 m_resurrectMap;
+ float m_resurrectX, m_resurrectY, m_resurrectZ;
+ uint32 m_resurrectHealth, m_resurrectMana;
+
+ WorldSession *m_session;
+
+ typedef std::list<Channel*> JoinedChannelsList;
+ JoinedChannelsList m_channels;
+
+ bool m_dontMove;
+
+ int m_cinematic;
+
+ Player *pTrader;
+ bool acceptTrade;
+ uint16 tradeItems[TRADE_SLOT_COUNT];
+ uint32 tradeGold;
+
+ time_t m_nextThinkTime;
+
+ uint32 m_Tutorials[8];
+ bool m_TutorialsChanged;
+
+ bool m_DailyQuestChanged;
+ time_t m_lastDailyQuestTime;
+
+ uint32 m_regenTimer;
+ uint32 m_breathTimer;
+ uint32 m_drunkTimer;
+ uint16 m_drunk;
+ uint32 m_weaponChangeTimer;
+
+ uint32 m_zoneUpdateId;
+ uint32 m_zoneUpdateTimer;
+ uint32 m_areaUpdateId;
+
+ uint32 m_deathTimer;
+ time_t m_deathExpireTime;
+
+ uint32 m_restTime;
+
+ uint32 m_WeaponProficiency;
+ uint32 m_ArmorProficiency;
+ bool m_canParry;
+ bool m_canBlock;
+ bool m_canDualWield;
+ uint8 m_swingErrorMsg;
+ float m_ammoDPS;
+ ////////////////////Rest System/////////////////////
+ int time_inn_enter;
+ uint32 inn_pos_mapid;
+ float inn_pos_x;
+ float inn_pos_y;
+ float inn_pos_z;
+ float m_rest_bonus;
+ RestType rest_type;
+ ////////////////////Rest System/////////////////////
+
+ // Transports
+ Transport * m_transport;
+
+ uint32 m_resetTalentsCost;
+ time_t m_resetTalentsTime;
+ uint32 m_usedTalentCount;
+
+ // Social
+ PlayerSocial *m_social;
+
+ // Groups
+ GroupReference m_group;
+ Group *m_groupInvite;
+ uint32 m_groupUpdateMask;
+ uint64 m_auraUpdateMask;
+
+ // Temporarily removed pet cache
+ uint32 m_temporaryUnsummonedPetNumber;
+ uint32 m_oldpetspell;
+
+ uint64 m_miniPet;
+ GuardianPetList m_guardianPets;
+
+ // Player summoning
+ time_t m_summon_expire;
+ uint32 m_summon_mapid;
+ float m_summon_x;
+ float m_summon_y;
+ float m_summon_z;
+
+ // Far Teleport
+ WorldLocation m_teleport_dest;
+
+ DeclinedName *m_declinedname;
+ private:
+ // internal common parts for CanStore/StoreItem functions
+ uint8 _CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool swap, Item *pSrcItem ) const;
+ uint8 _CanStoreItem_InBag( uint8 bag, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot ) const;
+ uint8 _CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot ) const;
+ Item* _StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, bool update );
+
+ GridReference<Player> m_gridRef;
+};
+
+void AddItemsSetItem(Player*player,Item *item);
+void RemoveItemsSetItem(Player*player,ItemPrototype const *proto);
+
+// "the bodies of template functions must be made available in a header file"
+template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell const* spell)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if (!spellInfo) return 0;
+ int32 totalpct = 0;
+ int32 totalflat = 0;
+ for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr)
+ {
+ SpellModifier *mod = *itr;
+
+ if(!IsAffectedBySpellmod(spellInfo,mod,spell))
+ continue;
+ if (mod->type == SPELLMOD_FLAT)
+ totalflat += mod->value;
+ else if (mod->type == SPELLMOD_PCT)
+ {
+ // skip percent mods for null basevalue (most important for spell mods with charges )
+ if(basevalue == T(0))
+ continue;
+
+ // special case (skip >10sec spell casts for instant cast setting)
+ if( mod->op==SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100)
+ continue;
+
+ totalpct += mod->value;
+ }
+
+ if (mod->charges > 0 )
+ {
+ --mod->charges;
+ if (mod->charges == 0)
+ {
+ mod->charges = -1;
+ mod->lastAffected = spell;
+ if(!mod->lastAffected)
+ mod->lastAffected = FindCurrentSpellBySpellId(spellId);
+ ++m_SpellModRemoveCount;
+ }
+ }
+ }
+
+ float diff = (float)basevalue*(float)totalpct/100.0f + (float)totalflat;
+ basevalue = T((float)basevalue + diff);
+ return T(diff);
+}
+#endif
diff --git a/src/game/PointMovementGenerator.cpp b/src/game/PointMovementGenerator.cpp
index b0e685fa412..33660fabde9 100644
--- a/src/game/PointMovementGenerator.cpp
+++ b/src/game/PointMovementGenerator.cpp
@@ -1,76 +1,76 @@
-/*
- * 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 "PointMovementGenerator.h"
-#include "Errors.h"
-#include "Creature.h"
-#include "CreatureAI.h"
-#include "MapManager.h"
-#include "DestinationHolderImp.h"
-
-//----- Point Movement Generator
-template<class T>
-void PointMovementGenerator<T>::Initialize(T &unit)
-{
- unit.StopMoving();
- Traveller<T> traveller(unit);
- i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z);
-
- if (unit.GetTypeId() == TYPEID_UNIT && ((Creature*)&unit)->canFly())
- unit.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
-}
-
-template<class T>
-bool PointMovementGenerator<T>::Update(T &unit, const uint32 &diff)
-{
- if(!&unit)
- return false;
-
- if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED))
- return true;
-
- Traveller<T> traveller(unit);
-
- i_destinationHolder.UpdateTraveller(traveller, diff, false);
-
- if(i_destinationHolder.HasArrived())
- {
- unit.StopMoving();
- MovementInform(unit);
- return false;
- }
-
- return true;
-}
-
-template<class T>
-void PointMovementGenerator<T>::MovementInform(T &unit)
-{
-}
-
-template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit)
-{
- unit.AI()->MovementInform(POINT_MOTION_TYPE, id);
-}
-
-template void PointMovementGenerator<Player>::Initialize(Player&);
-template bool PointMovementGenerator<Player>::Update(Player &, const uint32 &diff);
-template void PointMovementGenerator<Player>::MovementInform(Player&);
-
-template void PointMovementGenerator<Creature>::Initialize(Creature&);
-template bool PointMovementGenerator<Creature>::Update(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 "PointMovementGenerator.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "MapManager.h"
+#include "DestinationHolderImp.h"
+
+//----- Point Movement Generator
+template<class T>
+void PointMovementGenerator<T>::Initialize(T &unit)
+{
+ unit.StopMoving();
+ Traveller<T> traveller(unit);
+ i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z);
+
+ if (unit.GetTypeId() == TYPEID_UNIT && ((Creature*)&unit)->canFly())
+ unit.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+}
+
+template<class T>
+bool PointMovementGenerator<T>::Update(T &unit, const uint32 &diff)
+{
+ if(!&unit)
+ return false;
+
+ if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED))
+ return true;
+
+ Traveller<T> traveller(unit);
+
+ i_destinationHolder.UpdateTraveller(traveller, diff, false);
+
+ if(i_destinationHolder.HasArrived())
+ {
+ unit.StopMoving();
+ MovementInform(unit);
+ return false;
+ }
+
+ return true;
+}
+
+template<class T>
+void PointMovementGenerator<T>::MovementInform(T &unit)
+{
+}
+
+template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit)
+{
+ unit.AI()->MovementInform(POINT_MOTION_TYPE, id);
+}
+
+template void PointMovementGenerator<Player>::Initialize(Player&);
+template bool PointMovementGenerator<Player>::Update(Player &, const uint32 &diff);
+template void PointMovementGenerator<Player>::MovementInform(Player&);
+
+template void PointMovementGenerator<Creature>::Initialize(Creature&);
+template bool PointMovementGenerator<Creature>::Update(Creature&, const uint32 &diff);
diff --git a/src/game/QuestDef.cpp b/src/game/QuestDef.cpp
index d18c931440c..f9c73cef4a9 100644
--- a/src/game/QuestDef.cpp
+++ b/src/game/QuestDef.cpp
@@ -1,200 +1,200 @@
-/*
- * 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 "QuestDef.h"
-#include "Player.h"
-#include "World.h"
-
-Quest::Quest(Field * questRecord)
-{
- QuestId = questRecord[0].GetUInt32();
- QuestMethod = questRecord[1].GetUInt32();
- ZoneOrSort = questRecord[2].GetInt32();
- SkillOrClass = questRecord[3].GetInt32();
- MinLevel = questRecord[4].GetUInt32();
- QuestLevel = questRecord[5].GetUInt32();
- Type = questRecord[6].GetUInt32();
- RequiredRaces = questRecord[7].GetUInt32();
- RequiredSkillValue = questRecord[8].GetUInt32();
- RepObjectiveFaction = questRecord[9].GetUInt32();
- RepObjectiveValue = questRecord[10].GetInt32();
- RequiredMinRepFaction = questRecord[11].GetUInt32();
- RequiredMinRepValue = questRecord[12].GetInt32();
- RequiredMaxRepFaction = questRecord[13].GetUInt32();
- RequiredMaxRepValue = questRecord[14].GetInt32();
- SuggestedPlayers = questRecord[15].GetUInt32();
- LimitTime = questRecord[16].GetUInt32();
- QuestFlags = questRecord[17].GetUInt16();
- uint32 SpecialFlags = questRecord[18].GetUInt16();
- CharTitleId = questRecord[19].GetUInt32();
- PrevQuestId = questRecord[20].GetInt32();
- NextQuestId = questRecord[21].GetInt32();
- ExclusiveGroup = questRecord[22].GetInt32();
- NextQuestInChain = questRecord[23].GetUInt32();
- SrcItemId = questRecord[24].GetUInt32();
- SrcItemCount = questRecord[25].GetUInt32();
- SrcSpell = questRecord[26].GetUInt32();
- Title = questRecord[27].GetCppString();
- Details = questRecord[28].GetCppString();
- Objectives = questRecord[29].GetCppString();
- OfferRewardText = questRecord[30].GetCppString();
- RequestItemsText = questRecord[31].GetCppString();
- EndText = questRecord[32].GetCppString();
-
- for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
- ObjectiveText[i] = questRecord[33+i].GetCppString();
-
- for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
- ReqItemId[i] = questRecord[37+i].GetUInt32();
-
- for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
- ReqItemCount[i] = questRecord[41+i].GetUInt32();
-
- for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
- ReqSourceId[i] = questRecord[45+i].GetUInt32();
-
- for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
- ReqSourceCount[i] = questRecord[49+i].GetUInt32();
-
- for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
- ReqSourceRef[i] = questRecord[53+i].GetUInt32();
-
- for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
- ReqCreatureOrGOId[i] = questRecord[57+i].GetInt32();
-
- for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
- ReqCreatureOrGOCount[i] = questRecord[61+i].GetUInt32();
-
- for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
- ReqSpell[i] = questRecord[65+i].GetUInt32();
-
- for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
- RewChoiceItemId[i] = questRecord[69+i].GetUInt32();
-
- for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
- RewChoiceItemCount[i] = questRecord[75+i].GetUInt32();
-
- for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
- RewItemId[i] = questRecord[81+i].GetUInt32();
-
- for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
- RewItemCount[i] = questRecord[85+i].GetUInt32();
-
- for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
- RewRepFaction[i] = questRecord[89+i].GetUInt32();
-
- for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
- RewRepValue[i] = questRecord[94+i].GetInt32();
-
- RewOrReqMoney = questRecord[99].GetInt32();
- RewMoneyMaxLevel = questRecord[100].GetUInt32();
- RewSpell = questRecord[101].GetUInt32();
- RewSpellCast = questRecord[102].GetUInt32();
- RewMailTemplateId = questRecord[103].GetUInt32();
- RewMailDelaySecs = questRecord[104].GetUInt32();
- PointMapId = questRecord[105].GetUInt32();
- PointX = questRecord[106].GetFloat();
- PointY = questRecord[107].GetFloat();
- PointOpt = questRecord[108].GetUInt32();
-
- for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
- DetailsEmote[i] = questRecord[109+i].GetUInt32();
-
- IncompleteEmote = questRecord[113].GetUInt32();
- CompleteEmote = questRecord[114].GetUInt32();
-
- for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
- OfferRewardEmote[i] = questRecord[115+i].GetInt32();
-
- QuestStartScript = questRecord[119].GetUInt32();
- QuestCompleteScript = questRecord[120].GetUInt32();
-
- QuestFlags |= SpecialFlags << 16;
-
- m_reqitemscount = 0;
- m_reqCreatureOrGOcount = 0;
- m_rewitemscount = 0;
- m_rewchoiceitemscount = 0;
-
- for (int i=0; i < QUEST_OBJECTIVES_COUNT; i++)
- {
- if ( ReqItemId[i] )
- ++m_reqitemscount;
- if ( ReqCreatureOrGOId[i] )
- ++m_reqCreatureOrGOcount;
- }
-
- for (int i=0; i < QUEST_REWARDS_COUNT; i++)
- {
- if ( RewItemId[i] )
- ++m_rewitemscount;
- }
-
- for (int i=0; i < QUEST_REWARD_CHOICES_COUNT; i++)
- {
- if (RewChoiceItemId[i])
- ++m_rewchoiceitemscount;
- }
-}
-
-uint32 Quest::XPValue( Player *pPlayer ) const
-{
- if( pPlayer )
- {
- if( RewMoneyMaxLevel > 0 )
- {
- uint32 pLevel = pPlayer->getLevel();
- uint32 qLevel = QuestLevel;
- float fullxp = 0;
- if (qLevel >= 65)
- fullxp = RewMoneyMaxLevel / 6.0f;
- else if (qLevel == 64)
- fullxp = RewMoneyMaxLevel / 4.8f;
- else if (qLevel == 63)
- fullxp = RewMoneyMaxLevel / 3.6f;
- else if (qLevel == 62)
- fullxp = RewMoneyMaxLevel / 2.4f;
- else if (qLevel == 61)
- fullxp = RewMoneyMaxLevel / 1.2f;
- else if (qLevel > 0 && qLevel <= 60)
- fullxp = RewMoneyMaxLevel / 0.6f;
-
- if( pLevel <= qLevel + 5 )
- return (uint32)fullxp;
- else if( pLevel == qLevel + 6 )
- return (uint32)(fullxp * 0.8f);
- else if( pLevel == qLevel + 7 )
- return (uint32)(fullxp * 0.6f);
- else if( pLevel == qLevel + 8 )
- return (uint32)(fullxp * 0.4f);
- else if( pLevel == qLevel + 9 )
- return (uint32)(fullxp * 0.2f);
- else
- return (uint32)(fullxp * 0.1f);
- }
- }
- return 0;
-}
-
-int32 Quest::GetRewOrReqMoney() const
-{
- if(RewOrReqMoney <=0)
- return RewOrReqMoney;
-
- return int32(RewOrReqMoney * sWorld.getRate(RATE_DROP_MONEY));
-}
+/*
+ * 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 "QuestDef.h"
+#include "Player.h"
+#include "World.h"
+
+Quest::Quest(Field * questRecord)
+{
+ QuestId = questRecord[0].GetUInt32();
+ QuestMethod = questRecord[1].GetUInt32();
+ ZoneOrSort = questRecord[2].GetInt32();
+ SkillOrClass = questRecord[3].GetInt32();
+ MinLevel = questRecord[4].GetUInt32();
+ QuestLevel = questRecord[5].GetUInt32();
+ Type = questRecord[6].GetUInt32();
+ RequiredRaces = questRecord[7].GetUInt32();
+ RequiredSkillValue = questRecord[8].GetUInt32();
+ RepObjectiveFaction = questRecord[9].GetUInt32();
+ RepObjectiveValue = questRecord[10].GetInt32();
+ RequiredMinRepFaction = questRecord[11].GetUInt32();
+ RequiredMinRepValue = questRecord[12].GetInt32();
+ RequiredMaxRepFaction = questRecord[13].GetUInt32();
+ RequiredMaxRepValue = questRecord[14].GetInt32();
+ SuggestedPlayers = questRecord[15].GetUInt32();
+ LimitTime = questRecord[16].GetUInt32();
+ QuestFlags = questRecord[17].GetUInt16();
+ uint32 SpecialFlags = questRecord[18].GetUInt16();
+ CharTitleId = questRecord[19].GetUInt32();
+ PrevQuestId = questRecord[20].GetInt32();
+ NextQuestId = questRecord[21].GetInt32();
+ ExclusiveGroup = questRecord[22].GetInt32();
+ NextQuestInChain = questRecord[23].GetUInt32();
+ SrcItemId = questRecord[24].GetUInt32();
+ SrcItemCount = questRecord[25].GetUInt32();
+ SrcSpell = questRecord[26].GetUInt32();
+ Title = questRecord[27].GetCppString();
+ Details = questRecord[28].GetCppString();
+ Objectives = questRecord[29].GetCppString();
+ OfferRewardText = questRecord[30].GetCppString();
+ RequestItemsText = questRecord[31].GetCppString();
+ EndText = questRecord[32].GetCppString();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ObjectiveText[i] = questRecord[33+i].GetCppString();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqItemId[i] = questRecord[37+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqItemCount[i] = questRecord[41+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceId[i] = questRecord[45+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceCount[i] = questRecord[49+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceRef[i] = questRecord[53+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqCreatureOrGOId[i] = questRecord[57+i].GetInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqCreatureOrGOCount[i] = questRecord[61+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqSpell[i] = questRecord[65+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
+ RewChoiceItemId[i] = questRecord[69+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
+ RewChoiceItemCount[i] = questRecord[75+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
+ RewItemId[i] = questRecord[81+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
+ RewItemCount[i] = questRecord[85+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
+ RewRepFaction[i] = questRecord[89+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
+ RewRepValue[i] = questRecord[94+i].GetInt32();
+
+ RewOrReqMoney = questRecord[99].GetInt32();
+ RewMoneyMaxLevel = questRecord[100].GetUInt32();
+ RewSpell = questRecord[101].GetUInt32();
+ RewSpellCast = questRecord[102].GetUInt32();
+ RewMailTemplateId = questRecord[103].GetUInt32();
+ RewMailDelaySecs = questRecord[104].GetUInt32();
+ PointMapId = questRecord[105].GetUInt32();
+ PointX = questRecord[106].GetFloat();
+ PointY = questRecord[107].GetFloat();
+ PointOpt = questRecord[108].GetUInt32();
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ DetailsEmote[i] = questRecord[109+i].GetUInt32();
+
+ IncompleteEmote = questRecord[113].GetUInt32();
+ CompleteEmote = questRecord[114].GetUInt32();
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ OfferRewardEmote[i] = questRecord[115+i].GetInt32();
+
+ QuestStartScript = questRecord[119].GetUInt32();
+ QuestCompleteScript = questRecord[120].GetUInt32();
+
+ QuestFlags |= SpecialFlags << 16;
+
+ m_reqitemscount = 0;
+ m_reqCreatureOrGOcount = 0;
+ m_rewitemscount = 0;
+ m_rewchoiceitemscount = 0;
+
+ for (int i=0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if ( ReqItemId[i] )
+ ++m_reqitemscount;
+ if ( ReqCreatureOrGOId[i] )
+ ++m_reqCreatureOrGOcount;
+ }
+
+ for (int i=0; i < QUEST_REWARDS_COUNT; i++)
+ {
+ if ( RewItemId[i] )
+ ++m_rewitemscount;
+ }
+
+ for (int i=0; i < QUEST_REWARD_CHOICES_COUNT; i++)
+ {
+ if (RewChoiceItemId[i])
+ ++m_rewchoiceitemscount;
+ }
+}
+
+uint32 Quest::XPValue( Player *pPlayer ) const
+{
+ if( pPlayer )
+ {
+ if( RewMoneyMaxLevel > 0 )
+ {
+ uint32 pLevel = pPlayer->getLevel();
+ uint32 qLevel = QuestLevel;
+ float fullxp = 0;
+ if (qLevel >= 65)
+ fullxp = RewMoneyMaxLevel / 6.0f;
+ else if (qLevel == 64)
+ fullxp = RewMoneyMaxLevel / 4.8f;
+ else if (qLevel == 63)
+ fullxp = RewMoneyMaxLevel / 3.6f;
+ else if (qLevel == 62)
+ fullxp = RewMoneyMaxLevel / 2.4f;
+ else if (qLevel == 61)
+ fullxp = RewMoneyMaxLevel / 1.2f;
+ else if (qLevel > 0 && qLevel <= 60)
+ fullxp = RewMoneyMaxLevel / 0.6f;
+
+ if( pLevel <= qLevel + 5 )
+ return (uint32)fullxp;
+ else if( pLevel == qLevel + 6 )
+ return (uint32)(fullxp * 0.8f);
+ else if( pLevel == qLevel + 7 )
+ return (uint32)(fullxp * 0.6f);
+ else if( pLevel == qLevel + 8 )
+ return (uint32)(fullxp * 0.4f);
+ else if( pLevel == qLevel + 9 )
+ return (uint32)(fullxp * 0.2f);
+ else
+ return (uint32)(fullxp * 0.1f);
+ }
+ }
+ return 0;
+}
+
+int32 Quest::GetRewOrReqMoney() const
+{
+ if(RewOrReqMoney <=0)
+ return RewOrReqMoney;
+
+ return int32(RewOrReqMoney * sWorld.getRate(RATE_DROP_MONEY));
+}
diff --git a/src/game/QuestDef.h b/src/game/QuestDef.h
index fda013337f0..dfa7ec92654 100644
--- a/src/game/QuestDef.h
+++ b/src/game/QuestDef.h
@@ -1,332 +1,332 @@
-/*
- * 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
- */
-
-#ifndef MANGOSSERVER_QUEST_H
-#define MANGOSSERVER_QUEST_H
-
-#include "Platform/Define.h"
-#include "Database/DatabaseEnv.h"
-
-#include <string>
-#include <vector>
-
-class Player;
-
-class ObjectMgr;
-
-#define MAX_QUEST_LOG_SIZE 25
-
-#define QUEST_OBJECTIVES_COUNT 4
-#define QUEST_SOURCE_ITEM_IDS_COUNT 4
-#define QUEST_REWARD_CHOICES_COUNT 6
-#define QUEST_REWARDS_COUNT 4
-#define QUEST_DEPLINK_COUNT 10
-#define QUEST_REPUTATIONS_COUNT 5
-#define QUEST_EMOTE_COUNT 4
-
-enum QuestFailedReasons
-{
- INVALIDREASON_DONT_HAVE_REQ = 0,
- INVALIDREASON_QUEST_FAILED_LOW_LEVEL = 1, //You are not high enough level for that quest.
- INVALIDREASON_QUEST_FAILED_WRONG_RACE = 6, //That quest is not available to your race.
- INVALIDREASON_QUEST_ALREADY_DONE = 7, //You have completed that quest.
- INVALIDREASON_QUEST_ONLY_ONE_TIMED = 12, //You can only be on one timed quest at a time.
- INVALIDREASON_QUEST_ALREADY_ON = 13, //You are already on that quest
- INVALIDREASON_QUEST_FAILED_EXPANSION = 16, //This quest requires an expansion enabled account.
- INVALIDREASON_QUEST_ALREADY_ON2 = 18, //You are already on that quest
- INVALIDREASON_QUEST_FAILED_MISSING_ITEMS = 21, //You don't have the required items with you. Check storage.
- INVALIDREASON_QUEST_FAILED_NOT_ENOUGH_MONEY = 23, //You don't have enough money for that quest.
- INVALIDREASON_DAILY_QUESTS_REMAINING = 26, //You have already completed 10 daily quests today
- INVALIDREASON_QUEST_FAILED_CAIS = 27, //You cannot complete quests once you have reached tired time
-};
-
-enum QuestShareMessages
-{
- QUEST_PARTY_MSG_SHARING_QUEST = 0,
- QUEST_PARTY_MSG_CANT_TAKE_QUEST = 1,
- QUEST_PARTY_MSG_ACCEPT_QUEST = 2,
- QUEST_PARTY_MSG_REFUSE_QUEST = 3,
- QUEST_PARTY_MSG_TOO_FAR = 4,
- QUEST_PARTY_MSG_BUSY = 5,
- QUEST_PARTY_MSG_LOG_FULL = 6,
- QUEST_PARTY_MSG_HAVE_QUEST = 7,
- QUEST_PARTY_MSG_FINISH_QUEST = 8,
-};
-
-enum __QuestTradeSkill
-{
- QUEST_TRSKILL_NONE = 0,
- QUEST_TRSKILL_ALCHEMY = 1,
- QUEST_TRSKILL_BLACKSMITHING = 2,
- QUEST_TRSKILL_COOKING = 3,
- QUEST_TRSKILL_ENCHANTING = 4,
- QUEST_TRSKILL_ENGINEERING = 5,
- QUEST_TRSKILL_FIRSTAID = 6,
- QUEST_TRSKILL_HERBALISM = 7,
- QUEST_TRSKILL_LEATHERWORKING = 8,
- QUEST_TRSKILL_POISONS = 9,
- QUEST_TRSKILL_TAILORING = 10,
- QUEST_TRSKILL_MINING = 11,
- QUEST_TRSKILL_FISHING = 12,
- QUEST_TRSKILL_SKINNING = 13,
- QUEST_TRSKILL_JEWELCRAFTING = 14,
-};
-
-enum QuestStatus
-{
- QUEST_STATUS_NONE = 0,
- QUEST_STATUS_COMPLETE = 1,
- QUEST_STATUS_UNAVAILABLE = 2,
- QUEST_STATUS_INCOMPLETE = 3,
- QUEST_STATUS_AVAILABLE = 4,
- MAX_QUEST_STATUS
-};
-
-enum __QuestGiverStatus
-{
- DIALOG_STATUS_NONE = 0,
- DIALOG_STATUS_UNAVAILABLE = 1,
- DIALOG_STATUS_CHAT = 2,
- DIALOG_STATUS_INCOMPLETE = 3,
- DIALOG_STATUS_REWARD_REP = 4,
- DIALOG_STATUS_AVAILABLE_REP = 5,
- DIALOG_STATUS_AVAILABLE = 6,
- DIALOG_STATUS_REWARD2 = 7, // not yellow dot on minimap
- DIALOG_STATUS_REWARD = 8 // yellow dot on minimap
-};
-
-enum __QuestFlags
-{
- // Flags used at server and sended to client
- QUEST_FLAGS_STAY_ALIVE = 1, // Not used currently
- QUEST_FLAGS_PARTY_ACCEPT = 2, // Not used currently. If player in party, all players that can accept this quest will receive confirmation box to accept quest CMSG_QUEST_CONFIRM_ACCEPT/SMSG_QUEST_CONFIRM_ACCEPT
- QUEST_FLAGS_EXPLORATION = 4, // Not used currently
- QUEST_FLAGS_SHARABLE = 8, // Can be shared: Player::CanShareQuest()
- //QUEST_FLAGS_NONE2 = 16, // Not used currently
- QUEST_FLAGS_EPIC = 32, // Not used currently: Unsure of content
- QUEST_FLAGS_RAID = 64, // Not used currently
- QUEST_FLAGS_TBC = 128, // Not used currently: Available if TBC expension enabled only
- QUEST_FLAGS_UNK2 = 256, // Not used currently: _DELIVER_MORE Quest needs more than normal _q-item_ drops from mobs
- QUEST_FLAGS_HIDDEN_REWARDS = 512, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUESTGIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE))
- QUEST_FLAGS_AUTO_REWARDED = 1024, // These quests are automatically rewarded on quest complete and they will never appear in quest log client side.
- QUEST_FLAGS_TBC_RACES = 2048, // Not used currently: Bloodelf/draenei starting zone quests
- QUEST_FLAGS_DAILY = 4096, // Used to know quest is Daily one
-
- // Mangos flags for set SpecialFlags in DB if required but used only at server
- QUEST_MANGOS_FLAGS_REPEATABLE = 0x010000, // Set by 1 in SpecialFlags from DB
- QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT = 0x020000, // Set by 2 in SpecialFlags from DB (if reequired area explore, spell SPELL_EFFECT_QUEST_COMPLETE casting, table `*_script` command SCRIPT_COMMAND_QUEST_EXPLORED use, set from script DLL)
- QUEST_MANGOS_FLAGS_DB_ALLOWED = 0xFFFF | QUEST_MANGOS_FLAGS_REPEATABLE | QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT,
-
- // Mangos flags for internal use only
- QUEST_MANGOS_FLAGS_DELIVER = 0x040000, // Internal flag computed only
- QUEST_MANGOS_FLAGS_SPEAKTO = 0x080000, // Internal flag computed only
- QUEST_MANGOS_FLAGS_KILL_OR_CAST = 0x100000, // Internal flag computed only
- QUEST_MANGOS_FLAGS_TIMED = 0x200000, // Internal flag computed only
-};
-
-struct QuestLocale
-{
- QuestLocale() { ObjectiveText.resize(QUEST_OBJECTIVES_COUNT); }
-
- std::vector<std::string> Title;
- std::vector<std::string> Details;
- std::vector<std::string> Objectives;
- std::vector<std::string> OfferRewardText;
- std::vector<std::string> RequestItemsText;
- std::vector<std::string> EndText;
- std::vector< std::vector<std::string> > ObjectiveText;
-};
-
-// This Quest class provides a convenient way to access a few pretotaled (cached) quest details,
-// all base quest information, and any utility functions such as generating the amount of
-// xp to give
-class Quest
-{
- friend class ObjectMgr;
- public:
- Quest(Field * questRecord);
- uint32 XPValue( Player *pPlayer ) const;
-
- bool HasFlag( uint32 flag ) const { return ( QuestFlags & flag ) != 0; }
- void SetFlag( uint32 flag ) { QuestFlags |= flag; }
-
- // table data accessors:
- uint32 GetQuestId() const { return QuestId; }
- uint32 GetQuestMethod() const { return QuestMethod; }
- int32 GetZoneOrSort() const { return ZoneOrSort; }
- int32 GetSkillOrClass() const { return SkillOrClass; }
- uint32 GetMinLevel() const { return MinLevel; }
- uint32 GetQuestLevel() const { return QuestLevel; }
- uint32 GetType() const { return Type; }
- uint32 GetRequiredRaces() const { return RequiredRaces; }
- uint32 GetRequiredSkillValue() const { return RequiredSkillValue; }
- uint32 GetRepObjectiveFaction() const { return RepObjectiveFaction; }
- int32 GetRepObjectiveValue() const { return RepObjectiveValue; }
- uint32 GetRequiredMinRepFaction() const { return RequiredMinRepFaction; }
- int32 GetRequiredMinRepValue() const { return RequiredMinRepValue; }
- uint32 GetRequiredMaxRepFaction() const { return RequiredMaxRepFaction; }
- int32 GetRequiredMaxRepValue() const { return RequiredMaxRepValue; }
- uint32 GetSuggestedPlayers() const { return SuggestedPlayers; }
- uint32 GetLimitTime() const { return LimitTime; }
- int32 GetPrevQuestId() const { return PrevQuestId; }
- int32 GetNextQuestId() const { return NextQuestId; }
- int32 GetExclusiveGroup() const { return ExclusiveGroup; }
- uint32 GetNextQuestInChain() const { return NextQuestInChain; }
- uint32 GetCharTitleId() const { return CharTitleId; }
- uint32 GetSrcItemId() const { return SrcItemId; }
- uint32 GetSrcItemCount() const { return SrcItemCount; }
- uint32 GetSrcSpell() const { return SrcSpell; }
- std::string GetTitle() const { return Title; }
- std::string GetDetails() const { return Details; }
- std::string GetObjectives() const { return Objectives; }
- std::string GetOfferRewardText() const { return OfferRewardText; }
- std::string GetRequestItemsText() const { return RequestItemsText; }
- std::string GetEndText() const { return EndText; }
- int32 GetRewOrReqMoney() const;
- uint32 GetRewMoneyMaxLevel() const { return RewMoneyMaxLevel; }
- // use in XP calculation at client
- uint32 GetRewSpell() const { return RewSpell; }
- uint32 GetRewSpellCast() const { return RewSpellCast; }
- uint32 GetRewMailTemplateId() const { return RewMailTemplateId; }
- uint32 GetRewMailDelaySecs() const { return RewMailDelaySecs; }
- uint32 GetPointMapId() const { return PointMapId; }
- float GetPointX() const { return PointX; }
- float GetPointY() const { return PointY; }
- uint32 GetPointOpt() const { return PointOpt; }
- uint32 GetIncompleteEmote() const { return IncompleteEmote; }
- uint32 GetCompleteEmote() const { return CompleteEmote; }
- uint32 GetQuestStartScript() const { return QuestStartScript; }
- uint32 GetQuestCompleteScript() const { return QuestCompleteScript; }
- bool IsRepeatable() const { return QuestFlags & QUEST_MANGOS_FLAGS_REPEATABLE; }
- bool IsAutoComplete() const { return QuestMethod ? false : true; }
- uint32 GetFlags() const { return QuestFlags; }
- bool IsDaily() const { return QuestFlags & QUEST_FLAGS_DAILY; }
-
- // multiple values
- std::string ObjectiveText[QUEST_OBJECTIVES_COUNT];
- uint32 ReqItemId[QUEST_OBJECTIVES_COUNT];
- uint32 ReqItemCount[QUEST_OBJECTIVES_COUNT];
- uint32 ReqSourceId[QUEST_SOURCE_ITEM_IDS_COUNT];
- uint32 ReqSourceCount[QUEST_SOURCE_ITEM_IDS_COUNT];
- uint32 ReqSourceRef[QUEST_SOURCE_ITEM_IDS_COUNT];
- int32 ReqCreatureOrGOId[QUEST_OBJECTIVES_COUNT]; // >0 Creature <0 Gameobject
- uint32 ReqCreatureOrGOCount[QUEST_OBJECTIVES_COUNT];
- uint32 ReqSpell[QUEST_OBJECTIVES_COUNT];
- uint32 RewChoiceItemId[QUEST_REWARD_CHOICES_COUNT];
- uint32 RewChoiceItemCount[QUEST_REWARD_CHOICES_COUNT];
- uint32 RewItemId[QUEST_REWARDS_COUNT];
- uint32 RewItemCount[QUEST_REWARDS_COUNT];
- uint32 RewRepFaction[QUEST_REPUTATIONS_COUNT];
- int32 RewRepValue[QUEST_REPUTATIONS_COUNT];
- uint32 DetailsEmote[QUEST_EMOTE_COUNT];
- uint32 OfferRewardEmote[QUEST_EMOTE_COUNT];
-
- uint32 GetReqItemsCount() const { return m_reqitemscount; }
- uint32 GetReqCreatureOrGOcount() const { return m_reqCreatureOrGOcount; }
- uint32 GetRewChoiceItemsCount() const { return m_rewchoiceitemscount; }
- uint32 GetRewItemsCount() const { return m_rewitemscount; }
-
- typedef std::vector<int32> PrevQuests;
- PrevQuests prevQuests;
- typedef std::vector<uint32> PrevChainQuests;
- PrevChainQuests prevChainQuests;
-
- // cached data
- private:
- uint32 m_reqitemscount;
- uint32 m_reqCreatureOrGOcount;
- uint32 m_rewchoiceitemscount;
- uint32 m_rewitemscount;
-
- // table data
- protected:
- uint32 QuestId;
- uint32 QuestMethod;
- int32 ZoneOrSort;
- int32 SkillOrClass;
- uint32 MinLevel;
- uint32 QuestLevel;
- uint32 Type;
- uint32 RequiredRaces;
- uint32 RequiredSkillValue;
- uint32 RepObjectiveFaction;
- int32 RepObjectiveValue;
- uint32 RequiredMinRepFaction;
- int32 RequiredMinRepValue;
- uint32 RequiredMaxRepFaction;
- int32 RequiredMaxRepValue;
- uint32 SuggestedPlayers;
- uint32 LimitTime;
- uint32 QuestFlags;
- uint32 CharTitleId;
- int32 PrevQuestId;
- int32 NextQuestId;
- int32 ExclusiveGroup;
- uint32 NextQuestInChain;
- uint32 SrcItemId;
- uint32 SrcItemCount;
- uint32 SrcSpell;
- std::string Title;
- std::string Details;
- std::string Objectives;
- std::string OfferRewardText;
- std::string RequestItemsText;
- std::string EndText;
- int32 RewOrReqMoney;
- uint32 RewMoneyMaxLevel;
- uint32 RewSpell;
- uint32 RewSpellCast;
- uint32 RewMailTemplateId;
- uint32 RewMailDelaySecs;
- uint32 PointMapId;
- float PointX;
- float PointY;
- uint32 PointOpt;
- uint32 IncompleteEmote;
- uint32 CompleteEmote;
- uint32 QuestStartScript;
- uint32 QuestCompleteScript;
-};
-
-enum QuestUpdateState
-{
- QUEST_UNCHANGED = 0,
- QUEST_CHANGED = 1,
- QUEST_NEW = 2
-};
-
-struct QuestStatusData
-{
- QuestStatusData()
- : m_status(QUEST_STATUS_NONE),m_rewarded(false),
- m_explored(false), m_timer(0), uState(QUEST_NEW)
- {
- memset(m_itemcount, 0, QUEST_OBJECTIVES_COUNT * sizeof(uint32));
- memset(m_creatureOrGOcount, 0, QUEST_OBJECTIVES_COUNT * sizeof(uint32));
- }
-
- QuestStatus m_status;
- bool m_rewarded;
- bool m_explored;
- uint32 m_timer;
- QuestUpdateState uState;
-
- uint32 m_itemcount[ QUEST_OBJECTIVES_COUNT ];
- uint32 m_creatureOrGOcount[ QUEST_OBJECTIVES_COUNT ];
-};
-#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
+ */
+
+#ifndef MANGOSSERVER_QUEST_H
+#define MANGOSSERVER_QUEST_H
+
+#include "Platform/Define.h"
+#include "Database/DatabaseEnv.h"
+
+#include <string>
+#include <vector>
+
+class Player;
+
+class ObjectMgr;
+
+#define MAX_QUEST_LOG_SIZE 25
+
+#define QUEST_OBJECTIVES_COUNT 4
+#define QUEST_SOURCE_ITEM_IDS_COUNT 4
+#define QUEST_REWARD_CHOICES_COUNT 6
+#define QUEST_REWARDS_COUNT 4
+#define QUEST_DEPLINK_COUNT 10
+#define QUEST_REPUTATIONS_COUNT 5
+#define QUEST_EMOTE_COUNT 4
+
+enum QuestFailedReasons
+{
+ INVALIDREASON_DONT_HAVE_REQ = 0,
+ INVALIDREASON_QUEST_FAILED_LOW_LEVEL = 1, //You are not high enough level for that quest.
+ INVALIDREASON_QUEST_FAILED_WRONG_RACE = 6, //That quest is not available to your race.
+ INVALIDREASON_QUEST_ALREADY_DONE = 7, //You have completed that quest.
+ INVALIDREASON_QUEST_ONLY_ONE_TIMED = 12, //You can only be on one timed quest at a time.
+ INVALIDREASON_QUEST_ALREADY_ON = 13, //You are already on that quest
+ INVALIDREASON_QUEST_FAILED_EXPANSION = 16, //This quest requires an expansion enabled account.
+ INVALIDREASON_QUEST_ALREADY_ON2 = 18, //You are already on that quest
+ INVALIDREASON_QUEST_FAILED_MISSING_ITEMS = 21, //You don't have the required items with you. Check storage.
+ INVALIDREASON_QUEST_FAILED_NOT_ENOUGH_MONEY = 23, //You don't have enough money for that quest.
+ INVALIDREASON_DAILY_QUESTS_REMAINING = 26, //You have already completed 10 daily quests today
+ INVALIDREASON_QUEST_FAILED_CAIS = 27, //You cannot complete quests once you have reached tired time
+};
+
+enum QuestShareMessages
+{
+ QUEST_PARTY_MSG_SHARING_QUEST = 0,
+ QUEST_PARTY_MSG_CANT_TAKE_QUEST = 1,
+ QUEST_PARTY_MSG_ACCEPT_QUEST = 2,
+ QUEST_PARTY_MSG_REFUSE_QUEST = 3,
+ QUEST_PARTY_MSG_TOO_FAR = 4,
+ QUEST_PARTY_MSG_BUSY = 5,
+ QUEST_PARTY_MSG_LOG_FULL = 6,
+ QUEST_PARTY_MSG_HAVE_QUEST = 7,
+ QUEST_PARTY_MSG_FINISH_QUEST = 8,
+};
+
+enum __QuestTradeSkill
+{
+ QUEST_TRSKILL_NONE = 0,
+ QUEST_TRSKILL_ALCHEMY = 1,
+ QUEST_TRSKILL_BLACKSMITHING = 2,
+ QUEST_TRSKILL_COOKING = 3,
+ QUEST_TRSKILL_ENCHANTING = 4,
+ QUEST_TRSKILL_ENGINEERING = 5,
+ QUEST_TRSKILL_FIRSTAID = 6,
+ QUEST_TRSKILL_HERBALISM = 7,
+ QUEST_TRSKILL_LEATHERWORKING = 8,
+ QUEST_TRSKILL_POISONS = 9,
+ QUEST_TRSKILL_TAILORING = 10,
+ QUEST_TRSKILL_MINING = 11,
+ QUEST_TRSKILL_FISHING = 12,
+ QUEST_TRSKILL_SKINNING = 13,
+ QUEST_TRSKILL_JEWELCRAFTING = 14,
+};
+
+enum QuestStatus
+{
+ QUEST_STATUS_NONE = 0,
+ QUEST_STATUS_COMPLETE = 1,
+ QUEST_STATUS_UNAVAILABLE = 2,
+ QUEST_STATUS_INCOMPLETE = 3,
+ QUEST_STATUS_AVAILABLE = 4,
+ MAX_QUEST_STATUS
+};
+
+enum __QuestGiverStatus
+{
+ DIALOG_STATUS_NONE = 0,
+ DIALOG_STATUS_UNAVAILABLE = 1,
+ DIALOG_STATUS_CHAT = 2,
+ DIALOG_STATUS_INCOMPLETE = 3,
+ DIALOG_STATUS_REWARD_REP = 4,
+ DIALOG_STATUS_AVAILABLE_REP = 5,
+ DIALOG_STATUS_AVAILABLE = 6,
+ DIALOG_STATUS_REWARD2 = 7, // not yellow dot on minimap
+ DIALOG_STATUS_REWARD = 8 // yellow dot on minimap
+};
+
+enum __QuestFlags
+{
+ // Flags used at server and sended to client
+ QUEST_FLAGS_STAY_ALIVE = 1, // Not used currently
+ QUEST_FLAGS_PARTY_ACCEPT = 2, // Not used currently. If player in party, all players that can accept this quest will receive confirmation box to accept quest CMSG_QUEST_CONFIRM_ACCEPT/SMSG_QUEST_CONFIRM_ACCEPT
+ QUEST_FLAGS_EXPLORATION = 4, // Not used currently
+ QUEST_FLAGS_SHARABLE = 8, // Can be shared: Player::CanShareQuest()
+ //QUEST_FLAGS_NONE2 = 16, // Not used currently
+ QUEST_FLAGS_EPIC = 32, // Not used currently: Unsure of content
+ QUEST_FLAGS_RAID = 64, // Not used currently
+ QUEST_FLAGS_TBC = 128, // Not used currently: Available if TBC expension enabled only
+ QUEST_FLAGS_UNK2 = 256, // Not used currently: _DELIVER_MORE Quest needs more than normal _q-item_ drops from mobs
+ QUEST_FLAGS_HIDDEN_REWARDS = 512, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUESTGIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE))
+ QUEST_FLAGS_AUTO_REWARDED = 1024, // These quests are automatically rewarded on quest complete and they will never appear in quest log client side.
+ QUEST_FLAGS_TBC_RACES = 2048, // Not used currently: Bloodelf/draenei starting zone quests
+ QUEST_FLAGS_DAILY = 4096, // Used to know quest is Daily one
+
+ // Mangos flags for set SpecialFlags in DB if required but used only at server
+ QUEST_MANGOS_FLAGS_REPEATABLE = 0x010000, // Set by 1 in SpecialFlags from DB
+ QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT = 0x020000, // Set by 2 in SpecialFlags from DB (if reequired area explore, spell SPELL_EFFECT_QUEST_COMPLETE casting, table `*_script` command SCRIPT_COMMAND_QUEST_EXPLORED use, set from script DLL)
+ QUEST_MANGOS_FLAGS_DB_ALLOWED = 0xFFFF | QUEST_MANGOS_FLAGS_REPEATABLE | QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT,
+
+ // Mangos flags for internal use only
+ QUEST_MANGOS_FLAGS_DELIVER = 0x040000, // Internal flag computed only
+ QUEST_MANGOS_FLAGS_SPEAKTO = 0x080000, // Internal flag computed only
+ QUEST_MANGOS_FLAGS_KILL_OR_CAST = 0x100000, // Internal flag computed only
+ QUEST_MANGOS_FLAGS_TIMED = 0x200000, // Internal flag computed only
+};
+
+struct QuestLocale
+{
+ QuestLocale() { ObjectiveText.resize(QUEST_OBJECTIVES_COUNT); }
+
+ std::vector<std::string> Title;
+ std::vector<std::string> Details;
+ std::vector<std::string> Objectives;
+ std::vector<std::string> OfferRewardText;
+ std::vector<std::string> RequestItemsText;
+ std::vector<std::string> EndText;
+ std::vector< std::vector<std::string> > ObjectiveText;
+};
+
+// This Quest class provides a convenient way to access a few pretotaled (cached) quest details,
+// all base quest information, and any utility functions such as generating the amount of
+// xp to give
+class Quest
+{
+ friend class ObjectMgr;
+ public:
+ Quest(Field * questRecord);
+ uint32 XPValue( Player *pPlayer ) const;
+
+ bool HasFlag( uint32 flag ) const { return ( QuestFlags & flag ) != 0; }
+ void SetFlag( uint32 flag ) { QuestFlags |= flag; }
+
+ // table data accessors:
+ uint32 GetQuestId() const { return QuestId; }
+ uint32 GetQuestMethod() const { return QuestMethod; }
+ int32 GetZoneOrSort() const { return ZoneOrSort; }
+ int32 GetSkillOrClass() const { return SkillOrClass; }
+ uint32 GetMinLevel() const { return MinLevel; }
+ uint32 GetQuestLevel() const { return QuestLevel; }
+ uint32 GetType() const { return Type; }
+ uint32 GetRequiredRaces() const { return RequiredRaces; }
+ uint32 GetRequiredSkillValue() const { return RequiredSkillValue; }
+ uint32 GetRepObjectiveFaction() const { return RepObjectiveFaction; }
+ int32 GetRepObjectiveValue() const { return RepObjectiveValue; }
+ uint32 GetRequiredMinRepFaction() const { return RequiredMinRepFaction; }
+ int32 GetRequiredMinRepValue() const { return RequiredMinRepValue; }
+ uint32 GetRequiredMaxRepFaction() const { return RequiredMaxRepFaction; }
+ int32 GetRequiredMaxRepValue() const { return RequiredMaxRepValue; }
+ uint32 GetSuggestedPlayers() const { return SuggestedPlayers; }
+ uint32 GetLimitTime() const { return LimitTime; }
+ int32 GetPrevQuestId() const { return PrevQuestId; }
+ int32 GetNextQuestId() const { return NextQuestId; }
+ int32 GetExclusiveGroup() const { return ExclusiveGroup; }
+ uint32 GetNextQuestInChain() const { return NextQuestInChain; }
+ uint32 GetCharTitleId() const { return CharTitleId; }
+ uint32 GetSrcItemId() const { return SrcItemId; }
+ uint32 GetSrcItemCount() const { return SrcItemCount; }
+ uint32 GetSrcSpell() const { return SrcSpell; }
+ std::string GetTitle() const { return Title; }
+ std::string GetDetails() const { return Details; }
+ std::string GetObjectives() const { return Objectives; }
+ std::string GetOfferRewardText() const { return OfferRewardText; }
+ std::string GetRequestItemsText() const { return RequestItemsText; }
+ std::string GetEndText() const { return EndText; }
+ int32 GetRewOrReqMoney() const;
+ uint32 GetRewMoneyMaxLevel() const { return RewMoneyMaxLevel; }
+ // use in XP calculation at client
+ uint32 GetRewSpell() const { return RewSpell; }
+ uint32 GetRewSpellCast() const { return RewSpellCast; }
+ uint32 GetRewMailTemplateId() const { return RewMailTemplateId; }
+ uint32 GetRewMailDelaySecs() const { return RewMailDelaySecs; }
+ uint32 GetPointMapId() const { return PointMapId; }
+ float GetPointX() const { return PointX; }
+ float GetPointY() const { return PointY; }
+ uint32 GetPointOpt() const { return PointOpt; }
+ uint32 GetIncompleteEmote() const { return IncompleteEmote; }
+ uint32 GetCompleteEmote() const { return CompleteEmote; }
+ uint32 GetQuestStartScript() const { return QuestStartScript; }
+ uint32 GetQuestCompleteScript() const { return QuestCompleteScript; }
+ bool IsRepeatable() const { return QuestFlags & QUEST_MANGOS_FLAGS_REPEATABLE; }
+ bool IsAutoComplete() const { return QuestMethod ? false : true; }
+ uint32 GetFlags() const { return QuestFlags; }
+ bool IsDaily() const { return QuestFlags & QUEST_FLAGS_DAILY; }
+
+ // multiple values
+ std::string ObjectiveText[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqItemId[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqItemCount[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqSourceId[QUEST_SOURCE_ITEM_IDS_COUNT];
+ uint32 ReqSourceCount[QUEST_SOURCE_ITEM_IDS_COUNT];
+ uint32 ReqSourceRef[QUEST_SOURCE_ITEM_IDS_COUNT];
+ int32 ReqCreatureOrGOId[QUEST_OBJECTIVES_COUNT]; // >0 Creature <0 Gameobject
+ uint32 ReqCreatureOrGOCount[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqSpell[QUEST_OBJECTIVES_COUNT];
+ uint32 RewChoiceItemId[QUEST_REWARD_CHOICES_COUNT];
+ uint32 RewChoiceItemCount[QUEST_REWARD_CHOICES_COUNT];
+ uint32 RewItemId[QUEST_REWARDS_COUNT];
+ uint32 RewItemCount[QUEST_REWARDS_COUNT];
+ uint32 RewRepFaction[QUEST_REPUTATIONS_COUNT];
+ int32 RewRepValue[QUEST_REPUTATIONS_COUNT];
+ uint32 DetailsEmote[QUEST_EMOTE_COUNT];
+ uint32 OfferRewardEmote[QUEST_EMOTE_COUNT];
+
+ uint32 GetReqItemsCount() const { return m_reqitemscount; }
+ uint32 GetReqCreatureOrGOcount() const { return m_reqCreatureOrGOcount; }
+ uint32 GetRewChoiceItemsCount() const { return m_rewchoiceitemscount; }
+ uint32 GetRewItemsCount() const { return m_rewitemscount; }
+
+ typedef std::vector<int32> PrevQuests;
+ PrevQuests prevQuests;
+ typedef std::vector<uint32> PrevChainQuests;
+ PrevChainQuests prevChainQuests;
+
+ // cached data
+ private:
+ uint32 m_reqitemscount;
+ uint32 m_reqCreatureOrGOcount;
+ uint32 m_rewchoiceitemscount;
+ uint32 m_rewitemscount;
+
+ // table data
+ protected:
+ uint32 QuestId;
+ uint32 QuestMethod;
+ int32 ZoneOrSort;
+ int32 SkillOrClass;
+ uint32 MinLevel;
+ uint32 QuestLevel;
+ uint32 Type;
+ uint32 RequiredRaces;
+ uint32 RequiredSkillValue;
+ uint32 RepObjectiveFaction;
+ int32 RepObjectiveValue;
+ uint32 RequiredMinRepFaction;
+ int32 RequiredMinRepValue;
+ uint32 RequiredMaxRepFaction;
+ int32 RequiredMaxRepValue;
+ uint32 SuggestedPlayers;
+ uint32 LimitTime;
+ uint32 QuestFlags;
+ uint32 CharTitleId;
+ int32 PrevQuestId;
+ int32 NextQuestId;
+ int32 ExclusiveGroup;
+ uint32 NextQuestInChain;
+ uint32 SrcItemId;
+ uint32 SrcItemCount;
+ uint32 SrcSpell;
+ std::string Title;
+ std::string Details;
+ std::string Objectives;
+ std::string OfferRewardText;
+ std::string RequestItemsText;
+ std::string EndText;
+ int32 RewOrReqMoney;
+ uint32 RewMoneyMaxLevel;
+ uint32 RewSpell;
+ uint32 RewSpellCast;
+ uint32 RewMailTemplateId;
+ uint32 RewMailDelaySecs;
+ uint32 PointMapId;
+ float PointX;
+ float PointY;
+ uint32 PointOpt;
+ uint32 IncompleteEmote;
+ uint32 CompleteEmote;
+ uint32 QuestStartScript;
+ uint32 QuestCompleteScript;
+};
+
+enum QuestUpdateState
+{
+ QUEST_UNCHANGED = 0,
+ QUEST_CHANGED = 1,
+ QUEST_NEW = 2
+};
+
+struct QuestStatusData
+{
+ QuestStatusData()
+ : m_status(QUEST_STATUS_NONE),m_rewarded(false),
+ m_explored(false), m_timer(0), uState(QUEST_NEW)
+ {
+ memset(m_itemcount, 0, QUEST_OBJECTIVES_COUNT * sizeof(uint32));
+ memset(m_creatureOrGOcount, 0, QUEST_OBJECTIVES_COUNT * sizeof(uint32));
+ }
+
+ QuestStatus m_status;
+ bool m_rewarded;
+ bool m_explored;
+ uint32 m_timer;
+ QuestUpdateState uState;
+
+ uint32 m_itemcount[ QUEST_OBJECTIVES_COUNT ];
+ uint32 m_creatureOrGOcount[ QUEST_OBJECTIVES_COUNT ];
+};
+#endif
diff --git a/src/game/RandomMovementGenerator.cpp b/src/game/RandomMovementGenerator.cpp
index e27368c73f3..a926dd9aa4f 100644
--- a/src/game/RandomMovementGenerator.cpp
+++ b/src/game/RandomMovementGenerator.cpp
@@ -1,163 +1,163 @@
-/*
- * 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 "RandomMovementGenerator.h"
-#include "DestinationHolderImp.h"
-#include "Map.h"
-#include "Util.h"
-
-#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV"
-
-template<>
-void
-RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature)
-{
- float X,Y,Z,z,nx,ny,nz,wander_distance,ori,dist;
-
- creature.GetRespawnCoord(X, Y, Z, &ori, &wander_distance);
-
- z = creature.GetPositionZ();
- uint32 mapid=creature.GetMapId();
- Map const* map = MapManager::Instance().GetBaseMap(mapid);
-
- // For 2D/3D system selection
- bool is_land_ok = creature.canWalk();
- bool is_water_ok = creature.canSwim();
- bool is_air_ok = creature.canFly();
-
- const float angle = rand_norm()*(M_PI*2);
- const float range = rand_norm()*wander_distance;
- const float distanceX = range * cos(angle);
- const float distanceY = range * sin(angle);
-
- nx = X + distanceX;
- ny = Y + distanceY;
-
- // prevent invalid coordinates generation
- MaNGOS::NormalizeMapCoord(nx);
- MaNGOS::NormalizeMapCoord(ny);
-
- dist = (nx - X)*(nx - X) + (ny - Y)*(ny - Y);
-
- if (is_air_ok) // 3D system above ground and above water (flying mode)
- {
- const float distanceZ = rand_norm() * sqrtf(dist)/2; // Limit height change
- nz = Z + distanceZ;
- float tz = map->GetHeight(nx, ny, nz-2.0f, false); // Map check only, vmap needed here but need to alter vmaps checks for height.
- float wz = map->GetWaterLevel(nx, ny);
- if (tz >= nz || wz >= nz)
- return; // Problem here, we must fly above the ground and water, not under. Let's try on next tick
- }
- //else if (is_water_ok) // 3D system under water and above ground (swimming mode)
- else // 2D only
- {
- dist = dist>=100.0f ? 10.0f : sqrtf(dist); // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE)
- // The fastest way to get an accurate result 90% of the time.
- // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long.
- nz = map->GetHeight(nx,ny,Z+dist-2.0f,false); // Map check
- if (fabs(nz-Z)>dist)
- {
- nz = map->GetHeight(nx,ny,Z-2.0f,true); // Vmap Horizontal or above
- if (fabs(nz-Z)>dist)
- {
- nz = map->GetHeight(nx,ny,Z+dist-2.0f,true); // Vmap Higher
- if (fabs(nz-Z)>dist)
- return; // let's forget this bad coords where a z cannot be find and retry at next tick
- }
- }
- }
-
- Traveller<Creature> traveller(creature);
- creature.SetOrientation(creature.GetAngle(nx,ny));
- i_destinationHolder.SetDestination(traveller, nx, ny, nz);
- creature.addUnitState(UNIT_STAT_ROAMING);
- if (is_air_ok)
- {
- i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
- creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
- }
- //else if (is_water_ok) // Swimming mode to be done with more than this check
- else
- {
- i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(),5000+i_destinationHolder.GetTotalTravelTime()));
- creature.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE);
- }
-}
-
-template<>
-void
-RandomMovementGenerator<Creature>::Initialize(Creature &creature)
-{
- if(!creature.isAlive())
- return;
-
- if (creature.canFly())
- creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
- else
- creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE );
- _setRandomLocation(creature);
-}
-
-template<>
-void
-RandomMovementGenerator<Creature>::Reset(Creature &creature)
-{
- Initialize(creature);
-}
-
-template<>
-bool
-RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
-{
- 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);
- return true;
- }
-
- i_nextMoveTime.Update(diff);
-
- if(i_destinationHolder.HasArrived() && !creature.IsStopped() && !creature.canFly())
- creature.clearUnitState(UNIT_STAT_ROAMING);
-
- if(!i_destinationHolder.HasArrived() && creature.IsStopped())
- creature.addUnitState(UNIT_STAT_ROAMING);
-
- CreatureTraveller traveller(creature);
-
- if( i_destinationHolder.UpdateTraveller(traveller, diff, false, true) )
- {
- if(i_nextMoveTime.Passed())
- {
- if (creature.canFly())
- creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
- else
- creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE);
- _setRandomLocation(creature);
- }
- else if(creature.isPet() && creature.GetOwner() && creature.GetDistance(creature.GetOwner()) > PET_FOLLOW_DIST+2.5f)
- {
- creature.SetUnitMovementFlags(MOVEMENTFLAG_NONE);
- _setRandomLocation(creature);
- }
- }
- 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 "Creature.h"
+#include "MapManager.h"
+#include "RandomMovementGenerator.h"
+#include "DestinationHolderImp.h"
+#include "Map.h"
+#include "Util.h"
+
+#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV"
+
+template<>
+void
+RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature)
+{
+ float X,Y,Z,z,nx,ny,nz,wander_distance,ori,dist;
+
+ creature.GetRespawnCoord(X, Y, Z, &ori, &wander_distance);
+
+ z = creature.GetPositionZ();
+ uint32 mapid=creature.GetMapId();
+ Map const* map = MapManager::Instance().GetBaseMap(mapid);
+
+ // For 2D/3D system selection
+ bool is_land_ok = creature.canWalk();
+ bool is_water_ok = creature.canSwim();
+ bool is_air_ok = creature.canFly();
+
+ const float angle = rand_norm()*(M_PI*2);
+ const float range = rand_norm()*wander_distance;
+ const float distanceX = range * cos(angle);
+ const float distanceY = range * sin(angle);
+
+ nx = X + distanceX;
+ ny = Y + distanceY;
+
+ // prevent invalid coordinates generation
+ MaNGOS::NormalizeMapCoord(nx);
+ MaNGOS::NormalizeMapCoord(ny);
+
+ dist = (nx - X)*(nx - X) + (ny - Y)*(ny - Y);
+
+ if (is_air_ok) // 3D system above ground and above water (flying mode)
+ {
+ const float distanceZ = rand_norm() * sqrtf(dist)/2; // Limit height change
+ nz = Z + distanceZ;
+ float tz = map->GetHeight(nx, ny, nz-2.0f, false); // Map check only, vmap needed here but need to alter vmaps checks for height.
+ float wz = map->GetWaterLevel(nx, ny);
+ if (tz >= nz || wz >= nz)
+ return; // Problem here, we must fly above the ground and water, not under. Let's try on next tick
+ }
+ //else if (is_water_ok) // 3D system under water and above ground (swimming mode)
+ else // 2D only
+ {
+ dist = dist>=100.0f ? 10.0f : sqrtf(dist); // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE)
+ // The fastest way to get an accurate result 90% of the time.
+ // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long.
+ nz = map->GetHeight(nx,ny,Z+dist-2.0f,false); // Map check
+ if (fabs(nz-Z)>dist)
+ {
+ nz = map->GetHeight(nx,ny,Z-2.0f,true); // Vmap Horizontal or above
+ if (fabs(nz-Z)>dist)
+ {
+ nz = map->GetHeight(nx,ny,Z+dist-2.0f,true); // Vmap Higher
+ if (fabs(nz-Z)>dist)
+ return; // let's forget this bad coords where a z cannot be find and retry at next tick
+ }
+ }
+ }
+
+ Traveller<Creature> traveller(creature);
+ creature.SetOrientation(creature.GetAngle(nx,ny));
+ i_destinationHolder.SetDestination(traveller, nx, ny, nz);
+ creature.addUnitState(UNIT_STAT_ROAMING);
+ if (is_air_ok)
+ {
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+ }
+ //else if (is_water_ok) // Swimming mode to be done with more than this check
+ else
+ {
+ i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(),5000+i_destinationHolder.GetTotalTravelTime()));
+ creature.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE);
+ }
+}
+
+template<>
+void
+RandomMovementGenerator<Creature>::Initialize(Creature &creature)
+{
+ if(!creature.isAlive())
+ return;
+
+ if (creature.canFly())
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+ else
+ creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE );
+ _setRandomLocation(creature);
+}
+
+template<>
+void
+RandomMovementGenerator<Creature>::Reset(Creature &creature)
+{
+ Initialize(creature);
+}
+
+template<>
+bool
+RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
+{
+ 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);
+ return true;
+ }
+
+ i_nextMoveTime.Update(diff);
+
+ if(i_destinationHolder.HasArrived() && !creature.IsStopped() && !creature.canFly())
+ creature.clearUnitState(UNIT_STAT_ROAMING);
+
+ if(!i_destinationHolder.HasArrived() && creature.IsStopped())
+ creature.addUnitState(UNIT_STAT_ROAMING);
+
+ CreatureTraveller traveller(creature);
+
+ if( i_destinationHolder.UpdateTraveller(traveller, diff, false, true) )
+ {
+ if(i_nextMoveTime.Passed())
+ {
+ if (creature.canFly())
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+ else
+ creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE);
+ _setRandomLocation(creature);
+ }
+ else if(creature.isPet() && creature.GetOwner() && creature.GetDistance(creature.GetOwner()) > PET_FOLLOW_DIST+2.5f)
+ {
+ creature.SetUnitMovementFlags(MOVEMENTFLAG_NONE);
+ _setRandomLocation(creature);
+ }
+ }
+ return true;
+}
diff --git a/src/game/SocialMgr.cpp b/src/game/SocialMgr.cpp
index 858e6af6239..1f01c6ee1f6 100644
--- a/src/game/SocialMgr.cpp
+++ b/src/game/SocialMgr.cpp
@@ -1,311 +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)
-{
- // 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;
-}
+/*
+ * 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 ba5b5d97b47..70459c0e670 100644
--- a/src/game/Spell.cpp
+++ b/src/game/Spell.cpp
@@ -1,5089 +1,5089 @@
-/*
- * 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 "GridNotifiers.h"
-#include "GridNotifiersImpl.h"
-#include "Opcodes.h"
-#include "Log.h"
-#include "UpdateMask.h"
-#include "World.h"
-#include "ObjectMgr.h"
-#include "SpellMgr.h"
-#include "Player.h"
-#include "Pet.h"
-#include "Unit.h"
-#include "Spell.h"
-#include "DynamicObject.h"
-#include "SpellAuras.h"
-#include "Group.h"
-#include "UpdateData.h"
-#include "MapManager.h"
-#include "ObjectAccessor.h"
-#include "CellImpl.h"
-#include "Policies/SingletonImp.h"
-#include "SharedDefines.h"
-#include "Tools.h"
-#include "LootMgr.h"
-#include "VMapFactory.h"
-#include "BattleGround.h"
-#include "Util.h"
-
-#define SPELL_CHANNEL_UPDATE_INTERVAL 1000
-
-extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS];
-
-bool IsQuestTameSpell(uint32 spellId)
-{
- SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
- if (!spellproto) return false;
-
- return spellproto->Effect[0] == SPELL_EFFECT_THREAT
- && spellproto->Effect[1] == SPELL_EFFECT_APPLY_AURA && spellproto->EffectApplyAuraName[1] == SPELL_AURA_DUMMY;
-}
-
-SpellCastTargets::SpellCastTargets()
-{
- m_unitTarget = NULL;
- m_itemTarget = NULL;
- m_GOTarget = NULL;
-
- m_unitTargetGUID = 0;
- m_GOTargetGUID = 0;
- m_CorpseTargetGUID = 0;
- m_itemTargetGUID = 0;
- m_itemTargetEntry = 0;
-
- m_srcX = m_srcY = m_srcZ = m_destX = m_destY = m_destZ = 0;
- m_strTarget = "";
- m_targetMask = 0;
-}
-
-SpellCastTargets::~SpellCastTargets()
-{
-}
-
-void SpellCastTargets::setUnitTarget(Unit *target)
-{
- if (!target)
- return;
-
- m_destX = target->GetPositionX();
- m_destY = target->GetPositionY();
- m_destZ = target->GetPositionZ();
- m_unitTarget = target;
- m_unitTargetGUID = target->GetGUID();
- m_targetMask |= TARGET_FLAG_UNIT;
-}
-
-void SpellCastTargets::setDestination(float x, float y, float z)
-{
- m_destX = x;
- m_destY = y;
- m_destZ = z;
- m_targetMask |= TARGET_FLAG_DEST_LOCATION;
-}
-
-void SpellCastTargets::setGOTarget(GameObject *target)
-{
- m_GOTarget = target;
- m_GOTargetGUID = target->GetGUID();
- // m_targetMask |= TARGET_FLAG_OBJECT;
-}
-
-void SpellCastTargets::setItemTarget(Item* item)
-{
- if(!item)
- return;
-
- m_itemTarget = item;
- m_itemTargetGUID = item->GetGUID();
- m_itemTargetEntry = item->GetEntry();
- m_targetMask |= TARGET_FLAG_ITEM;
-}
-
-void SpellCastTargets::setCorpseTarget(Corpse* corpse)
-{
- m_CorpseTargetGUID = corpse->GetGUID();
-}
-
-void SpellCastTargets::Update(Unit* caster)
-{
- m_GOTarget = m_GOTargetGUID ? ObjectAccessor::GetGameObject(*caster,m_GOTargetGUID) : NULL;
- m_unitTarget = m_unitTargetGUID ?
- ( m_unitTargetGUID==caster->GetGUID() ? caster : ObjectAccessor::GetUnit(*caster, m_unitTargetGUID) ) :
- NULL;
-
- m_itemTarget = NULL;
- if(caster->GetTypeId()==TYPEID_PLAYER)
- {
- if(m_targetMask & TARGET_FLAG_ITEM)
- m_itemTarget = ((Player*)caster)->GetItemByGuid(m_itemTargetGUID);
- else
- {
- Player* pTrader = ((Player*)caster)->GetTrader();
- if(pTrader && m_itemTargetGUID < TRADE_SLOT_COUNT)
- m_itemTarget = pTrader->GetItemByPos(pTrader->GetItemPosByTradeSlot(m_itemTargetGUID));
- }
- if(m_itemTarget)
- m_itemTargetEntry = m_itemTarget->GetEntry();
- }
-}
-
-bool SpellCastTargets::read ( WorldPacket * data, Unit *caster )
-{
- if(data->rpos()+4 > data->size())
- return false;
-
- *data >> m_targetMask;
-
- if(m_targetMask == TARGET_FLAG_SELF)
- {
- m_destX = caster->GetPositionX();
- m_destY = caster->GetPositionY();
- m_destZ = caster->GetPositionZ();
- m_unitTarget = caster;
- m_unitTargetGUID = caster->GetGUID();
- return true;
- }
- // TARGET_FLAG_UNK2 is used for non-combat pets, maybe other?
- if( m_targetMask & (TARGET_FLAG_UNIT|TARGET_FLAG_UNK2) )
- if(!readGUID(*data, m_unitTargetGUID))
- return false;
-
- if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK ))
- if(!readGUID(*data, m_GOTargetGUID))
- return false;
-
- if(( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM )) && caster->GetTypeId() == TYPEID_PLAYER)
- if(!readGUID(*data, m_itemTargetGUID))
- return false;
-
- if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
- {
- if(data->rpos()+4+4+4 > data->size())
- return false;
-
- *data >> m_srcX >> m_srcY >> m_srcZ;
- if(!MaNGOS::IsValidMapCoord(m_srcX, m_srcY, m_srcZ))
- return false;
- }
-
- if( m_targetMask & TARGET_FLAG_DEST_LOCATION )
- {
- if(data->rpos()+4+4+4 > data->size())
- return false;
-
- *data >> m_destX >> m_destY >> m_destZ;
- if(!MaNGOS::IsValidMapCoord(m_destX, m_destY, m_destZ))
- return false;
- }
-
- if( m_targetMask & TARGET_FLAG_STRING )
- {
- if(data->rpos()+1 > data->size())
- return false;
-
- *data >> m_strTarget;
- }
-
- if( m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) )
- if(!readGUID(*data, m_CorpseTargetGUID))
- return false;
-
- // find real units/GOs
- Update(caster);
- return true;
-}
-
-void SpellCastTargets::write ( WorldPacket * data )
-{
- *data << uint32(m_targetMask);
-
- if( m_targetMask & ( TARGET_FLAG_UNIT | TARGET_FLAG_PVP_CORPSE | TARGET_FLAG_OBJECT | TARGET_FLAG_CORPSE | TARGET_FLAG_UNK2 ) )
- {
- if(m_targetMask & TARGET_FLAG_UNIT)
- {
- if(m_unitTarget)
- data->append(m_unitTarget->GetPackGUID());
- else
- *data << uint8(0);
- }
- else if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK ) )
- {
- if(m_GOTarget)
- data->append(m_GOTarget->GetPackGUID());
- else
- *data << uint8(0);
- }
- else if( m_targetMask & ( TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) )
- data->appendPackGUID(m_CorpseTargetGUID);
- else
- *data << uint8(0);
- }
-
- if( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM ) )
- {
- if(m_itemTarget)
- data->append(m_itemTarget->GetPackGUID());
- else
- *data << uint8(0);
- }
-
- if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
- *data << m_srcX << m_srcY << m_srcZ;
-
- if( m_targetMask & TARGET_FLAG_DEST_LOCATION )
- *data << m_destX << m_destY << m_destZ;
-
- if( m_targetMask & TARGET_FLAG_STRING )
- *data << m_strTarget;
-}
-
-Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 originalCasterGUID, Spell** triggeringContainer )
-{
- ASSERT( Caster != NULL && info != NULL );
- ASSERT( info == sSpellStore.LookupEntry( info->Id ) && "`info` must be pointer to sSpellStore element");
-
- m_spellInfo = info;
- m_caster = Caster;
- m_selfContainer = NULL;
- m_triggeringContainer = triggeringContainer;
- m_magnetPair.first = false;
- m_magnetPair.second = NULL;
- m_deletable = true;
- m_delayAtDamageCount = 0;
-
- m_applyMultiplierMask = 0;
-
- // Get data for type of attack
- switch (m_spellInfo->DmgClass)
- {
- case SPELL_DAMAGE_CLASS_MELEE:
- if (m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND)
- m_attackType = OFF_ATTACK;
- else
- m_attackType = BASE_ATTACK;
- break;
- case SPELL_DAMAGE_CLASS_RANGED:
- m_attackType = RANGED_ATTACK;
- break;
- default:
- // Wands
- if (m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_WAND)
- m_attackType = RANGED_ATTACK;
- else
- m_attackType = BASE_ATTACK;
- break;
- }
-
- m_spellSchoolMask = GetSpellSchoolMask(info); // Can be override for some spell (wand shoot for example)
-
- if(m_attackType == RANGED_ATTACK)
- {
- // wand case
- if((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0 && m_caster->GetTypeId()==TYPEID_PLAYER)
- {
- if(Item* pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK))
- m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->Damage->DamageType);
- }
- }
-
- if(originalCasterGUID)
- m_originalCasterGUID = originalCasterGUID;
- else
- m_originalCasterGUID = m_caster->GetGUID();
-
- if(m_originalCasterGUID==m_caster->GetGUID())
- m_originalCaster = m_caster;
- else
- {
- m_originalCaster = ObjectAccessor::GetUnit(*m_caster,m_originalCasterGUID);
- if(m_originalCaster && !m_originalCaster->IsInWorld()) m_originalCaster = NULL;
- }
-
- for(int i=0; i <3; ++i)
- m_currentBasePoints[i] = m_spellInfo->EffectBasePoints[i];
-
- m_spellState = SPELL_STATE_NULL;
-
- m_castPositionX = m_castPositionY = m_castPositionZ = 0;
- m_TriggerSpells.clear();
- m_IsTriggeredSpell = triggered;
- //m_AreaAura = false;
- m_CastItem = NULL;
-
- unitTarget = NULL;
- itemTarget = NULL;
- gameObjTarget = NULL;
- focusObject = NULL;
- m_cast_count = 0;
- m_triggeredByAuraSpell = NULL;
-
- //Auto Shot & Shoot
- if( m_spellInfo->AttributesEx2 == 0x000020 && !triggered )
- m_autoRepeat = true;
- else
- m_autoRepeat = false;
-
- m_powerCost = 0; // setup to correct value in Spell::prepare, don't must be used before.
- m_casttime = 0; // setup to correct value in Spell::prepare, don't must be used before.
- m_timer = 0; // will set to castime in preper
-
- m_needAliveTargetMask = 0;
-
- // determine reflection
- m_canReflect = false;
-
- if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && (m_spellInfo->AttributesEx2 & 0x4)==0)
- {
- for(int j=0;j<3;j++)
- {
- if (m_spellInfo->Effect[j]==0)
- continue;
-
- if(!IsPositiveTarget(m_spellInfo->EffectImplicitTargetA[j],m_spellInfo->EffectImplicitTargetB[j]))
- m_canReflect = true;
- else
- m_canReflect = (m_spellInfo->AttributesEx & (1<<7)) ? true : false;
-
- if(m_canReflect)
- continue;
- else
- break;
- }
- }
-
- CleanupTargetList();
-}
-
-Spell::~Spell()
-{
-}
-
-void Spell::FillTargetMap()
-{
- // TODO: ADD the correct target FILLS!!!!!!
-
- for(uint32 i=0;i<3;i++)
- {
- // not call for empty effect.
- // Also some spells use not used effect targets for store targets for dummy effect in triggered spells
- if(m_spellInfo->Effect[i]==0)
- continue;
-
- // targets for TARGET_SCRIPT_COORDINATES (A) and TARGET_SCRIPT filled in Spell::canCast call
- if( m_spellInfo->EffectImplicitTargetA[i] == TARGET_SCRIPT_COORDINATES ||
- m_spellInfo->EffectImplicitTargetA[i] == TARGET_SCRIPT ||
- m_spellInfo->EffectImplicitTargetB[i] == TARGET_SCRIPT && m_spellInfo->EffectImplicitTargetA[i] != TARGET_SELF )
- continue;
-
- // TODO: find a way so this is not needed?
- // for area auras always add caster as target (needed for totems for example)
- if(IsAreaAuraEffect(m_spellInfo->Effect[i]))
- AddUnitTarget(m_caster, i);
-
- std::list<Unit*> tmpUnitMap;
-
- // TargetA/TargetB dependent from each other, we not switch to full support this dependences
- // but need it support in some know cases
- switch(m_spellInfo->EffectImplicitTargetA[i])
- {
- case TARGET_ALL_AROUND_CASTER:
- if( m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_PARTY ||
- m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER ||
- m_spellInfo->EffectImplicitTargetB[i]==TARGET_RANDOM_RAID_MEMBER )
- {
- SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap);
- }
- // Note: this hack with search required until GO casting not implemented
- // environment damage spells already have around enemies targeting but this not help in case not existed GO casting support
- // currently each enemy selected explicitly and self cast damage
- else if(m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_ENEMY_IN_AREA && m_spellInfo->Effect[i]==SPELL_EFFECT_ENVIRONMENTAL_DAMAGE)
- {
- if(m_targets.getUnitTarget())
- tmpUnitMap.push_back(m_targets.getUnitTarget());
- }
- else
- {
- SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
- SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap);
- }
- break;
- case TARGET_TABLE_X_Y_Z_COORDINATES:
- // Only if target A, for target B (used in teleports) dest select in effect
- SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
- break;
- default:
- switch(m_spellInfo->EffectImplicitTargetB[i])
- {
- case TARGET_SCRIPT_COORDINATES: // B case filled in canCast but we need fill unit list base at A case
- SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
- break;
- default:
- SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
- SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap);
- break;
- }
- break;
- }
-
- if( (m_spellInfo->EffectImplicitTargetA[i]==0 || m_spellInfo->EffectImplicitTargetA[i]==TARGET_EFFECT_SELECT) &&
- (m_spellInfo->EffectImplicitTargetB[i]==0 || m_spellInfo->EffectImplicitTargetB[i]==TARGET_EFFECT_SELECT) )
- {
- // add here custom effects that need default target.
- // FOR EVERY TARGET TYPE THERE IS A DIFFERENT FILL!!
- switch(m_spellInfo->Effect[i])
- {
- case SPELL_EFFECT_DUMMY:
- {
- switch(m_spellInfo->Id)
- {
- case 20577: // Cannibalize
- {
- // non-standard target selection
- SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
- float max_range = GetSpellMaxRange(srange);
-
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- WorldObject* result = NULL;
-
- MaNGOS::CannibalizeObjectCheck u_check(m_caster, max_range);
- MaNGOS::WorldObjectSearcher<MaNGOS::CannibalizeObjectCheck > searcher(result, u_check);
-
- TypeContainerVisitor<MaNGOS::WorldObjectSearcher<MaNGOS::CannibalizeObjectCheck >, GridTypeMapContainer > grid_searcher(searcher);
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, grid_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
-
- if(!result)
- {
- TypeContainerVisitor<MaNGOS::WorldObjectSearcher<MaNGOS::CannibalizeObjectCheck >, WorldTypeMapContainer > world_searcher(searcher);
- cell_lock->Visit(cell_lock, world_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- }
-
- if(result)
- {
- switch(result->GetTypeId())
- {
- case TYPEID_UNIT:
- case TYPEID_PLAYER:
- tmpUnitMap.push_back((Unit*)result);
- break;
- case TYPEID_CORPSE:
- m_targets.setCorpseTarget((Corpse*)result);
- if(Player* owner = ObjectAccessor::FindPlayer(((Corpse*)result)->GetOwnerGUID()))
- tmpUnitMap.push_back(owner);
- break;
- }
- }
- else
- {
- // clear cooldown at fail
- if(m_caster->GetTypeId()==TYPEID_PLAYER)
- {
- ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id);
-
- WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
- data << uint32(m_spellInfo->Id);
- data << uint64(m_caster->GetGUID());
- ((Player*)m_caster)->GetSession()->SendPacket(&data);
- }
-
- SendCastResult(SPELL_FAILED_NO_EDIBLE_CORPSES);
- finish(false);
- }
- break;
- }
- default:
- if(m_targets.getUnitTarget())
- tmpUnitMap.push_back(m_targets.getUnitTarget());
- break;
- }
- break;
- }
- case SPELL_EFFECT_RESURRECT:
- case SPELL_EFFECT_PARRY:
- case SPELL_EFFECT_BLOCK:
- case SPELL_EFFECT_CREATE_ITEM:
- case SPELL_EFFECT_TRIGGER_SPELL:
- case SPELL_EFFECT_TRIGGER_MISSILE:
- case SPELL_EFFECT_LEARN_SPELL:
- case SPELL_EFFECT_SKILL_STEP:
- case SPELL_EFFECT_PROFICIENCY:
- case SPELL_EFFECT_SUMMON_POSSESSED:
- case SPELL_EFFECT_SUMMON_OBJECT_WILD:
- case SPELL_EFFECT_SELF_RESURRECT:
- case SPELL_EFFECT_REPUTATION:
- if(m_targets.getUnitTarget())
- tmpUnitMap.push_back(m_targets.getUnitTarget());
- break;
- case SPELL_EFFECT_SUMMON_PLAYER:
- if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->GetSelection())
- {
- Player* target = objmgr.GetPlayer(((Player*)m_caster)->GetSelection());
- if(target)
- tmpUnitMap.push_back(target);
- }
- break;
- case SPELL_EFFECT_RESURRECT_NEW:
- if(m_targets.getUnitTarget())
- tmpUnitMap.push_back(m_targets.getUnitTarget());
- if(m_targets.getCorpseTargetGUID())
- {
- Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID());
- if(corpse)
- {
- Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGUID());
- if(owner)
- tmpUnitMap.push_back(owner);
- }
- }
- break;
- case SPELL_EFFECT_SUMMON:
- if(m_spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED || m_spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED2)
- {
- if(m_targets.getUnitTarget())
- tmpUnitMap.push_back(m_targets.getUnitTarget());
- }
- else
- tmpUnitMap.push_back(m_caster);
- break;
- case SPELL_EFFECT_SUMMON_CHANGE_ITEM:
- case SPELL_EFFECT_SUMMON_WILD:
- case SPELL_EFFECT_SUMMON_GUARDIAN:
- case SPELL_EFFECT_TRANS_DOOR:
- case SPELL_EFFECT_ADD_FARSIGHT:
- case SPELL_EFFECT_STUCK:
- case SPELL_EFFECT_DESTROY_ALL_TOTEMS:
- case SPELL_EFFECT_SUMMON_DEMON:
- case SPELL_EFFECT_SKILL:
- tmpUnitMap.push_back(m_caster);
- break;
- case SPELL_EFFECT_LEARN_PET_SPELL:
- if(Pet* pet = m_caster->GetPet())
- tmpUnitMap.push_back(pet);
- break;
- case SPELL_EFFECT_ENCHANT_ITEM:
- case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY:
- case SPELL_EFFECT_DISENCHANT:
- case SPELL_EFFECT_FEED_PET:
- case SPELL_EFFECT_PROSPECTING:
- if(m_targets.getItemTarget())
- AddItemTarget(m_targets.getItemTarget(), i);
- break;
- case SPELL_EFFECT_APPLY_AURA:
- switch(m_spellInfo->EffectApplyAuraName[i])
- {
- case SPELL_AURA_ADD_FLAT_MODIFIER: // some spell mods auras have 0 target modes instead expected TARGET_SELF(1) (and present for other ranks for same spell for example)
- case SPELL_AURA_ADD_PCT_MODIFIER:
- tmpUnitMap.push_back(m_caster);
- break;
- default: // apply to target in other case
- if(m_targets.getUnitTarget())
- tmpUnitMap.push_back(m_targets.getUnitTarget());
- break;
- }
- break;
- case SPELL_EFFECT_APPLY_AREA_AURA_PARTY:
- // AreaAura
- if(m_spellInfo->Attributes == 0x9050000 || m_spellInfo->Attributes == 0x10000)
- SetTargetMap(i,TARGET_AREAEFFECT_PARTY,tmpUnitMap);
- break;
- case SPELL_EFFECT_SKIN_PLAYER_CORPSE:
- if(m_targets.getUnitTarget())
- {
- tmpUnitMap.push_back(m_targets.getUnitTarget());
- }
- else if (m_targets.getCorpseTargetGUID())
- {
- Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID());
- if(corpse)
- {
- Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGUID());
- if(owner)
- tmpUnitMap.push_back(owner);
- }
- }
- break;
- default:
- break;
- }
- }
- if(IsChanneledSpell(m_spellInfo) && !tmpUnitMap.empty())
- m_needAliveTargetMask |= (1<<i);
-
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- Player *me = (Player*)m_caster;
- for (std::list<Unit*>::const_iterator itr = tmpUnitMap.begin(); itr != tmpUnitMap.end(); itr++)
- {
- Unit *owner = (*itr)->GetOwner();
- Unit *u = owner ? owner : (*itr);
- if(u!=m_caster && u->IsPvP() && (!me->duel || me->duel->opponent != u))
- {
- me->UpdatePvP(true);
- me->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
- break;
- }
- }
- }
-
- for (std::list<Unit*>::iterator itr = tmpUnitMap.begin() ; itr != tmpUnitMap.end();)
- {
- if(!CheckTarget(*itr, i, false ))
- {
- itr = tmpUnitMap.erase(itr);
- continue;
- }
- else
- ++itr;
- }
-
- for(std::list<Unit*>::iterator iunit= tmpUnitMap.begin();iunit != tmpUnitMap.end();++iunit)
- AddUnitTarget((*iunit), i);
- }
-}
-
-void Spell::CleanupTargetList()
-{
- m_UniqueTargetInfo.clear();
- m_UniqueGOTargetInfo.clear();
- m_UniqueItemInfo.clear();
- m_countOfHit = 0;
- m_countOfMiss = 0;
- m_delayMoment = 0;
-}
-
-void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
-{
- if( m_spellInfo->Effect[effIndex]==0 )
- return;
-
- uint64 targetGUID = pVictim->GetGUID();
-
- // Lookup target in already in list
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- {
- if (targetGUID == ihit->targetGUID) // Found in list
- {
- ihit->effectMask |= 1<<effIndex; // Add only effect mask
- return;
- }
- }
-
- // This is new target calculate data for him
-
- // Get spell hit result on target
- TargetInfo target;
- target.targetGUID = targetGUID; // Store target GUID
- target.effectMask = 1<<effIndex; // Store index of effect
- target.processed = false; // Effects not apply on target
-
- // Calculate hit result
- target.missCondition = m_caster->SpellHitResult(pVictim, m_spellInfo, m_canReflect);
- if (target.missCondition == SPELL_MISS_NONE)
- ++m_countOfHit;
- else
- ++m_countOfMiss;
-
- // Spell have speed - need calculate incoming time
- if (m_spellInfo->speed > 0.0f)
- {
- // calculate spell incoming interval
- float dist = m_caster->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ());
- if (dist < 5.0f) dist = 5.0f;
- target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f);
-
- // Calculate minimum incoming time
- if (m_delayMoment==0 || m_delayMoment>target.timeDelay)
- m_delayMoment = target.timeDelay;
- }
- else
- target.timeDelay = 0LL;
-
- // If target reflect spell back to caster
- if (target.missCondition==SPELL_MISS_REFLECT)
- {
- // Calculate reflected spell result on caster
- target.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, m_canReflect);
-
- if (target.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell
- target.reflectResult = SPELL_MISS_PARRY;
-
- // Increase time interval for reflected spells by 1.5
- target.timeDelay+=target.timeDelay>>1;
- }
- else
- target.reflectResult = SPELL_MISS_NONE;
-
- // Add target to list
- m_UniqueTargetInfo.push_back(target);
-}
-
-void Spell::AddUnitTarget(uint64 unitGUID, uint32 effIndex)
-{
- Unit* unit = m_caster->GetGUID()==unitGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, unitGUID);
- if (unit)
- AddUnitTarget(unit, effIndex);
-}
-
-void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex)
-{
- if( m_spellInfo->Effect[effIndex]==0 )
- return;
-
- uint64 targetGUID = pVictim->GetGUID();
-
- // Lookup target in already in list
- for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit)
- {
- if (targetGUID == ihit->targetGUID) // Found in list
- {
- ihit->effectMask |= 1<<effIndex; // Add only effect mask
- return;
- }
- }
-
- // This is new target calculate data for him
-
- GOTargetInfo target;
- target.targetGUID = targetGUID;
- target.effectMask = 1<<effIndex;
- target.processed = false; // Effects not apply on target
-
- // Spell have speed - need calculate incoming time
- if (m_spellInfo->speed > 0.0f)
- {
- // calculate spell incoming interval
- float dist = m_caster->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ());
- if (dist < 5.0f) dist = 5.0f;
- target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f);
- if (m_delayMoment==0 || m_delayMoment>target.timeDelay)
- m_delayMoment = target.timeDelay;
- }
- else
- target.timeDelay = 0LL;
-
- ++m_countOfHit;
-
- // Add target to list
- m_UniqueGOTargetInfo.push_back(target);
-}
-
-void Spell::AddGOTarget(uint64 goGUID, uint32 effIndex)
-{
- GameObject* go = ObjectAccessor::GetGameObject(*m_caster, goGUID);
- if (go)
- AddGOTarget(go, effIndex);
-}
-
-void Spell::AddItemTarget(Item* pitem, uint32 effIndex)
-{
- if( m_spellInfo->Effect[effIndex]==0 )
- return;
-
- // Lookup target in already in list
- for(std::list<ItemTargetInfo>::iterator ihit= m_UniqueItemInfo.begin();ihit != m_UniqueItemInfo.end();++ihit)
- {
- if (pitem == ihit->item) // Found in list
- {
- ihit->effectMask |= 1<<effIndex; // Add only effect mask
- return;
- }
- }
-
- // This is new target add data
-
- ItemTargetInfo target;
- target.item = pitem;
- target.effectMask = 1<<effIndex;
- m_UniqueItemInfo.push_back(target);
-}
-
-void Spell::doTriggers(SpellMissInfo missInfo, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 block, uint32 absorb, bool crit)
-{
- // Do triggers depends from hit result (triggers on hit do in effects)
- // Set aura states depends from hit result
- if (missInfo!=SPELL_MISS_NONE)
- {
- // Miss/dodge/parry/block only for melee based spells
- // Resist only for magic based spells
- switch (missInfo)
- {
- case SPELL_MISS_MISS:
- if(m_caster->GetTypeId()== TYPEID_PLAYER)
- ((Player*)m_caster)->UpdateWeaponSkill(BASE_ATTACK);
-
- m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_MISS, m_spellInfo, m_IsTriggeredSpell);
- break;
- case SPELL_MISS_RESIST:
- m_caster->ProcDamageAndSpell(unitTarget, PROC_FLAG_TARGET_RESISTS, PROC_FLAG_RESIST_SPELL, 0, damageSchoolMask, m_spellInfo, m_IsTriggeredSpell);
- break;
- case SPELL_MISS_DODGE:
- if(unitTarget->GetTypeId() == TYPEID_PLAYER)
- ((Player*)unitTarget)->UpdateDefense();
-
- // Overpower
- if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARRIOR)
- {
- ((Player*) m_caster)->AddComboPoints(unitTarget, 1);
- m_caster->StartReactiveTimer( REACTIVE_OVERPOWER );
- }
-
- // Riposte
- if (unitTarget->getClass() != CLASS_ROGUE)
- {
- unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
- unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
- }
-
- m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_DODGE, m_spellInfo, m_IsTriggeredSpell);
- break;
- case SPELL_MISS_PARRY:
- // Update victim defense ?
- if(unitTarget->GetTypeId() == TYPEID_PLAYER)
- ((Player*)unitTarget)->UpdateDefense();
- // Mongoose bite - set only Counterattack here
- if (unitTarget->getClass() == CLASS_HUNTER)
- {
- unitTarget->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true);
- unitTarget->StartReactiveTimer( REACTIVE_HUNTER_PARRY );
- }
- else
- {
- unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
- unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
- }
- m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_PARRY, m_spellInfo, m_IsTriggeredSpell);
- break;
- case SPELL_MISS_BLOCK:
- unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
- unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
-
- m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_BLOCK, m_spellInfo, m_IsTriggeredSpell);
- break;
- // Trigger from this events not supported
- case SPELL_MISS_EVADE:
- case SPELL_MISS_IMMUNE:
- case SPELL_MISS_IMMUNE2:
- case SPELL_MISS_DEFLECT:
- case SPELL_MISS_ABSORB:
- // Trigger from reflects need do after get reflect result
- case SPELL_MISS_REFLECT:
- break;
- default:
- break;
- }
- }
-}
-
-void Spell::DoAllEffectOnTarget(TargetInfo *target)
-{
- if (target->processed) // Check target
- return;
- target->processed = true; // Target checked in apply effects procedure
-
- // Get mask of effects for target
- uint32 mask = target->effectMask;
- if (mask == 0) // No effects
- return;
-
- Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID);
- if (!unit)
- return;
-
- SpellMissInfo missInfo = target->missCondition;
- // Need init unitTarget by default unit (can changed in code on reflect)
- // Or on missInfo!=SPELL_MISS_NONE unitTarget undefined (but need in trigger subsystem)
- unitTarget = unit;
-
- if (missInfo==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target
- DoSpellHitOnUnit(unit, mask);
- else if (missInfo == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit)
- {
- if (target->reflectResult == SPELL_MISS_NONE) // If reflected spell hit caster -> do all effect on him
- DoSpellHitOnUnit(m_caster, mask);
- }
-
- // Do triggers only on miss/resist/parry/dodge
- if (missInfo!=SPELL_MISS_NONE)
- doTriggers(missInfo);
-
- // Call scripted function for AI if this spell is casted upon a creature (except pets)
- if(IS_CREATURE_GUID(target->targetGUID))
- {
- // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished)
- // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... )
- if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() )
- ((Player*)m_caster)->CastedCreatureOrGO(unit->GetEntry(),unit->GetGUID(),m_spellInfo->Id);
-
- if(((Creature*)unit)->AI())
- ((Creature*)unit)->AI()->SpellHit(m_caster ,m_spellInfo);
- }
-}
-
-void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask)
-{
- if(!unit || !effectMask)
- return;
-
- // remove spell_magnet aura after first spell redirect and destroy target if its totem
- if(m_magnetPair.first && m_magnetPair.second && m_magnetPair.second == unit)
- {
- if(unit->GetTypeId() == TYPEID_UNIT && ((Creature*)unit)->isTotem())
- unit->DealDamage(unit,unit->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
- return;
- }
-
- // Recheck immune (only for delayed spells)
- if( m_spellInfo->speed && (
- unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo),true) ||
- unit->IsImmunedToSpell(m_spellInfo,true) ))
- {
- m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE);
- return;
- }
-
- if( m_caster != unit )
- {
- if( !m_caster->IsFriendlyTo(unit) )
- {
- // for delayed spells ignore not visible explicit target
- if(m_spellInfo->speed > 0.0f && unit==m_targets.getUnitTarget() && !unit->isVisibleForOrDetect(m_caster,false))
- {
- m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE);
- return;
- }
-
- // exclude Arcane Missiles Dummy Aura aura for now (attack on hit)
- // TODO: find way to not need this?
- if(!(m_spellInfo->SpellFamilyName == SPELLFAMILY_MAGE &&
- m_spellInfo->SpellFamilyFlags & 0x800LL))
- {
- unit->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
-
- if( !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) )
- {
- if(!unit->IsStandState() && !unit->hasUnitState(UNIT_STAT_STUNNED))
- unit->SetStandState(PLAYER_STATE_NONE);
-
- if(!unit->isInCombat() && unit->GetTypeId() != TYPEID_PLAYER && ((Creature*)unit)->AI())
- ((Creature*)unit)->AI()->AttackStart(m_caster);
-
- unit->SetInCombatWith(m_caster);
- m_caster->SetInCombatWith(unit);
-
- if(Player *attackedPlayer = unit->GetCharmerOrOwnerPlayerOrPlayerItself())
- {
- m_caster->SetContestedPvP(attackedPlayer);
- }
- unit->AddThreat(m_caster, 0.0f);
- }
- }
- }
- else
- {
- // for delayed spells ignore negative spells (after duel end) for friendly targets
- if(m_spellInfo->speed > 0.0f && !IsPositiveSpell(m_spellInfo->Id))
- {
- m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE);
- return;
- }
-
- // assisting case, healing and resurrection
- if(unit->hasUnitState(UNIT_STAT_ATTACK_PLAYER))
- m_caster->SetContestedPvP();
- if( unit->isInCombat() && !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) )
- {
- m_caster->SetInCombatState(unit->GetCombatTimer() > 0);
- unit->getHostilRefManager().threatAssist(m_caster, 0.0f);
- }
- }
- }
-
- // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add
- m_diminishGroup = GetDiminishingReturnsGroupForSpell(m_spellInfo,m_triggeredByAuraSpell);
- m_diminishLevel = unit->GetDiminishing(m_diminishGroup);
- // Increase Diminishing on unit, current informations for actually casts will use values above
- if((GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_PLAYER && unit->GetTypeId() == TYPEID_PLAYER) || GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_ALL)
- unit->IncrDiminishing(m_diminishGroup);
-
- for(uint32 effectNumber=0;effectNumber<3;effectNumber++)
- {
- if (effectMask & (1<<effectNumber))
- {
- HandleEffects(unit,NULL,NULL,effectNumber,m_damageMultipliers[effectNumber]);
- if ( m_applyMultiplierMask & (1 << effectNumber) )
- {
- // Get multiplier
- float multiplier = m_spellInfo->DmgMultiplier[effectNumber];
- // Apply multiplier mods
- if(Player* modOwner = m_originalCaster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier,this);
- m_damageMultipliers[effectNumber] *= multiplier;
- }
- }
- }
-}
-
-void Spell::DoAllEffectOnTarget(GOTargetInfo *target)
-{
- if (target->processed) // Check target
- return;
- target->processed = true; // Target checked in apply effects procedure
-
- uint32 effectMask = target->effectMask;
- if(!effectMask)
- return;
-
- GameObject* go = ObjectAccessor::GetGameObject(*m_caster, target->targetGUID);
- if(!go)
- return;
-
- for(uint32 effectNumber=0;effectNumber<3;effectNumber++)
- if (effectMask & (1<<effectNumber))
- HandleEffects(NULL,NULL,go,effectNumber);
-
- // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished)
- // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... )
- if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() )
- ((Player*)m_caster)->CastedCreatureOrGO(go->GetEntry(),go->GetGUID(),m_spellInfo->Id);
-}
-
-void Spell::DoAllEffectOnTarget(ItemTargetInfo *target)
-{
- uint32 effectMask = target->effectMask;
- if(!target->item || !effectMask)
- return;
-
- for(uint32 effectNumber=0;effectNumber<3;effectNumber++)
- if (effectMask & (1<<effectNumber))
- HandleEffects(NULL, target->item, NULL, effectNumber);
-}
-
-bool Spell::IsAliveUnitPresentInTargetList()
-{
- // Not need check return true
- if (m_needAliveTargetMask == 0)
- return true;
-
- uint8 needAliveTargetMask = m_needAliveTargetMask;
-
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- {
- if( ihit->missCondition == SPELL_MISS_NONE && (needAliveTargetMask & ihit->effectMask) )
- {
- Unit *unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
-
- if (unit && unit->isAlive())
- needAliveTargetMask &= ~ihit->effectMask; // remove from need alive mask effect that have alive target
- }
- }
-
- // is all effects from m_needAliveTargetMask have alive targets
- return needAliveTargetMask==0;
-}
-
-// Helper for Chain Healing
-// Spell target first
-// Raidmates then descending by injury suffered (MaxHealth - Health)
-// Other players/mobs then descending by injury suffered (MaxHealth - Health)
-struct ChainHealingOrder : public std::binary_function<const Unit*, const Unit*, bool>
-{
- const Unit* MainTarget;
- ChainHealingOrder(Unit const* Target) : MainTarget(Target) {};
- // functor for operator ">"
- bool operator()(Unit const* _Left, Unit const* _Right) const
- {
- return (ChainHealingHash(_Left) < ChainHealingHash(_Right));
- }
- int32 ChainHealingHash(Unit const* Target) const
- {
- if (Target == MainTarget)
- return 0;
- else if (Target->GetTypeId() == TYPEID_PLAYER && MainTarget->GetTypeId() == TYPEID_PLAYER &&
- ((Player const*)Target)->IsInSameRaidWith((Player const*)MainTarget))
- {
- if (Target->GetHealth() == Target->GetMaxHealth())
- return 40000;
- else
- return 20000 - Target->GetMaxHealth() + Target->GetHealth();
- }
- else
- return 40000 - Target->GetMaxHealth() + Target->GetHealth();
- }
-};
-
-class ChainHealingFullHealth: std::unary_function<const Unit*, bool>
-{
- public:
- const Unit* MainTarget;
- ChainHealingFullHealth(const Unit* Target) : MainTarget(Target) {};
-
- bool operator()(const Unit* Target)
- {
- return (Target != MainTarget && Target->GetHealth() == Target->GetMaxHealth());
- }
-};
-
-// Helper for targets nearest to the spell target
-// The spell target is always first unless there is a target at _completely_ the same position (unbelievable case)
-struct TargetDistanceOrder : public std::binary_function<const Unit, const Unit, bool>
-{
- const Unit* MainTarget;
- TargetDistanceOrder(const Unit* Target) : MainTarget(Target) {};
- // functor for operator ">"
- bool operator()(const Unit* _Left, const Unit* _Right) const
- {
- return (MainTarget->GetDistance(_Left) < MainTarget->GetDistance(_Right));
- }
-};
-
-void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
-{
- float radius;
- if (m_spellInfo->EffectRadiusIndex[i])
- radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
- else
- radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
-
- if(m_originalCaster)
- if(Player* modOwner = m_originalCaster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius,this);
-
- uint32 EffectChainTarget = m_spellInfo->EffectChainTarget[i];
- if(m_originalCaster)
- if(Player* modOwner = m_originalCaster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this);
-
- uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets;
- switch(cur)
- {
- case TARGET_TOTEM_EARTH:
- case TARGET_TOTEM_WATER:
- case TARGET_TOTEM_AIR:
- case TARGET_TOTEM_FIRE:
- case TARGET_SELF:
- case TARGET_SELF2:
- case TARGET_DYNAMIC_OBJECT:
- case TARGET_AREAEFFECT_CUSTOM:
- case TARGET_AREAEFFECT_CUSTOM_2:
- case TARGET_SUMMON:
- {
- TagUnitMap.push_back(m_caster);
- break;
- }
- case TARGET_RANDOM_ENEMY_CHAIN_IN_AREA:
- {
- m_targets.m_targetMask = 0;
- unMaxTargets = EffectChainTarget;
- float max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS;
-
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- std::list<Unit *> tempUnitMap;
-
- {
- MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(m_caster, m_caster, max_range);
- MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck> searcher(tempUnitMap, 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(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- }
-
- if(tempUnitMap.empty())
- break;
-
- tempUnitMap.sort(TargetDistanceOrder(m_caster));
-
- //Now to get us a random target that's in the initial range of the spell
- uint32 t = 0;
- std::list<Unit *>::iterator itr = tempUnitMap.begin();
- while(itr!= tempUnitMap.end() && (*itr)->GetDistance(m_caster) < radius)
- ++t, ++itr;
-
- if(!t)
- break;
-
- itr = tempUnitMap.begin();
- std::advance(itr, rand()%t);
- Unit *pUnitTarget = *itr;
- TagUnitMap.push_back(pUnitTarget);
-
- tempUnitMap.erase(itr);
-
- tempUnitMap.sort(TargetDistanceOrder(pUnitTarget));
-
- t = unMaxTargets - 1;
- Unit *prev = pUnitTarget;
- std::list<Unit*>::iterator next = tempUnitMap.begin();
-
- while(t && next != tempUnitMap.end() )
- {
- if(prev->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS)
- break;
-
- if(!prev->IsWithinLOSInMap(*next))
- {
- ++next;
- continue;
- }
-
- prev = *next;
- TagUnitMap.push_back(prev);
- tempUnitMap.erase(next);
- tempUnitMap.sort(TargetDistanceOrder(prev));
- next = tempUnitMap.begin();
-
- --t;
- }
- }break;
- case TARGET_PET:
- {
- Pet* tmpUnit = m_caster->GetPet();
- if (!tmpUnit) break;
- TagUnitMap.push_back(tmpUnit);
- break;
- }
- case TARGET_CHAIN_DAMAGE:
- {
- if (EffectChainTarget <= 1)
- {
- Unit* pUnitTarget = SelectMagnetTarget();
- if(pUnitTarget)
- TagUnitMap.push_back(pUnitTarget);
- }
- else
- {
- Unit* pUnitTarget = m_targets.getUnitTarget();
- if(!pUnitTarget)
- break;
-
- unMaxTargets = EffectChainTarget;
-
- float max_range;
- if(m_spellInfo->DmgClass==SPELL_DAMAGE_CLASS_MELEE)
- max_range = radius; //
- else
- //FIXME: This very like horrible hack and wrong for most spells
- max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS;
-
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- Unit* originalCaster = GetOriginalCaster();
- if(originalCaster)
- {
- std::list<Unit *> tempUnitMap;
-
- {
- MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(pUnitTarget, originalCaster, max_range);
- MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck> searcher(tempUnitMap, 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(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- }
-
- tempUnitMap.sort(TargetDistanceOrder(pUnitTarget));
-
- if(tempUnitMap.empty())
- break;
-
- if(*tempUnitMap.begin() == pUnitTarget)
- tempUnitMap.erase(tempUnitMap.begin());
-
- TagUnitMap.push_back(pUnitTarget);
- uint32 t = unMaxTargets - 1;
- Unit *prev = pUnitTarget;
- std::list<Unit*>::iterator next = tempUnitMap.begin();
-
- while(t && next != tempUnitMap.end() )
- {
- if(prev->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS)
- break;
-
- if(!prev->IsWithinLOSInMap(*next))
- {
- ++next;
- continue;
- }
-
- prev = *next;
- TagUnitMap.push_back(prev);
- tempUnitMap.erase(next);
- tempUnitMap.sort(TargetDistanceOrder(prev));
- next = tempUnitMap.begin();
-
- --t;
- }
- }
- }
- }break;
- case TARGET_ALL_ENEMY_IN_AREA:
- {
- }break;
- case TARGET_ALL_ENEMY_IN_AREA_INSTANT:
- {
- // targets the ground, not the units in the area
- if (m_spellInfo->Effect[i]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
- {
- CellPair p(MaNGOS::ComputeCellPair(m_targets.m_destX, m_targets.m_destY));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_DEST_CENTER,SPELL_TARGETS_AOE_DAMAGE);
-
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
-
- // exclude caster (this can be important if this not original caster)
- TagUnitMap.remove(m_caster);
- }
- }break;
- case TARGET_DUELVSPLAYER_COORDINATES:
- {
- if(Unit* currentTarget = m_targets.getUnitTarget())
- {
- m_targets.setDestination(currentTarget->GetPositionX(), currentTarget->GetPositionY(), currentTarget->GetPositionZ());
- TagUnitMap.push_back(currentTarget);
- }
- }break;
- case TARGET_ALL_PARTY_AROUND_CASTER:
- case TARGET_ALL_PARTY_AROUND_CASTER_2:
- case TARGET_ALL_PARTY:
- {
- Player *pTarget = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself();
- Group *pGroup = pTarget ? pTarget->GetGroup() : NULL;
-
- if(pGroup)
- {
- uint8 subgroup = pTarget->GetSubGroup();
-
- for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- Player* Target = itr->getSource();
-
- // IsHostileTo check duel and controlled by enemy
- if( Target && Target->GetSubGroup()==subgroup && !m_caster->IsHostileTo(Target) )
- {
- if( m_caster->IsWithinDistInMap(Target, radius) )
- TagUnitMap.push_back(Target);
-
- if(Pet* pet = Target->GetPet())
- if( m_caster->IsWithinDistInMap(pet, radius) )
- TagUnitMap.push_back(pet);
- }
- }
- }
- else
- {
- Unit* ownerOrSelf = pTarget ? pTarget : m_caster->GetCharmerOrOwnerOrSelf();
- if(ownerOrSelf==m_caster || m_caster->IsWithinDistInMap(ownerOrSelf, radius))
- TagUnitMap.push_back(ownerOrSelf);
- if(Pet* pet = ownerOrSelf->GetPet())
- if( m_caster->IsWithinDistInMap(pet, radius) )
- TagUnitMap.push_back(pet);
- }
- }break;
- case TARGET_RANDOM_RAID_MEMBER:
- {
- if (m_caster->GetTypeId() == TYPEID_PLAYER)
- if(Player* target = ((Player*)m_caster)->GetNextRandomRaidMember(radius))
- TagUnitMap.push_back(target);
- }break;
- case TARGET_SINGLE_FRIEND:
- case TARGET_SINGLE_FRIEND_2:
- {
- if(m_targets.getUnitTarget())
- TagUnitMap.push_back(m_targets.getUnitTarget());
- }break;
- case TARGET_NONCOMBAT_PET:
- {
- if(Unit* target = m_targets.getUnitTarget())
- if( target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isPet() && ((Pet*)target)->getPetType() == MINI_PET)
- TagUnitMap.push_back(target);
- }break;
- case TARGET_ALL_AROUND_CASTER:
- {
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_SELF_CENTER,SPELL_TARGETS_AOE_DAMAGE);
-
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- }break;
- case TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER:
- {
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_SELF_CENTER,SPELL_TARGETS_FRIENDLY);
-
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- }break;
- case TARGET_ALL_FRIENDLY_UNITS_IN_AREA:
- {
- CellPair p(MaNGOS::ComputeCellPair(m_targets.m_destX, m_targets.m_destY));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_DEST_CENTER,SPELL_TARGETS_FRIENDLY);
-
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- }break;
- // TARGET_SINGLE_PARTY means that the spells can only be casted on a party member and not on the caster (some sceals, fire shield from imp, etc..)
- case TARGET_SINGLE_PARTY:
- {
- Unit *target = m_targets.getUnitTarget();
- // Thoses spells apparently can't be casted on the caster.
- if( target && target != m_caster)
- {
- // Can only be casted on group's members or its pets
- Group *pGroup = NULL;
-
- Unit* owner = m_caster->GetCharmerOrOwner();
- Unit *targetOwner = target->GetCharmerOrOwner();
- if(owner)
- {
- if(owner->GetTypeId() == TYPEID_PLAYER)
- {
- if( target == owner )
- {
- TagUnitMap.push_back(target);
- break;
- }
- pGroup = ((Player*)owner)->GetGroup();
- }
- }
- else if (m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- if( targetOwner == m_caster && target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->isPet())
- {
- TagUnitMap.push_back(target);
- break;
- }
- pGroup = ((Player*)m_caster)->GetGroup();
- }
-
- if(pGroup)
- {
- // Our target can also be a player's pet who's grouped with us or our pet. But can't be controlled player
- if(targetOwner)
- {
- if( targetOwner->GetTypeId() == TYPEID_PLAYER &&
- target->GetTypeId()==TYPEID_UNIT && (((Creature*)target)->isPet()) &&
- target->GetOwnerGUID()==targetOwner->GetGUID() &&
- pGroup->IsMember(((Player*)targetOwner)->GetGUID()))
- {
- TagUnitMap.push_back(target);
- }
- }
- // 1Our target can be a player who is on our group
- else if (target->GetTypeId() == TYPEID_PLAYER && pGroup->IsMember(((Player*)target)->GetGUID()))
- {
- TagUnitMap.push_back(target);
- }
- }
- }
- }break;
- case TARGET_GAMEOBJECT:
- {
- if(m_targets.getGOTarget())
- AddGOTarget(m_targets.getGOTarget(), i);
- }break;
- case TARGET_IN_FRONT_OF_CASTER:
- {
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- bool inFront = m_spellInfo->SpellVisual != 3879;
- MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, inFront ? PUSH_IN_FRONT : PUSH_IN_BACK,SPELL_TARGETS_AOE_DAMAGE);
-
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- }break;
- case TARGET_DUELVSPLAYER:
- {
- Unit *target = m_targets.getUnitTarget();
- if(target)
- {
- if(m_caster->IsFriendlyTo(target))
- {
- TagUnitMap.push_back(target);
- }
- else
- {
- Unit* pUnitTarget = SelectMagnetTarget();
- if(pUnitTarget)
- TagUnitMap.push_back(pUnitTarget);
- }
- }
- }break;
- case TARGET_GAMEOBJECT_ITEM:
- {
- if(m_targets.getGOTargetGUID())
- AddGOTarget(m_targets.getGOTarget(), i);
- else if(m_targets.getItemTarget())
- AddItemTarget(m_targets.getItemTarget(), i);
- break;
- }
- case TARGET_MASTER:
- {
- if(Unit* owner = m_caster->GetCharmerOrOwner())
- TagUnitMap.push_back(owner);
- break;
- }
- case TARGET_ALL_ENEMY_IN_AREA_CHANNELED:
- {
- // targets the ground, not the units in the area
- if (m_spellInfo->Effect[i]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
- {
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_DEST_CENTER,SPELL_TARGETS_AOE_DAMAGE);
-
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- }
- }break;
- case TARGET_MINION:
- {
- if(m_spellInfo->Effect[i] != SPELL_EFFECT_DUEL)
- TagUnitMap.push_back(m_caster);
- }break;
- case TARGET_SINGLE_ENEMY:
- {
- Unit* pUnitTarget = SelectMagnetTarget();
- if(pUnitTarget)
- TagUnitMap.push_back(pUnitTarget);
- }break;
- case TARGET_AREAEFFECT_PARTY:
- {
- Unit* owner = m_caster->GetCharmerOrOwner();
- Player *pTarget = NULL;
-
- if(owner)
- {
- TagUnitMap.push_back(m_caster);
- if(owner->GetTypeId() == TYPEID_PLAYER)
- pTarget = (Player*)owner;
- }
- else if (m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- if(Unit* target = m_targets.getUnitTarget())
- {
- if( target->GetTypeId() != TYPEID_PLAYER)
- {
- if(((Creature*)target)->isPet())
- {
- Unit *targetOwner = target->GetOwner();
- if(targetOwner->GetTypeId() == TYPEID_PLAYER)
- pTarget = (Player*)targetOwner;
- }
- }
- else
- pTarget = (Player*)target;
- }
- }
-
- Group* pGroup = pTarget ? pTarget->GetGroup() : NULL;
-
- if(pGroup)
- {
- uint8 subgroup = pTarget->GetSubGroup();
-
- for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- Player* Target = itr->getSource();
-
- // IsHostileTo check duel and controlled by enemy
- if(Target && Target->GetSubGroup()==subgroup && !m_caster->IsHostileTo(Target))
- {
- if( pTarget->IsWithinDistInMap(Target, radius) )
- TagUnitMap.push_back(Target);
-
- if(Pet* pet = Target->GetPet())
- if( pTarget->IsWithinDistInMap(pet, radius) )
- TagUnitMap.push_back(pet);
- }
- }
- }
- else if (owner)
- {
- if(m_caster->IsWithinDistInMap(owner, radius))
- TagUnitMap.push_back(owner);
- }
- else if(pTarget)
- {
- TagUnitMap.push_back(pTarget);
-
- if(Pet* pet = pTarget->GetPet())
- if( m_caster->IsWithinDistInMap(pet, radius) )
- TagUnitMap.push_back(pet);
- }
-
- }break;
- case TARGET_SCRIPT:
- {
- if(m_targets.getUnitTarget())
- TagUnitMap.push_back(m_targets.getUnitTarget());
- if(m_targets.getItemTarget())
- AddItemTarget(m_targets.getItemTarget(), i);
- }break;
- case TARGET_SELF_FISHING:
- {
- TagUnitMap.push_back(m_caster);
- }break;
- case TARGET_CHAIN_HEAL:
- {
- Unit* pUnitTarget = m_targets.getUnitTarget();
- if(!pUnitTarget)
- break;
-
- if (EffectChainTarget <= 1)
- TagUnitMap.push_back(pUnitTarget);
- else
- {
- unMaxTargets = EffectChainTarget;
- float max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS;
-
- std::list<Unit *> tempUnitMap;
-
- {
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, tempUnitMap, max_range, PUSH_SELF_CENTER, SPELL_TARGETS_FRIENDLY);
-
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
-
- }
-
- if(m_caster != pUnitTarget && std::find(tempUnitMap.begin(),tempUnitMap.end(),m_caster) == tempUnitMap.end() )
- tempUnitMap.push_front(m_caster);
-
- tempUnitMap.sort(TargetDistanceOrder(pUnitTarget));
-
- if(tempUnitMap.empty())
- break;
-
- if(*tempUnitMap.begin() == pUnitTarget)
- tempUnitMap.erase(tempUnitMap.begin());
-
- TagUnitMap.push_back(pUnitTarget);
- uint32 t = unMaxTargets - 1;
- Unit *prev = pUnitTarget;
- std::list<Unit*>::iterator next = tempUnitMap.begin();
-
- while(t && next != tempUnitMap.end() )
- {
- if(prev->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS)
- break;
-
- if(!prev->IsWithinLOSInMap(*next))
- {
- ++next;
- continue;
- }
-
- if((*next)->GetHealth() == (*next)->GetMaxHealth())
- {
- next = tempUnitMap.erase(next);
- continue;
- }
-
- prev = *next;
- TagUnitMap.push_back(prev);
- tempUnitMap.erase(next);
- tempUnitMap.sort(TargetDistanceOrder(prev));
- next = tempUnitMap.begin();
-
- --t;
- }
- }
- }break;
- case TARGET_CURRENT_ENEMY_COORDINATES:
- {
- Unit* currentTarget = m_targets.getUnitTarget();
- if(currentTarget)
- {
- TagUnitMap.push_back(currentTarget);
- m_targets.setDestination(currentTarget->GetPositionX(), currentTarget->GetPositionY(), currentTarget->GetPositionZ());
- if(m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_ENEMY_IN_AREA_INSTANT)
- {
- CellPair p(MaNGOS::ComputeCellPair(currentTarget->GetPositionX(), currentTarget->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
- MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius,PUSH_TARGET_CENTER, SPELL_TARGETS_AOE_DAMAGE);
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_notifier(notifier);
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_notifier(notifier);
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- }
- }
- }break;
- case TARGET_AREAEFFECT_PARTY_AND_CLASS:
- {
- Player* targetPlayer = m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER
- ? (Player*)m_targets.getUnitTarget() : NULL;
-
- Group* pGroup = targetPlayer ? targetPlayer->GetGroup() : NULL;
- if(pGroup)
- {
- for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- Player* Target = itr->getSource();
-
- // IsHostileTo check duel and controlled by enemy
- if( Target && targetPlayer->IsWithinDistInMap(Target, radius) &&
- targetPlayer->getClass() == Target->getClass() &&
- !m_caster->IsHostileTo(Target) )
- {
- TagUnitMap.push_back(Target);
- }
- }
- }
- else if(m_targets.getUnitTarget())
- TagUnitMap.push_back(m_targets.getUnitTarget());
- break;
- }
- case TARGET_TABLE_X_Y_Z_COORDINATES:
- {
- SpellTargetPosition const* st = spellmgr.GetSpellTargetPosition(m_spellInfo->Id);
- if(st)
- {
- if (st->target_mapId == m_caster->GetMapId())
- m_targets.setDestination(st->target_X, st->target_Y, st->target_Z);
-
- // if B==TARGET_TABLE_X_Y_Z_COORDINATES then A already fill all required targets
- if (m_spellInfo->EffectImplicitTargetB[i] && m_spellInfo->EffectImplicitTargetB[i]!=TARGET_TABLE_X_Y_Z_COORDINATES)
- {
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- SpellTargets targetB = SPELL_TARGETS_AOE_DAMAGE;
- // Select friendly targets for positive effect
- if (IsPositiveEffect(m_spellInfo->Id, i))
- targetB = SPELL_TARGETS_FRIENDLY;
-
- MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius,PUSH_DEST_CENTER, targetB);
-
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_notifier(notifier);
- TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_notifier(notifier);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- cell_lock->Visit(cell_lock, grid_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
- }
- }
- else
- sLog.outError( "SPELL: unknown target coordinates for spell ID %u\n", m_spellInfo->Id );
- }break;
- case TARGET_BEHIND_VICTIM:
- {
- Unit *pTarget = m_caster->getVictim();
- if(!pTarget && m_caster->GetTypeId() == TYPEID_PLAYER)
- pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection());
-
- if(pTarget)
- {
- float _target_x, _target_y, _target_z;
- pTarget->GetClosePoint(_target_x, _target_y, _target_z, m_caster->GetObjectSize(), CONTACT_DISTANCE, M_PI);
- if(pTarget->IsWithinLOS(_target_x,_target_y,_target_z))
- m_targets.setDestination(_target_x, _target_y, _target_z);
- }
- }break;
- default:
- break;
- }
-
- if (unMaxTargets && TagUnitMap.size() > unMaxTargets)
- {
- // make sure one unit is always removed per iteration
- uint32 removed_utarget = 0;
- for (std::list<Unit*>::iterator itr = TagUnitMap.begin(), next; itr != TagUnitMap.end(); itr = next)
- {
- next = itr;
- ++next;
- if (!*itr) continue;
- if ((*itr) == m_targets.getUnitTarget())
- {
- TagUnitMap.erase(itr);
- removed_utarget = 1;
- // break;
- }
- }
- // remove random units from the map
- while (TagUnitMap.size() > unMaxTargets - removed_utarget)
- {
- uint32 poz = urand(0, TagUnitMap.size()-1);
- for (std::list<Unit*>::iterator itr = TagUnitMap.begin(); itr != TagUnitMap.end(); ++itr, --poz)
- {
- if (!*itr) continue;
- if (!poz)
- {
- TagUnitMap.erase(itr);
- break;
- }
- }
- }
- // the player's target will always be added to the map
- if (removed_utarget && m_targets.getUnitTarget())
- TagUnitMap.push_back(m_targets.getUnitTarget());
- }
-}
-
-void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura)
-{
- m_targets = *targets;
-
- m_spellState = SPELL_STATE_PREPARING;
-
- m_castPositionX = m_caster->GetPositionX();
- m_castPositionY = m_caster->GetPositionY();
- m_castPositionZ = m_caster->GetPositionZ();
- m_castOrientation = m_caster->GetOrientation();
-
- if(triggeredByAura)
- m_triggeredByAuraSpell = triggeredByAura->GetSpellProto();
-
- // create and add update event for this spell
- SpellEvent* Event = new SpellEvent(this);
- m_caster->m_Events.AddEvent(Event, m_caster->m_Events.CalculateTime(1));
-
- //Prevent casting at cast another spell (ServerSide check)
- if(m_caster->IsNonMeleeSpellCasted(false, true) && m_cast_count)
- {
- SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS);
- finish(false);
- return;
- }
-
- // Fill cost data
- m_powerCost = CalculatePowerCost();
-
- uint8 result = CanCast(true);
- if(result != 0 && !IsAutoRepeat()) //always cast autorepeat dummy for triggering
- {
- if(triggeredByAura)
- {
- SendChannelUpdate(0);
- triggeredByAura->SetAuraDuration(0);
- }
- SendCastResult(result);
- finish(false);
- return;
- }
-
- // calculate cast time (calculated after first CanCast check to prevent charge counting for first CanCast fail)
- m_casttime = GetSpellCastTime(m_spellInfo, this);
-
- // set timer base at cast time
- ReSetTimer();
-
- // stealth must be removed at cast starting (at show channel bar)
- // skip triggered spell (item equip spell casting and other not explicit character casts/item uses)
- if ( !m_IsTriggeredSpell && isSpellBreakStealth(m_spellInfo) )
- {
- m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
- m_caster->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
- }
-
- if(m_IsTriggeredSpell)
- cast(true);
- else
- {
- m_caster->SetCurrentCastedSpell( this );
- m_selfContainer = &(m_caster->m_currentSpells[GetCurrentContainer()]);
- SendSpellStart();
- }
-}
-
-void Spell::cancel()
-{
- if(m_spellState == SPELL_STATE_FINISHED)
- return;
-
- m_autoRepeat = false;
- switch (m_spellState)
- {
- case SPELL_STATE_PREPARING:
- case SPELL_STATE_DELAYED:
- {
- SendInterrupted(0);
- SendCastResult(SPELL_FAILED_INTERRUPTED);
- } break;
-
- case SPELL_STATE_CASTING:
- {
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- {
- if( ihit->missCondition == SPELL_MISS_NONE )
- {
- Unit* unit = m_caster->GetGUID()==(*ihit).targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
- if( unit && unit->isAlive() )
- unit->RemoveAurasDueToSpell(m_spellInfo->Id);
- }
- }
-
- m_caster->RemoveAurasDueToSpell(m_spellInfo->Id);
- SendChannelUpdate(0);
- SendInterrupted(0);
- SendCastResult(SPELL_FAILED_INTERRUPTED);
- } break;
-
- default:
- {
- } break;
- }
-
- finish(false);
- m_caster->RemoveDynObject(m_spellInfo->Id);
- m_caster->RemoveGameObject(m_spellInfo->Id,true);
-}
-
-void Spell::cast(bool skipCheck)
-{
- uint8 castResult = 0;
-
- // update pointers base at GUIDs to prevent access to non-existed already object
- UpdatePointers();
-
- // cancel at lost main target unit
- if(!m_targets.getUnitTarget() && m_targets.getUnitTargetGUID() && m_targets.getUnitTargetGUID() != m_caster->GetGUID())
- {
- cancel();
- return;
- }
-
- if(m_caster->GetTypeId() != TYPEID_PLAYER && m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster)
- m_caster->SetInFront(m_targets.getUnitTarget());
-
- castResult = CheckPower();
- if(castResult != 0)
- {
- SendCastResult(castResult);
- finish(false);
- return;
- }
-
- // triggered cast called from Spell::prepare where it was already checked
- if(!skipCheck)
- {
- castResult = CanCast(false);
- if(castResult != 0)
- {
- SendCastResult(castResult);
- finish(false);
- return;
- }
- }
-
- // Conflagrate - consumes immolate
- if ((m_spellInfo->TargetAuraState == AURA_STATE_IMMOLATE) && m_targets.getUnitTarget())
- {
- // for caster applied auras only
- Unit::AuraList const &mPeriodic = m_targets.getUnitTarget()->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
- for(Unit::AuraList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i)
- {
- if( (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && ((*i)->GetSpellProto()->SpellFamilyFlags & 4) &&
- (*i)->GetCasterGUID()==m_caster->GetGUID() )
- {
- m_targets.getUnitTarget()->RemoveAura((*i)->GetId(), (*i)->GetEffIndex());
- break;
- }
- }
- }
-
- // traded items have trade slot instead of guid in m_itemTargetGUID
- // set to real guid to be sent later to the client
- m_targets.updateTradeSlotItem();
-
- // CAST SPELL
- SendSpellCooldown();
-
- TakePower();
- TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot
- FillTargetMap();
-
- if(m_spellState == SPELL_STATE_FINISHED) // stop cast if spell marked as finish somewhere in Take*/FillTargetMap
- return;
-
- SendCastResult(castResult);
- SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()...
-
- // Pass cast spell event to handler (not send triggered by aura spells)
- if (m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE && m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_RANGED && !m_triggeredByAuraSpell)
- {
- m_caster->ProcDamageAndSpell(m_targets.getUnitTarget(), PROC_FLAG_CAST_SPELL, PROC_FLAG_NONE, 0, SPELL_SCHOOL_MASK_NONE, m_spellInfo, m_IsTriggeredSpell);
-
- // update pointers base at GUIDs to prevent access to non-existed already object
- UpdatePointers(); // pointers can be invalidate at triggered spell casting
- }
-
- // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells
- if (m_spellInfo->speed > 0.0f)
- {
-
- // Remove used for cast item if need (it can be already NULL after TakeReagents call
- // in case delayed spell remove item at cast delay start
- TakeCastItem();
-
- // Okay, maps created, now prepare flags
- m_immediateHandled = false;
- m_spellState = SPELL_STATE_DELAYED;
- SetDelayStart(0);
- }
- else
- {
- // Immediate spell, no big deal
- handle_immediate();
- }
-}
-
-void Spell::handle_immediate()
-{
- // start channeling if applicable
- if(IsChanneledSpell(m_spellInfo))
- {
- m_spellState = SPELL_STATE_CASTING;
- SendChannelStart(GetSpellDuration(m_spellInfo));
- }
-
- // process immediate effects (items, ground, etc.) also initialize some variables
- _handle_immediate_phase();
-
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- DoAllEffectOnTarget(&(*ihit));
-
- for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit)
- DoAllEffectOnTarget(&(*ihit));
-
- // spell is finished, perform some last features of the spell here
- _handle_finish_phase();
-
- // Remove used for cast item if need (it can be already NULL after TakeReagents call
- TakeCastItem();
-
- if(m_spellState != SPELL_STATE_CASTING)
- finish(true); // successfully finish spell cast (not last in case autorepeat or channel spell)
-}
-
-uint64 Spell::handle_delayed(uint64 t_offset)
-{
- uint64 next_time = 0;
-
- if (!m_immediateHandled)
- {
- _handle_immediate_phase();
- m_immediateHandled = true;
- }
-
- // now recheck units targeting correctness (need before any effects apply to prevent adding immunity at first effect not allow apply second spell effect and similar cases)
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end();++ihit)
- {
- if (ihit->processed == false)
- {
- if ( ihit->timeDelay <= t_offset )
- DoAllEffectOnTarget(&(*ihit));
- else if( next_time == 0 || ihit->timeDelay < next_time )
- next_time = ihit->timeDelay;
- }
- }
-
- // now recheck gameobject targeting correctness
- for(std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end();++ighit)
- {
- if (ighit->processed == false)
- {
- if ( ighit->timeDelay <= t_offset )
- DoAllEffectOnTarget(&(*ighit));
- else if( next_time == 0 || ighit->timeDelay < next_time )
- next_time = ighit->timeDelay;
- }
- }
- // All targets passed - need finish phase
- if (next_time == 0)
- {
- // spell is finished, perform some last features of the spell here
- _handle_finish_phase();
-
- finish(true); // successfully finish spell cast
-
- // return zero, spell is finished now
- return 0;
- }
- else
- {
- // spell is unfinished, return next execution time
- return next_time;
- }
-}
-
-void Spell::_handle_immediate_phase()
-{
- // handle some immediate features of the spell here
- HandleThreatSpells(m_spellInfo->Id);
-
- m_needSpellLog = IsNeedSendToClient();
- for(uint32 j = 0;j<3;j++)
- {
- if(m_spellInfo->Effect[j]==0)
- continue;
-
- // apply Send Event effect to ground in case empty target lists
- if( m_spellInfo->Effect[j] == SPELL_EFFECT_SEND_EVENT && !HaveTargetsForEffect(j) )
- {
- HandleEffects(NULL,NULL,NULL, j);
- continue;
- }
-
- // Don't do spell log, if is school damage spell
- if(m_spellInfo->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE || m_spellInfo->Effect[j] == 0)
- m_needSpellLog = false;
-
- uint32 EffectChainTarget = m_spellInfo->EffectChainTarget[j];
- if(m_originalCaster)
- if(Player* modOwner = m_originalCaster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this);
-
- // initialize multipliers
- m_damageMultipliers[j] = 1.0f;
- if( (m_spellInfo->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE || m_spellInfo->EffectImplicitTargetA[j] == TARGET_CHAIN_HEAL) &&
- (EffectChainTarget > 1) )
- m_applyMultiplierMask |= 1 << j;
- }
-
- // initialize Diminishing Returns Data
- m_diminishLevel = DIMINISHING_LEVEL_1;
- m_diminishGroup = DIMINISHING_NONE;
-
- // process items
- for(std::list<ItemTargetInfo>::iterator ihit= m_UniqueItemInfo.begin();ihit != m_UniqueItemInfo.end();++ihit)
- DoAllEffectOnTarget(&(*ihit));
-
- // process ground
- for(uint32 j = 0;j<3;j++)
- {
- // persistent area auras target only the ground
- if(m_spellInfo->Effect[j] == SPELL_EFFECT_PERSISTENT_AREA_AURA)
- HandleEffects(NULL,NULL,NULL, j);
- }
-}
-
-void Spell::_handle_finish_phase()
-{
- // spell log
- if(m_needSpellLog)
- SendLogExecute();
-}
-
-void Spell::SendSpellCooldown()
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player* _player = (Player*)m_caster;
- // Add cooldown for max (disable spell)
- // Cooldown started on SendCooldownEvent call
- if (m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
- {
- _player->AddSpellCooldown(m_spellInfo->Id, 0, time(NULL) - 1);
- return;
- }
-
- // init cooldown values
- uint32 cat = 0;
- int32 rec = -1;
- int32 catrec = -1;
-
- // some special item spells without correct cooldown in SpellInfo
- // cooldown information stored in item prototype
- // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client.
-
- if(m_CastItem)
- {
- ItemPrototype const* proto = m_CastItem->GetProto();
- if(proto)
- {
- for(int idx = 0; idx < 5; ++idx)
- {
- if(proto->Spells[idx].SpellId == m_spellInfo->Id)
- {
- cat = proto->Spells[idx].SpellCategory;
- rec = proto->Spells[idx].SpellCooldown;
- catrec = proto->Spells[idx].SpellCategoryCooldown;
- break;
- }
- }
- }
- }
-
- // if no cooldown found above then base at DBC data
- if(rec < 0 && catrec < 0)
- {
- cat = m_spellInfo->Category;
- rec = m_spellInfo->RecoveryTime;
- catrec = m_spellInfo->CategoryRecoveryTime;
- }
-
- // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
- // prevent 0 cooldowns set by another way
- if (rec <= 0 && catrec <= 0 && (cat == 76 || cat == 351))
- rec = _player->GetAttackTime(RANGED_ATTACK);
-
- // Now we have cooldown data (if found any), time to apply mods
- if(rec > 0)
- _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, rec, this);
-
- if(catrec > 0)
- _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, catrec, this);
-
- // replace negative cooldowns by 0
- if (rec < 0) rec = 0;
- if (catrec < 0) catrec = 0;
-
- // no cooldown after applying spell mods
- if( rec == 0 && catrec == 0)
- return;
-
- time_t curTime = time(NULL);
-
- time_t catrecTime = catrec ? curTime+catrec/1000 : 0; // in secs
- time_t recTime = rec ? curTime+rec/1000 : catrecTime;// in secs
-
- // self spell cooldown
- if(recTime > 0)
- _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, recTime);
-
- // category spells
- if (catrec > 0)
- {
- SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat);
- if(i_scstore != sSpellCategoryStore.end())
- {
- for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
- {
- if(*i_scset == m_spellInfo->Id) // skip main spell, already handled above
- continue;
-
- _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, catrecTime);
- }
- }
- }
-}
-
-void Spell::update(uint32 difftime)
-{
- // update pointers based at it's GUIDs
- UpdatePointers();
-
- if(m_targets.getUnitTargetGUID() && !m_targets.getUnitTarget())
- {
- cancel();
- return;
- }
-
- // check if the player caster has moved before the spell finished
- if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) &&
- (m_castPositionX != m_caster->GetPositionX() || m_castPositionY != m_caster->GetPositionY() || m_castPositionZ != m_caster->GetPositionZ()) &&
- (m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)))
- {
- // always cancel for channeled spells
- if( m_spellState == SPELL_STATE_CASTING )
- cancel();
- // don't cancel for melee, autorepeat, triggered and instant spells
- else if(!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_IsTriggeredSpell && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT))
- cancel();
- }
-
- switch(m_spellState)
- {
- case SPELL_STATE_PREPARING:
- {
- if(m_timer)
- {
- if(difftime >= m_timer)
- m_timer = 0;
- else
- m_timer -= difftime;
- }
-
- if(m_timer == 0 && !IsNextMeleeSwingSpell() && !IsAutoRepeat())
- cast();
- } break;
- case SPELL_STATE_CASTING:
- {
- if(m_timer > 0)
- {
- if( m_caster->GetTypeId() == TYPEID_PLAYER )
- {
- // check if player has jumped before the channeling finished
- if(m_caster->HasUnitMovementFlag(MOVEMENTFLAG_JUMPING))
- cancel();
-
- // check for incapacitating player states
- if( m_caster->hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_CONFUSED))
- cancel();
-
- // check if player has turned if flag is set
- if( m_spellInfo->ChannelInterruptFlags & CHANNEL_FLAG_TURNING && m_castOrientation != m_caster->GetOrientation() )
- cancel();
- }
-
- // check if there are alive targets left
- if (!IsAliveUnitPresentInTargetList())
- {
- SendChannelUpdate(0);
- finish();
- }
-
- if(difftime >= m_timer)
- m_timer = 0;
- else
- m_timer -= difftime;
- }
-
- if(m_timer == 0)
- {
- SendChannelUpdate(0);
-
- // channeled spell processed independently for quest targeting
- // cast at creature (or GO) quest objectives update at successful cast channel finished
- // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... )
- if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() )
- {
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- {
- TargetInfo* target = &*ihit;
- if(!IS_CREATURE_GUID(target->targetGUID))
- continue;
-
- Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID);
- if (unit==NULL)
- continue;
-
- ((Player*)m_caster)->CastedCreatureOrGO(unit->GetEntry(),unit->GetGUID(),m_spellInfo->Id);
- }
-
- for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit)
- {
- GOTargetInfo* target = &*ihit;
-
- GameObject* go = ObjectAccessor::GetGameObject(*m_caster, target->targetGUID);
- if(!go)
- continue;
-
- ((Player*)m_caster)->CastedCreatureOrGO(go->GetEntry(),go->GetGUID(),m_spellInfo->Id);
- }
- }
-
- finish();
- }
- } break;
- default:
- {
- }break;
- }
-}
-
-void Spell::finish(bool ok)
-{
- if(!m_caster)
- return;
-
- if(m_spellState == SPELL_STATE_FINISHED)
- return;
-
- m_spellState = SPELL_STATE_FINISHED;
-
- //remove spell mods
- if (m_caster->GetTypeId() == TYPEID_PLAYER)
- ((Player*)m_caster)->RemoveSpellMods(this);
-
- // other code related only to successfully finished spells
- if(!ok)
- return;
-
- //handle SPELL_AURA_ADD_TARGET_TRIGGER auras
- Unit::AuraList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER);
- for(Unit::AuraList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i)
- {
- SpellEntry const *auraSpellInfo = (*i)->GetSpellProto();
- uint32 auraSpellIdx = (*i)->GetEffIndex();
- if (IsAffectedBy(auraSpellInfo, auraSpellIdx))
- {
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- if( ihit->effectMask & (1<<auraSpellIdx) )
- {
- // check m_caster->GetGUID() let load auras at login and speedup most often case
- Unit *unit = m_caster->GetGUID()== ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
- if (unit && unit->isAlive())
- {
- // Calculate chance at that moment (can be depend for example from combo points)
- int32 chance = m_caster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, (*i)->GetBasePoints(),unit);
-
- if(roll_chance_i(chance))
- m_caster->CastSpell(unit, auraSpellInfo->EffectTriggerSpell[auraSpellIdx], true, NULL, (*i));
- }
- }
- }
- }
-
- if (IsMeleeAttackResetSpell())
- {
- m_caster->resetAttackTimer(BASE_ATTACK);
- if(m_caster->haveOffhandWeapon())
- m_caster->resetAttackTimer(OFF_ATTACK);
- }
-
- /*if (IsRangedAttackResetSpell())
- m_caster->resetAttackTimer(RANGED_ATTACK);*/
-
- // Clear combo at finish state
- if(m_caster->GetTypeId() == TYPEID_PLAYER && NeedsComboPoints(m_spellInfo))
- ((Player*)m_caster)->ClearComboPoints();
-
- // call triggered spell only at successful cast (after clear combo points -> for add some if need)
- if(!m_TriggerSpells.empty())
- TriggerSpell();
-
- // Stop Attack for some spells
- if( m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET )
- m_caster->AttackStop();
-}
-
-void Spell::SendCastResult(uint8 result)
-{
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if(((Player*)m_caster)->GetSession()->PlayerLoading()) // don't send cast results at loading time
- return;
-
- if(result != 0)
- {
- WorldPacket data(SMSG_CAST_FAILED, (4+1+1));
- data << uint32(m_spellInfo->Id);
- data << uint8(result); // problem
- data << uint8(m_cast_count); // single cast or multi 2.3 (0/1)
- switch (result)
- {
- case SPELL_FAILED_REQUIRES_SPELL_FOCUS:
- data << uint32(m_spellInfo->RequiresSpellFocus);
- break;
- case SPELL_FAILED_REQUIRES_AREA:
- // hardcode areas limitation case
- if( m_spellInfo->Id==41618 || m_spellInfo->Id==41620 )
- data << uint32(3842);
- else if( m_spellInfo->Id==41617 || m_spellInfo->Id==41619 )
- data << uint32(3905);
- // normal case
- else
- data << uint32(m_spellInfo->AreaId);
- break;
- case SPELL_FAILED_TOTEMS:
- if(m_spellInfo->Totem[0])
- data << uint32(m_spellInfo->Totem[0]);
- if(m_spellInfo->Totem[1])
- data << uint32(m_spellInfo->Totem[1]);
- break;
- case SPELL_FAILED_TOTEM_CATEGORY:
- if(m_spellInfo->TotemCategory[0])
- data << uint32(m_spellInfo->TotemCategory[0]);
- if(m_spellInfo->TotemCategory[1])
- data << uint32(m_spellInfo->TotemCategory[1]);
- break;
- case SPELL_FAILED_EQUIPPED_ITEM_CLASS:
- data << uint32(m_spellInfo->EquippedItemClass);
- data << uint32(m_spellInfo->EquippedItemSubClassMask);
- data << uint32(m_spellInfo->EquippedItemInventoryTypeMask);
- break;
- }
- ((Player*)m_caster)->GetSession()->SendPacket(&data);
- }
- else
- {
- WorldPacket data(SMSG_CLEAR_EXTRA_AURA_INFO, (8+4));
- data.append(m_caster->GetPackGUID());
- data << uint32(m_spellInfo->Id);
- ((Player*)m_caster)->GetSession()->SendPacket(&data);
- }
-}
-
-void Spell::SendSpellStart()
-{
- if(!IsNeedSendToClient())
- return;
-
- sLog.outDebug("Sending SMSG_SPELL_START id=%u",m_spellInfo->Id);
-
- uint16 castFlags = CAST_FLAG_UNKNOWN1;
- if(IsRangedSpell())
- castFlags |= CAST_FLAG_AMMO;
-
- Unit * target;
- if(!m_targets.getUnitTarget())
- target = m_caster;
- else
- target = m_targets.getUnitTarget();
-
- WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2));
- if(m_CastItem)
- data.append(m_CastItem->GetPackGUID());
- else
- data.append(m_caster->GetPackGUID());
-
- data.append(m_caster->GetPackGUID());
- data << uint32(m_spellInfo->Id);
- data << uint8(m_cast_count); // single cast or multi 2.3 (0/1)
- data << uint16(castFlags);
- data << uint32(m_timer);
-
- m_targets.write(&data);
-
- if( castFlags & CAST_FLAG_AMMO )
- WriteAmmoToPacket(&data);
-
- m_caster->SendMessageToSet(&data, true);
-}
-
-void Spell::SendSpellGo()
-{
- // not send invisible spell casting
- if(!IsNeedSendToClient())
- return;
-
- sLog.outDebug("Sending SMSG_SPELL_GO id=%u",m_spellInfo->Id);
-
- Unit * target;
- if(!m_targets.getUnitTarget())
- target = m_caster;
- else
- target = m_targets.getUnitTarget();
-
- uint16 castFlags = CAST_FLAG_UNKNOWN3;
- if(IsRangedSpell())
- castFlags |= CAST_FLAG_AMMO;
-
- WorldPacket data(SMSG_SPELL_GO, 50); // guess size
- if(m_CastItem)
- data.append(m_CastItem->GetPackGUID());
- else
- data.append(m_caster->GetPackGUID());
-
- data.append(m_caster->GetPackGUID());
- data << uint32(m_spellInfo->Id);
- data << uint16(castFlags);
- data << uint32(getMSTime()); // timestamp
-
- WriteSpellGoTargets(&data);
-
- m_targets.write(&data);
-
- if( castFlags & CAST_FLAG_AMMO )
- WriteAmmoToPacket(&data);
-
- m_caster->SendMessageToSet(&data, true);
-}
-
-void Spell::WriteAmmoToPacket( WorldPacket * data )
-{
- uint32 ammoInventoryType = 0;
- uint32 ammoDisplayID = 0;
-
- if (m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK );
- if(pItem)
- {
- ammoInventoryType = pItem->GetProto()->InventoryType;
- if( ammoInventoryType == INVTYPE_THROWN )
- ammoDisplayID = pItem->GetProto()->DisplayInfoID;
- else
- {
- uint32 ammoID = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID);
- if(ammoID)
- {
- ItemPrototype const *pProto = objmgr.GetItemPrototype( ammoID );
- if(pProto)
- {
- ammoDisplayID = pProto->DisplayInfoID;
- ammoInventoryType = pProto->InventoryType;
- }
- }
- else if(m_caster->GetDummyAura(46699)) // Requires No Ammo
- {
- ammoDisplayID = 5996; // normal arrow
- ammoInventoryType = INVTYPE_AMMO;
- }
- }
- }
- }
- // TODO: implement selection ammo data based at ranged weapon stored in equipmodel/equipinfo/equipslot fields
-
- *data << uint32(ammoDisplayID);
- *data << uint32(ammoInventoryType);
-}
-
-void Spell::WriteSpellGoTargets( WorldPacket * data )
-{
- *data << (uint8)m_countOfHit;
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- if ((*ihit).missCondition == SPELL_MISS_NONE) // Add only hits
- *data << uint64(ihit->targetGUID);
-
- for(std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin();ighit != m_UniqueGOTargetInfo.end();++ighit)
- *data << uint64(ighit->targetGUID); // Always hits
-
- *data << (uint8)m_countOfMiss;
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- {
- if( ihit->missCondition != SPELL_MISS_NONE ) // Add only miss
- {
- *data << uint64(ihit->targetGUID);
- *data << uint8(ihit->missCondition);
- if( ihit->missCondition == SPELL_MISS_REFLECT )
- *data << uint8(ihit->reflectResult);
- }
- }
-}
-
-void Spell::SendLogExecute()
-{
- Unit *target = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster;
-
- WorldPacket data(SMSG_SPELLLOGEXECUTE, (8+4+4+4+4+8));
-
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- data.append(m_caster->GetPackGUID());
- else
- data.append(target->GetPackGUID());
-
- data << uint32(m_spellInfo->Id);
- uint32 count1 = 1;
- data << uint32(count1); // count1 (effect count?)
- for(uint32 i = 0; i < count1; ++i)
- {
- data << uint32(m_spellInfo->Effect[0]); // spell effect?
- uint32 count2 = 1;
- data << uint32(count2); // count2 (target count?)
- for(uint32 j = 0; j < count2; ++j)
- {
- switch(m_spellInfo->Effect[0])
- {
- case SPELL_EFFECT_POWER_DRAIN:
- if(Unit *unit = m_targets.getUnitTarget())
- data.append(unit->GetPackGUID());
- else
- data << uint8(0);
- data << uint32(0);
- data << uint32(0);
- data << float(0);
- break;
- case SPELL_EFFECT_ADD_EXTRA_ATTACKS:
- if(Unit *unit = m_targets.getUnitTarget())
- data.append(unit->GetPackGUID());
- else
- data << uint8(0);
- data << uint32(0); // count?
- break;
- case SPELL_EFFECT_INTERRUPT_CAST:
- if(Unit *unit = m_targets.getUnitTarget())
- data.append(unit->GetPackGUID());
- else
- data << uint8(0);
- data << uint32(0); // spellid
- break;
- case SPELL_EFFECT_DURABILITY_DAMAGE:
- if(Unit *unit = m_targets.getUnitTarget())
- data.append(unit->GetPackGUID());
- else
- data << uint8(0);
- data << uint32(0);
- data << uint32(0);
- break;
- case SPELL_EFFECT_OPEN_LOCK:
- case SPELL_EFFECT_OPEN_LOCK_ITEM:
- if(Item *item = m_targets.getItemTarget())
- data.append(item->GetPackGUID());
- else
- data << uint8(0);
- break;
- case SPELL_EFFECT_CREATE_ITEM:
- data << uint32(m_spellInfo->EffectItemType[0]);
- break;
- case SPELL_EFFECT_SUMMON:
- case SPELL_EFFECT_SUMMON_WILD:
- case SPELL_EFFECT_SUMMON_GUARDIAN:
- case SPELL_EFFECT_TRANS_DOOR:
- case SPELL_EFFECT_SUMMON_PET:
- case SPELL_EFFECT_SUMMON_POSSESSED:
- case SPELL_EFFECT_SUMMON_TOTEM:
- case SPELL_EFFECT_SUMMON_OBJECT_WILD:
- case SPELL_EFFECT_CREATE_HOUSE:
- case SPELL_EFFECT_DUEL:
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT1:
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT2:
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT3:
- case SPELL_EFFECT_SUMMON_TOTEM_SLOT4:
- case SPELL_EFFECT_SUMMON_PHANTASM:
- case SPELL_EFFECT_SUMMON_CRITTER:
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT1:
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT2:
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT3:
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT4:
- case SPELL_EFFECT_SUMMON_DEMON:
- case SPELL_EFFECT_150:
- if(Unit *unit = m_targets.getUnitTarget())
- data.append(unit->GetPackGUID());
- else if(m_targets.getItemTargetGUID())
- data.appendPackGUID(m_targets.getItemTargetGUID());
- else if(GameObject *go = m_targets.getGOTarget())
- data.append(go->GetPackGUID());
- else
- data << uint8(0); // guid
- break;
- case SPELL_EFFECT_FEED_PET:
- data << uint32(m_targets.getItemTargetEntry());
- break;
- case SPELL_EFFECT_DISMISS_PET:
- if(Unit *unit = m_targets.getUnitTarget())
- data.append(unit->GetPackGUID());
- else
- data << uint8(0);
- break;
- default:
- return;
- }
- }
- }
-
- m_caster->SendMessageToSet(&data, true);
-}
-
-void Spell::SendInterrupted(uint8 result)
-{
- WorldPacket data(SMSG_SPELL_FAILURE, (8+4+1));
- data.append(m_caster->GetPackGUID());
- data << m_spellInfo->Id;
- data << result;
- m_caster->SendMessageToSet(&data, true);
-
- data.Initialize(SMSG_SPELL_FAILED_OTHER, (8+4));
- data.append(m_caster->GetPackGUID());
- data << m_spellInfo->Id;
- m_caster->SendMessageToSet(&data, true);
-}
-
-void Spell::SendChannelUpdate(uint32 time)
-{
- if(time == 0)
- {
- m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,0);
- m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL,0);
- }
-
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- WorldPacket data( MSG_CHANNEL_UPDATE, 8+4 );
- data.append(m_caster->GetPackGUID());
- data << time;
-
- ((Player*)m_caster)->GetSession()->SendPacket( &data );
-}
-
-void Spell::SendChannelStart(uint32 duration)
-{
- WorldObject* target = NULL;
-
- // select first not rsusted target from target list for _0_ effect
- if(!m_UniqueTargetInfo.empty())
- {
- for(std::list<TargetInfo>::iterator itr= m_UniqueTargetInfo.begin();itr != m_UniqueTargetInfo.end();++itr)
- {
- if( (itr->effectMask & (1<<0)) && itr->reflectResult==SPELL_MISS_NONE && itr->targetGUID != m_caster->GetGUID())
- {
- target = ObjectAccessor::GetUnit(*m_caster, itr->targetGUID);
- break;
- }
- }
- }
- else if(!m_UniqueGOTargetInfo.empty())
- {
- for(std::list<GOTargetInfo>::iterator itr= m_UniqueGOTargetInfo.begin();itr != m_UniqueGOTargetInfo.end();++itr)
- {
- if(itr->effectMask & (1<<0) )
- {
- target = ObjectAccessor::GetGameObject(*m_caster, itr->targetGUID);
- break;
- }
- }
- }
-
- if (m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- WorldPacket data( MSG_CHANNEL_START, (8+4+4) );
- data.append(m_caster->GetPackGUID());
- data << m_spellInfo->Id;
- data << duration;
-
- ((Player*)m_caster)->GetSession()->SendPacket( &data );
- }
-
- m_timer = duration;
- if(target)
- m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, target->GetGUID());
- m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id);
-}
-
-void Spell::SendResurrectRequest(Player* target)
-{
- WorldPacket data(SMSG_RESURRECT_REQUEST, (8+4+2+4));
- data << m_caster->GetGUID();
- data << uint32(1) << uint16(0) << uint32(1);
-
- target->GetSession()->SendPacket(&data);
-}
-
-void Spell::SendPlaySpellVisual(uint32 SpellID)
-{
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12);
- data << m_caster->GetGUID();
- data << SpellID;
- ((Player*)m_caster)->GetSession()->SendPacket(&data);
-}
-
-void Spell::TakeCastItem()
-{
- if(!m_CastItem || m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- // not remove cast item at triggered spell (equipping, weapon damage, etc)
- if(m_IsTriggeredSpell)
- return;
-
- ItemPrototype const *proto = m_CastItem->GetProto();
-
- if(!proto)
- {
- // This code is to avoid a crash
- // I'm not sure, if this is really an error, but I guess every item needs a prototype
- sLog.outError("Cast item has no item prototype highId=%d, lowId=%d",m_CastItem->GetGUIDHigh(), m_CastItem->GetGUIDLow());
- return;
- }
-
- bool expendable = false;
- bool withoutCharges = false;
-
- for (int i = 0; i<5; i++)
- {
- if (proto->Spells[i].SpellId)
- {
- // item has limited charges
- if (proto->Spells[i].SpellCharges)
- {
- if (proto->Spells[i].SpellCharges < 0)
- expendable = true;
-
- int32 charges = m_CastItem->GetSpellCharges(i);
-
- // item has charges left
- if (charges)
- {
- (charges > 0) ? --charges : ++charges; // abs(charges) less at 1 after use
- if (proto->Stackable < 2)
- m_CastItem->SetSpellCharges(i, charges);
- m_CastItem->SetState(ITEM_CHANGED, (Player*)m_caster);
- }
-
- // all charges used
- withoutCharges = (charges == 0);
- }
- }
- }
-
- if (expendable && withoutCharges)
- {
- uint32 count = 1;
- ((Player*)m_caster)->DestroyItemCount(m_CastItem, count, true);
-
- // prevent crash at access to deleted m_targets.getItemTarget
- if(m_CastItem==m_targets.getItemTarget())
- m_targets.setItemTarget(NULL);
-
- m_CastItem = NULL;
- }
-}
-
-void Spell::TakePower()
-{
- if(m_CastItem || m_triggeredByAuraSpell)
- return;
-
- // health as power used
- if(m_spellInfo->powerType == POWER_HEALTH)
- {
- m_caster->ModifyHealth( -(int32)m_powerCost );
- return;
- }
-
- if(m_spellInfo->powerType >= MAX_POWERS)
- {
- sLog.outError("Spell::TakePower: Unknown power type '%d'", m_spellInfo->powerType);
- return;
- }
-
- Powers powerType = Powers(m_spellInfo->powerType);
-
- m_caster->ModifyPower(powerType, -(int32)m_powerCost);
-
- // Set the five second timer
- if (powerType == POWER_MANA && m_powerCost > 0)
- m_caster->SetLastManaUse(getMSTime());
-}
-
-void Spell::TakeReagents()
-{
- if(m_IsTriggeredSpell) // reagents used in triggered spell removed by original spell or don't must be removed.
- return;
-
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if (m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP &&
- m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))
- return;
-
- Player* p_caster = (Player*)m_caster;
-
- for(uint32 x=0;x<8;x++)
- {
- if(m_spellInfo->Reagent[x] <= 0)
- continue;
-
- uint32 itemid = m_spellInfo->Reagent[x];
- uint32 itemcount = m_spellInfo->ReagentCount[x];
-
- // if CastItem is also spell reagent
- if (m_CastItem)
- {
- ItemPrototype const *proto = m_CastItem->GetProto();
- if( proto && proto->ItemId == itemid )
- {
- for(int s=0;s<5;s++)
- {
- // CastItem will be used up and does not count as reagent
- int32 charges = m_CastItem->GetSpellCharges(s);
- if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2)
- {
- ++itemcount;
- break;
- }
- }
-
- m_CastItem = NULL;
- }
- }
-
- // if getItemTarget is also spell reagent
- if (m_targets.getItemTargetEntry()==itemid)
- m_targets.setItemTarget(NULL);
-
- p_caster->DestroyItemCount(itemid, itemcount, true);
- }
-}
-
-void Spell::HandleThreatSpells(uint32 spellId)
-{
- if(!m_targets.getUnitTarget() || !spellId)
- return;
-
- if(!m_targets.getUnitTarget()->CanHaveThreatList())
- return;
-
- SpellThreatEntry const *threatSpell = sSpellThreatStore.LookupEntry<SpellThreatEntry>(spellId);
- if(!threatSpell)
- return;
-
- m_targets.getUnitTarget()->AddThreat(m_caster, float(threatSpell->threat));
-
- DEBUG_LOG("Spell %u, rank %u, added an additional %i threat", spellId, spellmgr.GetSpellRank(spellId), threatSpell->threat);
-}
-
-void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i, float DamageMultiplier)
-{
- unitTarget = pUnitTarget;
- itemTarget = pItemTarget;
- gameObjTarget = pGOTarget;
-
- uint8 eff = m_spellInfo->Effect[i];
- uint32 mechanic = m_spellInfo->EffectMechanic[i];
-
- damage = int32(CalculateDamage((uint8)i,unitTarget)*DamageMultiplier);
-
- sLog.outDebug( "Spell: Effect : %u", eff);
-
- //Simply return. Do not display "immune" in red text on client
- if(unitTarget && unitTarget->IsImmunedToSpellEffect(eff, mechanic))
- return;
-
- if(eff<TOTAL_SPELL_EFFECTS)
- {
- //sLog.outDebug( "WORLD: Spell FX %d < TOTAL_SPELL_EFFECTS ", eff);
- (*this.*SpellEffects[eff])(i);
- }
- /*
- else
- {
- sLog.outDebug( "WORLD: Spell FX %d > TOTAL_SPELL_EFFECTS ", eff);
- if (m_CastItem)
- EffectEnchantItemTmp(i);
- else
- {
- sLog.outError("SPELL: unknown effect %u spell id %u\n",
- eff, m_spellInfo->Id);
- }
- }
- */
-}
-
-void Spell::TriggerSpell()
-{
- for(TriggerSpells::iterator si=m_TriggerSpells.begin(); si!=m_TriggerSpells.end(); ++si)
- {
- Spell* spell = new Spell(m_caster, (*si), true, m_originalCasterGUID, this->m_selfContainer);
- spell->prepare(&m_targets); // use original spell original targets
- }
-}
-
-uint8 Spell::CanCast(bool strict)
-{
- // check cooldowns to prevent cheating
- if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id))
- {
- if(m_triggeredByAuraSpell)
- return SPELL_FAILED_DONT_REPORT;
- else
- return SPELL_FAILED_NOT_READY;
- }
-
- // only allow triggered spells if at an ended battleground
- if( !m_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER)
- if(BattleGround * bg = ((Player*)m_caster)->GetBattleGround())
- if(bg->GetStatus() == STATUS_WAIT_LEAVE)
- return SPELL_FAILED_DONT_REPORT;
-
- // only check at first call, Stealth auras are already removed at second call
- // for now, ignore triggered spells
- if( strict && !m_IsTriggeredSpell)
- {
- // Cannot be used in this stance/form
- if(uint8 shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form))
- return shapeError;
-
- if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura()))
- return SPELL_FAILED_ONLY_STEALTHED;
- }
-
- // caster state requirements
- if(m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState)))
- return SPELL_FAILED_CASTER_AURASTATE;
- if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot)))
- return SPELL_FAILED_CASTER_AURASTATE;
-
- // cancel autorepeat spells if cast start when moving
- // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code)
- if( m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->isMoving() )
- {
- // skip stuck spell to allow use it in falling case and apply spell limitations at movement
- if( (!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING) || m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK) &&
- (IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0) )
- return SPELL_FAILED_MOVING;
- }
-
- Unit *target = m_targets.getUnitTarget();
-
- if(target)
- {
- // target state requirements (not allowed state), apply to self also
- if(m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot)))
- return SPELL_FAILED_TARGET_AURASTATE;
-
-
- if(target != m_caster)
- {
- // target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds
- if(m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState)))
- return SPELL_FAILED_TARGET_AURASTATE;
-
- // Not allow casting on flying player
- if (target->isInFlight())
- return SPELL_FAILED_BAD_TARGETS;
-
- if(VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target))
- return SPELL_FAILED_LINE_OF_SIGHT;
-
- // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode
- // this case can be triggered if rank not found (too low-level target for first rank)
- if(m_caster->GetTypeId() == TYPEID_PLAYER && !IsPassiveSpell(m_spellInfo->Id) && !m_CastItem)
- {
- for(int i=0;i<3;i++)
- {
- if(IsPositiveEffect(m_spellInfo->Id, i) && m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA)
- if(target->getLevel() + 10 < m_spellInfo->spellLevel)
- return SPELL_FAILED_LOWLEVEL;
- }
- }
- }
-
- // check pet presents
- for(int j=0;j<3;j++)
- {
- if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_PET)
- {
- target = m_caster->GetPet();
- if(!target)
- {
- if(m_triggeredByAuraSpell) // not report pet not existence for triggered spells
- return SPELL_FAILED_DONT_REPORT;
- else
- return SPELL_FAILED_NO_PET;
- }
- break;
- }
- }
-
- //check creature type
- //ignore self casts (including area casts when caster selected as target)
- if(target != m_caster)
- {
- if(!CheckTargetCreatureType(target))
- {
- if(target->GetTypeId()==TYPEID_PLAYER)
- return SPELL_FAILED_TARGET_IS_PLAYER;
- else
- return SPELL_FAILED_BAD_TARGETS;
- }
- }
-
- // TODO: this check can be applied and for player to prevent cheating when IsPositiveSpell will return always correct result.
- // check target for pet/charmed casts (not self targeted), self targeted cast used for area effects and etc
- if(m_caster != target && m_caster->GetTypeId()==TYPEID_UNIT && m_caster->GetCharmerOrOwnerGUID())
- {
- // check correctness positive/negative cast target (pet cast real check and cheating check)
- if(IsPositiveSpell(m_spellInfo->Id))
- {
- if(m_caster->IsHostileTo(target))
- return SPELL_FAILED_BAD_TARGETS;
- }
- else
- {
- if(m_caster->IsFriendlyTo(target))
- return SPELL_FAILED_BAD_TARGETS;
- }
- }
-
- if(IsPositiveSpell(m_spellInfo->Id))
- {
- if(target->IsImmunedToSpell(m_spellInfo,false))
- return SPELL_FAILED_TARGET_AURASTATE;
- }
-
- //Must be behind the target.
- if( m_spellInfo->AttributesEx2 == 0x100000 && (m_spellInfo->AttributesEx & 0x200) == 0x200 && target->HasInArc(M_PI, m_caster) )
- {
- SendInterrupted(2);
- return SPELL_FAILED_NOT_BEHIND;
- }
-
- //Target must be facing you.
- if((m_spellInfo->Attributes == 0x150010) && !target->HasInArc(M_PI, m_caster) )
- {
- SendInterrupted(2);
- return SPELL_FAILED_NOT_INFRONT;
- }
-
- // check if target is in combat
- if (target != m_caster && (m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET) && target->isInCombat())
- {
- return SPELL_FAILED_TARGET_AFFECTING_COMBAT;
- }
- }
- // Spell casted only on battleground
- if((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND) && m_caster->GetTypeId()==TYPEID_PLAYER)
- if(!((Player*)m_caster)->InBattleGround())
- return SPELL_FAILED_ONLY_BATTLEGROUNDS;
-
- // do not allow spells to be cast in arenas
- // - with greater than 15 min CD without SPELL_ATTR_EX4_USABLE_IN_ARENA flag
- // - with SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA flag
- if( (m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) ||
- GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * 1000 && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) )
- if(MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId()))
- if(mapEntry->IsBattleArena())
- return SPELL_FAILED_NOT_IN_ARENA;
-
- // zone check
- if(!IsSpellAllowedInLocation(m_spellInfo,m_caster->GetMapId(),m_caster->GetZoneId(),m_caster->GetAreaId()))
- return SPELL_FAILED_REQUIRES_AREA;
-
- // not let players cast spells at mount (and let do it to creatures)
- if( m_caster->IsMounted() && m_caster->GetTypeId()==TYPEID_PLAYER && !m_IsTriggeredSpell &&
- !IsPassiveSpell(m_spellInfo->Id) && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_MOUNTED) )
- {
- if(m_caster->isInFlight())
- return SPELL_FAILED_NOT_FLYING;
- else
- return SPELL_FAILED_NOT_MOUNTED;
- }
-
- // always (except passive spells) check items (focus object can be required for any type casts)
- if(!IsPassiveSpell(m_spellInfo->Id))
- if(uint8 castResult = CheckItems())
- return castResult;
-
- //ImpliciteTargetA-B = 38, If fact there is 0 Spell with ImpliciteTargetB=38
- if(m_UniqueTargetInfo.empty()) // skip second canCast apply (for delayed spells for example)
- {
- for(uint8 j = 0; j < 3; j++)
- {
- if( m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT ||
- m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT && m_spellInfo->EffectImplicitTargetA[j] != TARGET_SELF ||
- m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
- m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
- {
- bool okDoo = false;
-
- SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(m_spellInfo->Id);
- SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id);
- if(lower==upper)
- sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = TARGET_SCRIPT or TARGET_SCRIPT_COORDINATES, but does not have record in `spell_script_target`",m_spellInfo->Id);
-
- SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
- float range = GetSpellMaxRange(srange);
-
- Creature* creatureScriptTarget = NULL;
- GameObject* goScriptTarget = NULL;
-
- for(SpellScriptTarget::const_iterator i_spellST = lower; i_spellST != upper; ++i_spellST)
- {
- switch(i_spellST->second.type)
- {
- case SPELL_TARGET_TYPE_GAMEOBJECT:
- {
- GameObject* p_GameObject = NULL;
-
- if(i_spellST->second.targetEntry)
- {
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
-
- MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range);
- MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(p_GameObject,go_check);
-
- TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
-
- if(p_GameObject)
- {
- // remember found target and range, next attempt will find more near target with another entry
- creatureScriptTarget = NULL;
- goScriptTarget = p_GameObject;
- range = go_check.GetLastRange();
- }
- }
- else if( focusObject ) //Focus Object
- {
- float frange = m_caster->GetDistance(focusObject);
- if(range >= frange)
- {
- creatureScriptTarget = NULL;
- goScriptTarget = focusObject;
- range = frange;
- }
- }
- break;
- }
- case SPELL_TARGET_TYPE_CREATURE:
- case SPELL_TARGET_TYPE_DEAD:
- default:
- {
- Creature *p_Creature = NULL;
-
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate(); // Really don't know what is that???
-
- MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range);
- MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(p_Creature, u_check);
-
- TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, grid_creature_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
-
- if(p_Creature )
- {
- creatureScriptTarget = p_Creature;
- goScriptTarget = NULL;
- range = u_check.GetLastRange();
- }
- break;
- }
- }
- }
-
- if(creatureScriptTarget)
- {
- // store coordinates for TARGET_SCRIPT_COORDINATES
- if (m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
- m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
- {
- m_targets.setDestination(creatureScriptTarget->GetPositionX(),creatureScriptTarget->GetPositionY(),creatureScriptTarget->GetPositionZ());
-
- if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES && m_spellInfo->EffectImplicitTargetB[j] == 0 && m_spellInfo->Effect[j]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
- AddUnitTarget(creatureScriptTarget, j);
- }
- // store explicit target for TARGET_SCRIPT
- else
- AddUnitTarget(creatureScriptTarget, j);
- }
- else if(goScriptTarget)
- {
- // store coordinates for TARGET_SCRIPT_COORDINATES
- if (m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
- m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
- {
- m_targets.setDestination(goScriptTarget->GetPositionX(),goScriptTarget->GetPositionY(),goScriptTarget->GetPositionZ());
-
- if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES && m_spellInfo->EffectImplicitTargetB[j] == 0 && m_spellInfo->Effect[j]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
- AddGOTarget(goScriptTarget, j);
- }
- // store explicit target for TARGET_SCRIPT
- else
- AddGOTarget(goScriptTarget, j);
- }
- //Missing DB Entry or targets for this spellEffect.
- else
- {
- // not report target not existence for triggered spells
- if(m_triggeredByAuraSpell || m_IsTriggeredSpell)
- return SPELL_FAILED_DONT_REPORT;
- else
- return SPELL_FAILED_BAD_TARGETS;
- }
- }
- }
- }
-
- if(uint8 castResult = CheckRange(strict))
- return castResult;
-
- {
- if(uint8 castResult = CheckPower())
- return castResult;
- }
-
- if(!m_triggeredByAuraSpell) // triggered spell not affected by stun/etc
- if(uint8 castResult = CheckCasterAuras())
- return castResult;
-
- for (int i = 0; i < 3; i++)
- {
- // for effects of spells that have only one target
- switch(m_spellInfo->Effect[i])
- {
- case SPELL_EFFECT_DUMMY:
- {
- if(m_spellInfo->SpellIconID == 1648) // Execute
- {
- if(!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2)
- return SPELL_FAILED_BAD_TARGETS;
- }
- else if (m_spellInfo->Id == 51582) // Rocket Boots Engaged
- {
- if(m_caster->IsInWater())
- return SPELL_FAILED_ONLY_ABOVEWATER;
- }
- else if(m_spellInfo->SpellIconID==156) // Holy Shock
- {
- // spell different for friends and enemies
- // hart version required facing
- if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, target ))
- return SPELL_FAILED_UNIT_NOT_INFRONT;
- }
- break;
- }
- case SPELL_EFFECT_SCHOOL_DAMAGE:
- {
- // Hammer of Wrath
- if(m_spellInfo->SpellVisual == 7250)
- {
- if (!m_targets.getUnitTarget())
- return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
-
- if(m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2)
- return SPELL_FAILED_BAD_TARGETS;
- }
- break;
- }
- case SPELL_EFFECT_TAMECREATURE:
- {
- if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER)
- return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
-
- if (m_targets.getUnitTarget()->getLevel() > m_caster->getLevel())
- return SPELL_FAILED_HIGHLEVEL;
-
- CreatureInfo const *cinfo = ((Creature*)m_targets.getUnitTarget())->GetCreatureInfo();
- if( cinfo->type != CREATURE_TYPE_BEAST )
- return SPELL_FAILED_BAD_TARGETS;
-
- // use SMSG_PET_TAME_FAILURE?
- if( !(cinfo->flag1 & 1) || !(cinfo->family) )
- return SPELL_FAILED_BAD_TARGETS;
-
- if(m_caster->GetPetGUID())
- return SPELL_FAILED_ALREADY_HAVE_SUMMON;
-
- if(m_caster->GetCharmGUID())
- return SPELL_FAILED_ALREADY_HAVE_CHARM;
-
- break;
- }
- case SPELL_EFFECT_LEARN_SPELL:
- {
- if(m_spellInfo->EffectImplicitTargetA[i] != TARGET_PET)
- break;
-
- Pet* pet = m_caster->GetPet();
-
- if(!pet)
- return SPELL_FAILED_NO_PET;
-
- SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(m_spellInfo->EffectTriggerSpell[i]);
-
- if(!learn_spellproto)
- return SPELL_FAILED_NOT_KNOWN;
-
- if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id))
- return SPELL_FAILED_TOO_MANY_SKILLS;
-
- if(m_spellInfo->spellLevel > pet->getLevel())
- return SPELL_FAILED_LOWLEVEL;
-
- if(!pet->HasTPForSpell(learn_spellproto->Id))
- return SPELL_FAILED_TRAINING_POINTS;
-
- break;
- }
- case SPELL_EFFECT_LEARN_PET_SPELL:
- {
- Pet* pet = m_caster->GetPet();
-
- if(!pet)
- return SPELL_FAILED_NO_PET;
-
- SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(m_spellInfo->EffectTriggerSpell[i]);
-
- if(!learn_spellproto)
- return SPELL_FAILED_NOT_KNOWN;
-
- if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id))
- return SPELL_FAILED_TOO_MANY_SKILLS;
-
- if(m_spellInfo->spellLevel > pet->getLevel())
- return SPELL_FAILED_LOWLEVEL;
-
- if(!pet->HasTPForSpell(learn_spellproto->Id))
- return SPELL_FAILED_TRAINING_POINTS;
-
- break;
- }
- case SPELL_EFFECT_FEED_PET:
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getItemTarget() )
- return SPELL_FAILED_BAD_TARGETS;
-
- Pet* pet = m_caster->GetPet();
-
- if(!pet)
- return SPELL_FAILED_NO_PET;
-
- if(!pet->HaveInDiet(m_targets.getItemTarget()->GetProto()))
- return SPELL_FAILED_WRONG_PET_FOOD;
-
- if(!pet->GetCurrentFoodBenefitLevel(m_targets.getItemTarget()->GetProto()->ItemLevel))
- return SPELL_FAILED_FOOD_LOWLEVEL;
-
- if(m_caster->isInCombat() || pet->isInCombat())
- return SPELL_FAILED_AFFECTING_COMBAT;
-
- break;
- }
- case SPELL_EFFECT_POWER_BURN:
- case SPELL_EFFECT_POWER_DRAIN:
- {
- // Can be area effect, Check only for players and not check if target - caster (spell can have multiply drain/burn effects)
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- if(Unit* target = m_targets.getUnitTarget())
- if(target!=m_caster && target->getPowerType()!=m_spellInfo->EffectMiscValue[i])
- return SPELL_FAILED_BAD_TARGETS;
- break;
- }
- case SPELL_EFFECT_CHARGE:
- {
- if (m_caster->hasUnitState(UNIT_STAT_ROOT))
- return SPELL_FAILED_ROOTED;
-
- break;
- }
- case SPELL_EFFECT_SKINNING:
- {
- if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() != TYPEID_UNIT)
- return SPELL_FAILED_BAD_TARGETS;
-
- if( !(m_targets.getUnitTarget()->GetUInt32Value(UNIT_FIELD_FLAGS) & UNIT_FLAG_SKINNABLE) )
- return SPELL_FAILED_TARGET_UNSKINNABLE;
-
- Creature* creature = (Creature*)m_targets.getUnitTarget();
- if ( creature->GetCreatureType() != CREATURE_TYPE_CRITTER && ( !creature->lootForBody || !creature->loot.empty() ) )
- {
- return SPELL_FAILED_TARGET_NOT_LOOTED;
- }
-
- uint32 skill;
- if(creature->GetCreatureInfo()->flag1 & 256)
- skill = SKILL_HERBALISM; // special case
- else if(creature->GetCreatureInfo()->flag1 & 512)
- skill = SKILL_MINING; // special case
- else
- skill = SKILL_SKINNING; // normal case
-
- int32 skillValue = ((Player*)m_caster)->GetSkillValue(skill);
- int32 TargetLevel = m_targets.getUnitTarget()->getLevel();
- int32 ReqValue = (skillValue < 100 ? (TargetLevel-10)*10 : TargetLevel*5);
- if (ReqValue > skillValue)
- return SPELL_FAILED_LOW_CASTLEVEL;
-
- // chance for fail at orange skinning attempt
- if( (m_selfContainer && (*m_selfContainer) == this) &&
- skillValue < sWorld.GetConfigMaxSkillValue() &&
- (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue-25, skillValue+37) )
- return SPELL_FAILED_TRY_AGAIN;
-
- break;
- }
- case SPELL_EFFECT_OPEN_LOCK_ITEM:
- case SPELL_EFFECT_OPEN_LOCK:
- {
- if( m_spellInfo->EffectImplicitTargetA[i] != TARGET_GAMEOBJECT &&
- m_spellInfo->EffectImplicitTargetA[i] != TARGET_GAMEOBJECT_ITEM )
- break;
-
- if( m_caster->GetTypeId() != TYPEID_PLAYER // only players can open locks, gather etc.
- // we need a go target in case of TARGET_GAMEOBJECT
- || m_spellInfo->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT && !m_targets.getGOTarget()
- // we need a go target, or an openable item target in case of TARGET_GAMEOBJECT_ITEM
- || m_spellInfo->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT_ITEM && !m_targets.getGOTarget() &&
- (!m_targets.getItemTarget() || !m_targets.getItemTarget()->GetProto()->LockID || m_targets.getItemTarget()->GetOwner() != m_caster ) )
- return SPELL_FAILED_BAD_TARGETS;
-
- // In BattleGround players can use only flags and banners
- if( ((Player*)m_caster)->InBattleGround() &&
- !((Player*)m_caster)->isAllowUseBattleGroundObject() )
- return SPELL_FAILED_TRY_AGAIN;
-
- // get the lock entry
- LockEntry const *lockInfo = NULL;
- if (GameObject* go=m_targets.getGOTarget())
- lockInfo = sLockStore.LookupEntry(go->GetLockId());
- else if(Item* itm=m_targets.getItemTarget())
- lockInfo = sLockStore.LookupEntry(itm->GetProto()->LockID);
-
- // check lock compatibility
- if (lockInfo)
- {
- // check for lock - key pair (checked by client also, just prevent cheating
- bool ok_key = false;
- for(int it = 0; it < 5; ++it)
- {
- switch(lockInfo->keytype[it])
- {
- case LOCK_KEY_NONE:
- break;
- case LOCK_KEY_ITEM:
- {
- if(lockInfo->key[it])
- {
- if(m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it])
- ok_key =true;
- break;
- }
- }
- case LOCK_KEY_SKILL:
- {
- if(uint32(m_spellInfo->EffectMiscValue[i])!=lockInfo->key[it])
- break;
-
- switch(lockInfo->key[it])
- {
- case LOCKTYPE_HERBALISM:
- if(((Player*)m_caster)->HasSkill(SKILL_HERBALISM))
- ok_key =true;
- break;
- case LOCKTYPE_MINING:
- if(((Player*)m_caster)->HasSkill(SKILL_MINING))
- ok_key =true;
- break;
- default:
- ok_key =true;
- break;
- }
- }
- }
- if(ok_key)
- break;
- }
-
- if(!ok_key)
- return SPELL_FAILED_BAD_TARGETS;
- }
-
- // chance for fail at orange mining/herb/LockPicking gathering attempt
- if (!m_selfContainer || ((*m_selfContainer) != this))
- break;
-
- // get the skill value of the player
- int32 SkillValue = 0;
- bool canFailAtMax = true;
- if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_HERBALISM)
- {
- SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_HERBALISM);
- canFailAtMax = false;
- }
- else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_MINING)
- {
- SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_MINING);
- canFailAtMax = false;
- }
- else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK)
- SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_LOCKPICKING);
-
- // castitem check: rogue using skeleton keys. the skill values should not be added in this case.
- if(m_CastItem)
- SkillValue = 0;
-
- // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value)
- SkillValue += m_currentBasePoints[i]+1;
-
- // get the required lock value
- int32 ReqValue=0;
- if (lockInfo)
- {
- // check for lock - key pair
- bool ok = false;
- for(int it = 0; it < 5; ++it)
- {
- if(lockInfo->keytype[it]==LOCK_KEY_ITEM && lockInfo->key[it] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it])
- {
- // if so, we're good to go
- ok = true;
- break;
- }
- }
- if(ok)
- break;
-
- if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK)
- ReqValue = lockInfo->requiredlockskill;
- else
- ReqValue = lockInfo->requiredminingskill;
- }
-
- // skill doesn't meet the required value
- if (ReqValue > SkillValue)
- return SPELL_FAILED_LOW_CASTLEVEL;
-
- // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill)
- if((canFailAtMax || SkillValue < sWorld.GetConfigMaxSkillValue()) && ReqValue > irand(SkillValue-25, SkillValue+37))
- return SPELL_FAILED_TRY_AGAIN;
-
- break;
- }
- case SPELL_EFFECT_SUMMON_DEAD_PET:
- {
- Creature *pet = m_caster->GetPet();
- if(!pet)
- return SPELL_FAILED_NO_PET;
-
- if(pet->isAlive())
- return SPELL_FAILED_ALREADY_HAVE_SUMMON;
-
- break;
- }
- // This is generic summon effect now and don't make this check for summon types similar
- // SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN.
- // These won't show up in m_caster->GetPetGUID()
- case SPELL_EFFECT_SUMMON:
- {
- switch(m_spellInfo->EffectMiscValueB[i])
- {
- case SUMMON_TYPE_POSESSED:
- case SUMMON_TYPE_POSESSED2:
- case SUMMON_TYPE_DEMON:
- case SUMMON_TYPE_SUMMON:
- {
- if(m_caster->GetPetGUID())
- return SPELL_FAILED_ALREADY_HAVE_SUMMON;
-
- if(m_caster->GetCharmGUID())
- return SPELL_FAILED_ALREADY_HAVE_CHARM;
- break;
- }
- }
- break;
- }
- // Don't make this check for SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN.
- // These won't show up in m_caster->GetPetGUID()
- case SPELL_EFFECT_SUMMON_POSSESSED:
- case SPELL_EFFECT_SUMMON_PHANTASM:
- case SPELL_EFFECT_SUMMON_DEMON:
- {
- if(m_caster->GetPetGUID())
- return SPELL_FAILED_ALREADY_HAVE_SUMMON;
-
- if(m_caster->GetCharmGUID())
- return SPELL_FAILED_ALREADY_HAVE_CHARM;
-
- break;
- }
- case SPELL_EFFECT_SUMMON_PET:
- {
- if(m_caster->GetPetGUID()) //let warlock do a replacement summon
- {
-
- Pet* pet = ((Player*)m_caster)->GetPet();
-
- if (m_caster->GetTypeId()==TYPEID_PLAYER && m_caster->getClass()==CLASS_WARLOCK)
- {
- if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player)
- pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID());
- }
- else
- return SPELL_FAILED_ALREADY_HAVE_SUMMON;
- }
-
- if(m_caster->GetCharmGUID())
- return SPELL_FAILED_ALREADY_HAVE_CHARM;
-
- break;
- }
- case SPELL_EFFECT_SUMMON_PLAYER:
- {
- if(m_caster->GetTypeId()!=TYPEID_PLAYER)
- return SPELL_FAILED_BAD_TARGETS;
- if(!((Player*)m_caster)->GetSelection())
- return SPELL_FAILED_BAD_TARGETS;
-
- Player* target = objmgr.GetPlayer(((Player*)m_caster)->GetSelection());
- if( !target || ((Player*)m_caster)==target || !target->IsInSameRaidWith((Player*)m_caster) )
- return SPELL_FAILED_BAD_TARGETS;
-
- // check if our map is dungeon
- if( sMapStore.LookupEntry(m_caster->GetMapId())->IsDungeon() )
- {
- InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(m_caster->GetMapId());
- if(!instance)
- return SPELL_FAILED_TARGET_NOT_IN_INSTANCE;
- if ( instance->levelMin > target->getLevel() )
- return SPELL_FAILED_LOWLEVEL;
- if ( instance->levelMax && instance->levelMax < target->getLevel() )
- return SPELL_FAILED_HIGHLEVEL;
- }
- break;
- }
- case SPELL_EFFECT_LEAP:
- case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER:
- {
- float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
- float fx = m_caster->GetPositionX() + dis * cos(m_caster->GetOrientation());
- float fy = m_caster->GetPositionY() + dis * sin(m_caster->GetOrientation());
- // teleport a bit above terrain level to avoid falling below it
- float fz = MapManager::Instance().GetBaseMap(m_caster->GetMapId())->GetHeight(fx,fy,m_caster->GetPositionZ(),true);
- if(fz <= INVALID_HEIGHT) // note: this also will prevent use effect in instances without vmaps height enabled
- return SPELL_FAILED_TRY_AGAIN;
-
- float caster_pos_z = m_caster->GetPositionZ();
- // Control the caster to not climb or drop when +-fz > 8
- if(!(fz<=caster_pos_z+8 && fz>=caster_pos_z-8))
- return SPELL_FAILED_TRY_AGAIN;
-
- // not allow use this effect at battleground until battleground start
- if(m_caster->GetTypeId()==TYPEID_PLAYER)
- if(BattleGround const *bg = ((Player*)m_caster)->GetBattleGround())
- if(bg->GetStatus() != STATUS_IN_PROGRESS)
- return SPELL_FAILED_TRY_AGAIN;
- break;
- }
- case SPELL_EFFECT_STEAL_BENEFICIAL_BUFF:
- {
- if (m_targets.getUnitTarget()==m_caster)
- return SPELL_FAILED_BAD_TARGETS;
- break;
- }
- default:break;
- }
- }
-
- for (int i = 0; i < 3; i++)
- {
- switch(m_spellInfo->EffectApplyAuraName[i])
- {
- case SPELL_AURA_MOD_POSSESS:
- case SPELL_AURA_MOD_CHARM:
- {
- if(m_caster->GetPetGUID())
- return SPELL_FAILED_ALREADY_HAVE_SUMMON;
-
- if(m_caster->GetCharmGUID())
- return SPELL_FAILED_ALREADY_HAVE_CHARM;
-
- if(m_caster->GetCharmerGUID())
- return SPELL_FAILED_CHARMED;
-
- if(!m_targets.getUnitTarget())
- return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
-
- if(m_targets.getUnitTarget()->GetCharmerGUID())
- return SPELL_FAILED_CHARMED;
-
- if(int32(m_targets.getUnitTarget()->getLevel()) > CalculateDamage(i,m_targets.getUnitTarget()))
- return SPELL_FAILED_HIGHLEVEL;
- };break;
- case SPELL_AURA_MOUNTED:
- {
- if (m_caster->IsInWater())
- return SPELL_FAILED_ONLY_ABOVEWATER;
-
- if (m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->GetTransport())
- return SPELL_FAILED_NO_MOUNTS_ALLOWED;
-
- // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells
- if (m_caster->GetTypeId()==TYPEID_PLAYER && !sMapStore.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell && !m_spellInfo->AreaId)
- return SPELL_FAILED_NO_MOUNTS_ALLOWED;
-
- if (m_caster->GetAreaId()==35)
- return SPELL_FAILED_NO_MOUNTS_ALLOWED;
-
- ShapeshiftForm form = m_caster->m_form;
- if( form == FORM_CAT || form == FORM_TREE || form == FORM_TRAVEL ||
- form == FORM_AQUA || form == FORM_BEAR || form == FORM_DIREBEAR ||
- form == FORM_CREATUREBEAR || form == FORM_GHOSTWOLF || form == FORM_FLIGHT ||
- form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN )
- return SPELL_FAILED_NOT_SHAPESHIFT;
-
- break;
- }
- case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS:
- {
- if(!m_targets.getUnitTarget())
- return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
-
- // can be casted at non-friendly unit or own pet/charm
- if(m_caster->IsFriendlyTo(m_targets.getUnitTarget()))
- return SPELL_FAILED_TARGET_FRIENDLY;
- };break;
- case SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED:
- case SPELL_AURA_FLY:
- {
- // not allow cast fly spells at old maps by players (all spells is self target)
- if(m_caster->GetTypeId()==TYPEID_PLAYER)
- {
- if( !((Player*)m_caster)->isGameMaster() &&
- GetVirtualMapForMapAndZone(m_caster->GetMapId(),m_caster->GetZoneId()) != 530)
- return SPELL_FAILED_NOT_HERE;
- }
- };break;
- case SPELL_AURA_PERIODIC_MANA_LEECH:
- {
- if (!m_targets.getUnitTarget())
- return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
-
- if (m_caster->GetTypeId()!=TYPEID_PLAYER || m_CastItem)
- break;
-
- if(m_targets.getUnitTarget()->getPowerType()!=POWER_MANA)
- return SPELL_FAILED_BAD_TARGETS;
- break;
- }
- default:break;
- }
- }
-
- // all ok
- return 0;
-}
-
-int16 Spell::PetCanCast(Unit* target)
-{
- if(!m_caster->isAlive())
- return SPELL_FAILED_CASTER_DEAD;
-
- if(m_caster->IsNonMeleeSpellCasted(false)) //prevent spellcast interuption by another spellcast
- return SPELL_FAILED_SPELL_IN_PROGRESS;
- if(m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo))
- return SPELL_FAILED_AFFECTING_COMBAT;
-
- if(m_caster->GetTypeId()==TYPEID_UNIT && (((Creature*)m_caster)->isPet() || m_caster->isCharmed()))
- {
- //dead owner (pets still alive when owners ressed?)
- if(m_caster->GetCharmerOrOwner() && !m_caster->GetCharmerOrOwner()->isAlive())
- return SPELL_FAILED_CASTER_DEAD;
-
- if(!target && m_targets.getUnitTarget())
- target = m_targets.getUnitTarget();
-
- bool need = false;
- for(uint32 i = 0;i<3;i++)
- {
- if(m_spellInfo->EffectImplicitTargetA[i] == TARGET_CHAIN_DAMAGE || m_spellInfo->EffectImplicitTargetA[i] == TARGET_SINGLE_FRIEND || m_spellInfo->EffectImplicitTargetA[i] == TARGET_DUELVSPLAYER || m_spellInfo->EffectImplicitTargetA[i] == TARGET_SINGLE_PARTY || m_spellInfo->EffectImplicitTargetA[i] == TARGET_CURRENT_ENEMY_COORDINATES)
- {
- need = true;
- if(!target)
- return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
- break;
- }
- }
- if(need)
- m_targets.setUnitTarget(target);
-
- Unit* _target = m_targets.getUnitTarget();
-
- if(_target) //for target dead/target not valid
- {
- if(!_target->isAlive())
- return SPELL_FAILED_BAD_TARGETS;
-
- if(IsPositiveSpell(m_spellInfo->Id))
- {
- if(m_caster->IsHostileTo(_target))
- return SPELL_FAILED_BAD_TARGETS;
- }
- else
- {
- bool duelvsplayertar = false;
- for(int j=0;j<3;j++)
- {
- //TARGET_DUELVSPLAYER is positive AND negative
- duelvsplayertar |= (m_spellInfo->EffectImplicitTargetA[j] == TARGET_DUELVSPLAYER);
- }
- if(m_caster->IsFriendlyTo(target) && !duelvsplayertar)
- {
- return SPELL_FAILED_BAD_TARGETS;
- }
- }
- }
- //cooldown
- if(((Creature*)m_caster)->HasSpellCooldown(m_spellInfo->Id))
- return SPELL_FAILED_NOT_READY;
- }
-
- uint16 result = CanCast(true);
- if(result != 0)
- return result;
- else
- return -1; //this allows to check spell fail 0, in combat
-}
-
-uint8 Spell::CheckCasterAuras() const
-{
- // Flag drop spells totally immuned to caster auras
- // FIXME: find more nice check for all totally immuned spells
- // AttributesEx3 & 0x10000000?
- if(m_spellInfo->Id==23336 || m_spellInfo->Id==23334 || m_spellInfo->Id==34991)
- return 0;
-
- uint8 school_immune = 0;
- uint32 mechanic_immune = 0;
- uint32 dispel_immune = 0;
-
- //Check if the spell grants school or mechanic immunity.
- //We use bitmasks so the loop is done only once and not on every aura check below.
- if ( m_spellInfo->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY )
- {
- for(int i = 0;i < 3; i ++)
- {
- if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_SCHOOL_IMMUNITY)
- school_immune |= uint32(m_spellInfo->EffectMiscValue[i]);
- else if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MECHANIC_IMMUNITY)
- mechanic_immune |= 1 << uint32(m_spellInfo->EffectMiscValue[i]);
- else if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_DISPEL_IMMUNITY)
- dispel_immune |= GetDispellMask(DispelType(m_spellInfo->EffectMiscValue[i]));
- }
- //immune movement impairement and loss of control
- if(m_spellInfo->Id==(uint32)42292)
- mechanic_immune = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
- }
-
- //Check whether the cast should be prevented by any state you might have.
- uint8 prevented_reason = 0;
- // Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out
- if(!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED) && m_caster->HasAuraType(SPELL_AURA_MOD_STUN))
- prevented_reason = SPELL_FAILED_STUNNED;
- else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED))
- prevented_reason = SPELL_FAILED_CONFUSED;
- else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED))
- prevented_reason = SPELL_FAILED_FLEEING;
- else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE)
- prevented_reason = SPELL_FAILED_SILENCED;
- else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY)
- prevented_reason = SPELL_FAILED_PACIFIED;
-
- // Attr must make flag drop spell totally immuned from all effects
- if(prevented_reason)
- {
- if(school_immune || mechanic_immune || dispel_immune)
- {
- //Checking auras is needed now, because you are prevented by some state but the spell grants immunity.
- Unit::AuraMap const& auras = m_caster->GetAuras();
- for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); itr++)
- {
- if(itr->second)
- {
- if( GetSpellMechanicMask(itr->second->GetSpellProto(), itr->second->GetEffIndex()) & mechanic_immune )
- continue;
- if( GetSpellSchoolMask(itr->second->GetSpellProto()) & school_immune )
- continue;
- if( (1<<(itr->second->GetSpellProto()->Dispel)) & dispel_immune)
- continue;
-
- //Make a second check for spell failed so the right SPELL_FAILED message is returned.
- //That is needed when your casting is prevented by multiple states and you are only immune to some of them.
- switch(itr->second->GetModifier()->m_auraname)
- {
- case SPELL_AURA_MOD_STUN:
- if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED))
- return SPELL_FAILED_STUNNED;
- break;
- case SPELL_AURA_MOD_CONFUSE:
- if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED))
- return SPELL_FAILED_CONFUSED;
- break;
- case SPELL_AURA_MOD_FEAR:
- if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED))
- return SPELL_FAILED_FLEEING;
- break;
- case SPELL_AURA_MOD_SILENCE:
- case SPELL_AURA_MOD_PACIFY:
- case SPELL_AURA_MOD_PACIFY_SILENCE:
- if( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY)
- return SPELL_FAILED_PACIFIED;
- else if ( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE)
- return SPELL_FAILED_SILENCED;
- break;
- }
- }
- }
- }
- //You are prevented from casting and the spell casted does not grant immunity. Return a failed error.
- else
- return prevented_reason;
- }
- return 0; // all ok
-}
-
-bool Spell::CanAutoCast(Unit* target)
-{
- uint64 targetguid = target->GetGUID();
-
- for(uint32 j = 0;j<3;j++)
- {
- if(m_spellInfo->Effect[j] == SPELL_EFFECT_APPLY_AURA)
- {
- if( m_spellInfo->StackAmount <= 1)
- {
- if( target->HasAura(m_spellInfo->Id, j) )
- return false;
- }
- else
- {
- if( target->GetAuras().count(Unit::spellEffectPair(m_spellInfo->Id, j)) >= m_spellInfo->StackAmount)
- return false;
- }
- }
- else if ( IsAreaAuraEffect( m_spellInfo->Effect[j] ))
- {
- if( target->HasAura(m_spellInfo->Id, j) )
- return false;
- }
- }
-
- int16 result = PetCanCast(target);
-
- if(result == -1 || result == SPELL_FAILED_UNIT_NOT_INFRONT)
- {
- FillTargetMap();
- //check if among target units, our WANTED target is as well (->only self cast spells return false)
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- if( ihit->targetGUID == targetguid )
- return true;
- }
- return false; //target invalid
-}
-
-uint8 Spell::CheckRange(bool strict)
-{
- float range_mod;
-
- // self cast doesn't need range checking -- also for Starshards fix
- if (m_spellInfo->rangeIndex == 1) return 0;
-
- if (strict) //add radius of caster
- range_mod = 1.25;
- else //add radius of caster and ~5 yds "give"
- range_mod = 6.25;
-
- SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
- float max_range = GetSpellMaxRange(srange) + range_mod;
- float min_range = GetSpellMinRange(srange);
-
- if(Player* modOwner = m_caster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this);
-
- Unit *target = m_targets.getUnitTarget();
-
- if(target && target != m_caster)
- {
- // distance from target center in checks
- float dist = m_caster->GetDistance(target->GetPositionX(),target->GetPositionY(),target->GetPositionZ());
- if(dist > max_range)
- return SPELL_FAILED_OUT_OF_RANGE; //0x5A;
- if(dist < min_range)
- return SPELL_FAILED_TOO_CLOSE;
- if( m_caster->GetTypeId() == TYPEID_PLAYER &&
- (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc( M_PI, target ) )
- return SPELL_FAILED_UNIT_NOT_INFRONT;
- }
-
- if(m_targets.m_targetMask == TARGET_FLAG_DEST_LOCATION && m_targets.m_destX != 0 && m_targets.m_destY != 0 && m_targets.m_destZ != 0)
- {
- float dist = m_caster->GetDistance(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ);
- if(dist > max_range)
- return SPELL_FAILED_OUT_OF_RANGE;
- if(dist < min_range)
- return SPELL_FAILED_TOO_CLOSE;
- }
-
- return 0; // ok
-}
-
-int32 Spell::CalculatePowerCost()
-{
- // item cast not used power
- if(m_CastItem)
- return 0;
-
- // Spell drain all exist power on cast (Only paladin lay of Hands)
- if (m_spellInfo->AttributesEx & SPELL_ATTR_EX_DRAIN_ALL_POWER)
- {
- // If power type - health drain all
- if (m_spellInfo->powerType == POWER_HEALTH)
- return m_caster->GetHealth();
- // Else drain all power
- if (m_spellInfo->powerType < MAX_POWERS)
- return m_caster->GetPower(Powers(m_spellInfo->powerType));
- sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", m_spellInfo->powerType, m_spellInfo->Id);
- return 0;
- }
-
- // Base powerCost
- int32 powerCost = m_spellInfo->manaCost;
- // PCT cost from total amount
- if (m_spellInfo->ManaCostPercentage)
- {
- switch (m_spellInfo->powerType)
- {
- // health as power used
- case POWER_HEALTH:
- powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetCreateHealth() / 100;
- break;
- case POWER_MANA:
- powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetCreateMana() / 100;
- break;
- case POWER_RAGE:
- case POWER_FOCUS:
- case POWER_ENERGY:
- case POWER_HAPPINESS:
- // case POWER_RUNES:
- powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetMaxPower(Powers(m_spellInfo->powerType)) / 100;
- break;
- default:
- sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", m_spellInfo->powerType, m_spellInfo->Id);
- return 0;
- }
- }
- SpellSchools school = GetFirstSchoolInMask(m_spellSchoolMask);
- // Flat mod from caster auras by spell school
- powerCost += m_caster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school);
- // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost)
- if ( m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_SPELL_VS_EXTEND_COST )
- powerCost += m_caster->GetAttackTime(OFF_ATTACK)/100;
- // Apply cost mod by spell
- if(Player* modOwner = m_caster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, powerCost, this);
-
- if(m_spellInfo->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION)
- powerCost = int32(powerCost/ (1.117f* m_spellInfo->spellLevel / m_caster->getLevel() -0.1327f));
-
- // PCT mod from user auras by school
- powerCost = int32(powerCost * (1.0f+m_caster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+school)));
- if (powerCost < 0)
- powerCost = 0;
- return powerCost;
-}
-
-uint8 Spell::CheckPower()
-{
- // item cast not used power
- if(m_CastItem)
- return 0;
-
- // health as power used - need check health amount
- if(m_spellInfo->powerType == POWER_HEALTH)
- {
- if(m_caster->GetHealth() <= m_powerCost)
- return SPELL_FAILED_CASTER_AURASTATE;
- return 0;
- }
- // Check valid power type
- if( m_spellInfo->powerType >= MAX_POWERS )
- {
- sLog.outError("Spell::CheckMana: Unknown power type '%d'", m_spellInfo->powerType);
- return SPELL_FAILED_UNKNOWN;
- }
- // Check power amount
- Powers powerType = Powers(m_spellInfo->powerType);
- if(m_caster->GetPower(powerType) < m_powerCost)
- return SPELL_FAILED_NO_POWER;
- else
- return 0;
-}
-
-uint8 Spell::CheckItems()
-{
- if (m_caster->GetTypeId() != TYPEID_PLAYER)
- return 0;
-
- uint32 itemid, itemcount;
- Player* p_caster = (Player*)m_caster;
-
- if(m_CastItem)
- {
- itemid = m_CastItem->GetEntry();
- if( !p_caster->HasItemCount(itemid,1) )
- return SPELL_FAILED_ITEM_NOT_READY;
- else
- {
- ItemPrototype const *proto = m_CastItem->GetProto();
- if(!proto)
- return SPELL_FAILED_ITEM_NOT_READY;
-
- for (int i = 0; i<5; i++)
- {
- if (proto->Spells[i].SpellCharges)
- {
- if(m_CastItem->GetSpellCharges(i)==0)
- return SPELL_FAILED_NO_CHARGES_REMAIN;
- }
- }
-
- uint32 ItemClass = proto->Class;
- if (ItemClass == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget())
- {
- for (int i = 0; i < 3; i++)
- {
- // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster
- if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET)
- continue;
-
- if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL)
- if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth())
- return (uint8)SPELL_FAILED_ALREADY_AT_FULL_HEALTH;
-
- // Mana Potion, Rage Potion, Thistle Tea(Rogue), ...
- if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE)
- {
- if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
- return (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER;
-
- Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
-
- if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power))
- return (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER;
- }
- }
- }
- }
- }
-
- if(m_targets.getItemTargetGUID())
- {
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return SPELL_FAILED_BAD_TARGETS;
-
- if(!m_targets.getItemTarget())
- return SPELL_FAILED_ITEM_GONE;
-
- if(!m_targets.getItemTarget()->IsFitToSpellRequirements(m_spellInfo))
- return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
- }
- // if not item target then required item must be equipped
- else
- {
- if(m_caster->GetTypeId() == TYPEID_PLAYER && !((Player*)m_caster)->HasItemFitToSpellReqirements(m_spellInfo))
- return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
- }
-
- if(m_spellInfo->RequiresSpellFocus)
- {
- CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
-
- GameObject* ok = NULL;
- MaNGOS::GameObjectFocusCheck go_check(m_caster,m_spellInfo->RequiresSpellFocus);
- MaNGOS::GameObjectSearcher<MaNGOS::GameObjectFocusCheck> checker(ok,go_check);
-
- TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectFocusCheck>, GridTypeMapContainer > object_checker(checker);
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
-
- if(!ok)
- return (uint8)SPELL_FAILED_REQUIRES_SPELL_FOCUS;
-
- focusObject = ok; // game object found in range
- }
-
- if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP &&
- m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION)))
- {
- for(uint32 i=0;i<8;i++)
- {
- if(m_spellInfo->Reagent[i] <= 0)
- continue;
-
- itemid = m_spellInfo->Reagent[i];
- itemcount = m_spellInfo->ReagentCount[i];
-
- // if CastItem is also spell reagent
- if( m_CastItem && m_CastItem->GetEntry() == itemid )
- {
- ItemPrototype const *proto = m_CastItem->GetProto();
- if(!proto)
- return SPELL_FAILED_ITEM_NOT_READY;
- for(int s=0;s<5;s++)
- {
- // CastItem will be used up and does not count as reagent
- int32 charges = m_CastItem->GetSpellCharges(s);
- if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2)
- {
- ++itemcount;
- break;
- }
- }
- }
- if( !p_caster->HasItemCount(itemid,itemcount) )
- return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54
- }
- }
-
- uint32 totems = 2;
- for(int i=0;i<2;++i)
- {
- if(m_spellInfo->Totem[i] != 0)
- {
- if( p_caster->HasItemCount(m_spellInfo->Totem[i],1) )
- {
- totems -= 1;
- continue;
- }
- }else
- totems -= 1;
- }
- if(totems != 0)
- return (uint8)SPELL_FAILED_TOTEMS; //0x7C
-
- //Check items for TotemCategory
- uint32 TotemCategory = 2;
- for(int i=0;i<2;++i)
- {
- if(m_spellInfo->TotemCategory[i] != 0)
- {
- if( p_caster->HasItemTotemCategory(m_spellInfo->TotemCategory[i]) )
- {
- TotemCategory -= 1;
- continue;
- }
- }
- else
- TotemCategory -= 1;
- }
- if(TotemCategory != 0)
- return (uint8)SPELL_FAILED_TOTEM_CATEGORY; //0x7B
-
- for(int i = 0; i < 3; i++)
- {
- switch (m_spellInfo->Effect[i])
- {
- case SPELL_EFFECT_CREATE_ITEM:
- {
- if (!m_IsTriggeredSpell && m_spellInfo->EffectItemType[i])
- {
- ItemPosCountVec dest;
- uint8 msg = p_caster->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->EffectItemType[i], 1 );
- if (msg != EQUIP_ERR_OK )
- {
- p_caster->SendEquipError( msg, NULL, NULL );
- return SPELL_FAILED_DONT_REPORT;
- }
- }
- break;
- }
- case SPELL_EFFECT_ENCHANT_ITEM:
- {
- Item* targetItem = m_targets.getItemTarget();
- if(!targetItem)
- return SPELL_FAILED_ITEM_NOT_FOUND;
-
- if( targetItem->GetProto()->ItemLevel < m_spellInfo->baseLevel )
- return SPELL_FAILED_LOWLEVEL;
- // Not allow enchant in trade slot for some enchant type
- if( targetItem->GetOwner() != m_caster )
- {
- uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
- SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!pEnchant)
- return SPELL_FAILED_ERROR;
- if (pEnchant->slot & ENCHANTMENT_CAN_SOULBOUND)
- return SPELL_FAILED_NOT_TRADEABLE;
- }
- break;
- }
- case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY:
- {
- Item *item = m_targets.getItemTarget();
- if(!item)
- return SPELL_FAILED_ITEM_NOT_FOUND;
- // Not allow enchant in trade slot for some enchant type
- if( item->GetOwner() != m_caster )
- {
- uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
- SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!pEnchant)
- return SPELL_FAILED_ERROR;
- if (pEnchant->slot & ENCHANTMENT_CAN_SOULBOUND)
- return SPELL_FAILED_NOT_TRADEABLE;
- }
- break;
- }
- case SPELL_EFFECT_ENCHANT_HELD_ITEM:
- // check item existence in effect code (not output errors at offhand hold item effect to main hand for example
- break;
- case SPELL_EFFECT_DISENCHANT:
- {
- if(!m_targets.getItemTarget())
- return SPELL_FAILED_CANT_BE_DISENCHANTED;
-
- // prevent disenchanting in trade slot
- if( m_targets.getItemTarget()->GetOwnerGUID() != m_caster->GetGUID() )
- return SPELL_FAILED_CANT_BE_DISENCHANTED;
-
- ItemPrototype const* itemProto = m_targets.getItemTarget()->GetProto();
- if(!itemProto)
- return SPELL_FAILED_CANT_BE_DISENCHANTED;
-
- uint32 item_quality = itemProto->Quality;
- // 2.0.x addon: Check player enchanting level agains the item desenchanting requirements
- uint32 item_disenchantskilllevel = itemProto->RequiredDisenchantSkill;
- if (item_disenchantskilllevel == uint32(-1))
- return SPELL_FAILED_CANT_BE_DISENCHANTED;
- if (item_disenchantskilllevel > p_caster->GetSkillValue(SKILL_ENCHANTING))
- return SPELL_FAILED_LOW_CASTLEVEL;
- if(item_quality > 4 || item_quality < 2)
- return SPELL_FAILED_CANT_BE_DISENCHANTED;
- if(itemProto->Class != ITEM_CLASS_WEAPON && itemProto->Class != ITEM_CLASS_ARMOR)
- return SPELL_FAILED_CANT_BE_DISENCHANTED;
- if (!itemProto->DisenchantID)
- return SPELL_FAILED_CANT_BE_DISENCHANTED;
- break;
- }
- case SPELL_EFFECT_PROSPECTING:
- {
- if(!m_targets.getItemTarget())
- return SPELL_FAILED_CANT_BE_PROSPECTED;
- //ensure item is a prospectable ore
- if(!(m_targets.getItemTarget()->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP) || m_targets.getItemTarget()->GetProto()->Class != ITEM_CLASS_TRADE_GOODS)
- return SPELL_FAILED_CANT_BE_PROSPECTED;
- //prevent prospecting in trade slot
- if( m_targets.getItemTarget()->GetOwnerGUID() != m_caster->GetGUID() )
- return SPELL_FAILED_CANT_BE_PROSPECTED;
- //Check for enough skill in jewelcrafting
- uint32 item_prospectingskilllevel = m_targets.getItemTarget()->GetProto()->RequiredSkillRank;
- if(item_prospectingskilllevel >p_caster->GetSkillValue(SKILL_JEWELCRAFTING))
- return SPELL_FAILED_LOW_CASTLEVEL;
- //make sure the player has the required ores in inventory
- if(m_targets.getItemTarget()->GetCount() < 5)
- return SPELL_FAILED_PROSPECT_NEED_MORE;
-
- if(!LootTemplates_Prospecting.HaveLootFor(m_targets.getItemTargetEntry()))
- return SPELL_FAILED_CANT_BE_PROSPECTED;
-
- break;
- }
- case SPELL_EFFECT_WEAPON_DAMAGE:
- case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
- {
- if(m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_TARGET_NOT_PLAYER;
- if( m_attackType != RANGED_ATTACK )
- break;
- Item *pItem = ((Player*)m_caster)->GetWeaponForAttack(m_attackType);
- if(!pItem || pItem->IsBroken())
- return SPELL_FAILED_EQUIPPED_ITEM;
-
- switch(pItem->GetProto()->SubClass)
- {
- case ITEM_SUBCLASS_WEAPON_THROWN:
- {
- uint32 ammo = pItem->GetEntry();
- if( !((Player*)m_caster)->HasItemCount( ammo, 1 ) )
- return SPELL_FAILED_NO_AMMO;
- }; break;
- case ITEM_SUBCLASS_WEAPON_GUN:
- case ITEM_SUBCLASS_WEAPON_BOW:
- case ITEM_SUBCLASS_WEAPON_CROSSBOW:
- {
- uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID);
- if(!ammo)
- {
- // Requires No Ammo
- if(m_caster->GetDummyAura(46699))
- break; // skip other checks
-
- return SPELL_FAILED_NO_AMMO;
- }
-
- ItemPrototype const *ammoProto = objmgr.GetItemPrototype( ammo );
- if(!ammoProto)
- return SPELL_FAILED_NO_AMMO;
-
- if(ammoProto->Class != ITEM_CLASS_PROJECTILE)
- return SPELL_FAILED_NO_AMMO;
-
- // check ammo ws. weapon compatibility
- switch(pItem->GetProto()->SubClass)
- {
- case ITEM_SUBCLASS_WEAPON_BOW:
- case ITEM_SUBCLASS_WEAPON_CROSSBOW:
- if(ammoProto->SubClass!=ITEM_SUBCLASS_ARROW)
- return SPELL_FAILED_NO_AMMO;
- break;
- case ITEM_SUBCLASS_WEAPON_GUN:
- if(ammoProto->SubClass!=ITEM_SUBCLASS_BULLET)
- return SPELL_FAILED_NO_AMMO;
- break;
- default:
- return SPELL_FAILED_NO_AMMO;
- }
-
- if( !((Player*)m_caster)->HasItemCount( ammo, 1 ) )
- return SPELL_FAILED_NO_AMMO;
- }; break;
- case ITEM_SUBCLASS_WEAPON_WAND:
- default:
- break;
- }
- break;
- }
- default:break;
- }
- }
-
- return uint8(0);
-}
-
-void Spell::Delayed()
-{
- if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if (m_spellState == SPELL_STATE_DELAYED)
- return; // spell is active and can't be time-backed
-
- // spells not loosing casting time ( slam, dynamites, bombs.. )
- if(!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE))
- return;
-
- //check resist chance
- int32 resistChance = 100; //must be initialized to 100 for percent modifiers
- ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this);
- resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100;
- if (roll_chance_i(resistChance))
- return;
-
- int32 delaytime = GetNextDelayAtDamageMsTime();
-
- if(int32(m_timer) + delaytime > m_casttime)
- {
- delaytime = m_casttime - m_timer;
- m_timer = m_casttime;
- }
- else
- m_timer += delaytime;
-
- sLog.outDetail("Spell %u partially interrupted for (%d) ms at damage",m_spellInfo->Id,delaytime);
-
- WorldPacket data(SMSG_SPELL_DELAYED, 8+4);
- data.append(m_caster->GetPackGUID());
- data << uint32(delaytime);
-
- m_caster->SendMessageToSet(&data,true);
-}
-
-void Spell::DelayedChannel()
-{
- if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING)
- return;
-
- //check resist chance
- int32 resistChance = 100; //must be initialized to 100 for percent modifiers
- ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this);
- resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100;
- if (roll_chance_i(resistChance))
- return;
-
- int32 delaytime = GetNextDelayAtDamageMsTime();
-
- if(int32(m_timer) < delaytime)
- {
- delaytime = m_timer;
- m_timer = 0;
- }
- else
- m_timer -= delaytime;
-
- sLog.outDebug("Spell %u partially interrupted for %i ms, new duration: %u ms", m_spellInfo->Id, delaytime, m_timer);
-
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- {
- if ((*ihit).missCondition == SPELL_MISS_NONE)
- {
- Unit* unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
- if (unit)
- {
- for (int j=0;j<3;j++)
- if( ihit->effectMask & (1<<j) )
- unit->DelayAura(m_spellInfo->Id, j, delaytime);
- }
-
- }
- }
-
- for(int j = 0; j < 3; j++)
- {
- // partially interrupt persistent area auras
- DynamicObject* dynObj = m_caster->GetDynObject(m_spellInfo->Id, j);
- if(dynObj)
- dynObj->Delay(delaytime);
- }
-
- SendChannelUpdate(m_timer);
-}
-
-void Spell::UpdatePointers()
-{
- if(m_originalCasterGUID==m_caster->GetGUID())
- m_originalCaster = m_caster;
- else
- {
- m_originalCaster = ObjectAccessor::GetUnit(*m_caster,m_originalCasterGUID);
- if(m_originalCaster && !m_originalCaster->IsInWorld()) m_originalCaster = NULL;
- }
-
- m_targets.Update(m_caster);
-}
-
-bool Spell::IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId)
-{
- return spellmgr.IsAffectedBySpell(m_spellInfo,spellInfo->Id,effectId,spellInfo->EffectItemType[effectId]);
-}
-
-bool Spell::CheckTargetCreatureType(Unit* target) const
-{
- uint32 spellCreatureTargetMask = m_spellInfo->TargetCreatureType;
-
- // Curse of Doom : not find another way to fix spell target check :/
- if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags == 0x0200000000LL)
- {
- // not allow cast at player
- if(target->GetTypeId()==TYPEID_PLAYER)
- return false;
-
- spellCreatureTargetMask = 0x7FF;
- }
-
- // Dismiss Pet and Taming Lesson skipped
- if(m_spellInfo->Id == 2641 || m_spellInfo->Id == 23356)
- spellCreatureTargetMask = 0;
-
- if (spellCreatureTargetMask)
- {
- uint32 TargetCreatureType = target->GetCreatureTypeMask();
-
- return !TargetCreatureType || (spellCreatureTargetMask & TargetCreatureType);
- }
- return true;
-}
-
-CurrentSpellTypes Spell::GetCurrentContainer()
-{
- if (IsNextMeleeSwingSpell())
- return(CURRENT_MELEE_SPELL);
- else if (IsAutoRepeat())
- return(CURRENT_AUTOREPEAT_SPELL);
- else if (IsChanneledSpell(m_spellInfo))
- return(CURRENT_CHANNELED_SPELL);
- else
- return(CURRENT_GENERIC_SPELL);
-}
-
-bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase )
-{
- // Check targets for creature type mask and remove not appropriate (skip explicit self target case, maybe need other explicit targets)
- if(m_spellInfo->EffectImplicitTargetA[eff]!=TARGET_SELF && !m_magnetPair.first)
- {
- if (!CheckTargetCreatureType(target))
- return false;
- }
-
- // Check targets for not_selectable unit flag and remove
- // A player can cast spells on his pet (or other controlled unit) though in any state
- if (target != m_caster && target->GetCharmerOrOwnerGUID() != m_caster->GetGUID())
- {
- // any unattackable target skipped
- if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
- return false;
-
- // unselectable targets skipped in all cases except TARGET_SCRIPT targeting
- // in case TARGET_SCRIPT target selected by server always and can't be cheated
- if( target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE) &&
- m_spellInfo->EffectImplicitTargetA[eff] != TARGET_SCRIPT &&
- m_spellInfo->EffectImplicitTargetB[eff] != TARGET_SCRIPT )
- return false;
- }
-
- //Check player targets and remove if in GM mode or GM invisibility (for not self casting case)
- if( target != m_caster && target->GetTypeId()==TYPEID_PLAYER)
- {
- if(((Player*)target)->GetVisibility()==VISIBILITY_OFF)
- return false;
-
- if(((Player*)target)->isGameMaster() && !IsPositiveSpell(m_spellInfo->Id))
- return false;
- }
-
- //Check targets for LOS visibility (except spells without range limitations )
- switch(m_spellInfo->Effect[eff])
- {
- case SPELL_EFFECT_SUMMON_PLAYER: // from anywhere
- break;
- case SPELL_EFFECT_DUMMY:
- if(m_spellInfo->Id!=20577) // Cannibalize
- break;
- //fall through
- case SPELL_EFFECT_RESURRECT_NEW:
- // player far away, maybe his corpse near?
- if(target!=m_caster && !target->IsWithinLOSInMap(m_caster))
- {
- if(!m_targets.getCorpseTargetGUID())
- return false;
-
- Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID());
- if(!corpse)
- return false;
-
- if(target->GetGUID()!=corpse->GetOwnerGUID())
- return false;
-
- if(!corpse->IsWithinLOSInMap(m_caster))
- return false;
- }
-
- // all ok by some way or another, skip normal check
- break;
- default: // normal case
- if(target!=m_caster && !target->IsWithinLOSInMap(m_caster))
- return false;
- break;
- }
-
- return true;
-}
-
-Unit* Spell::SelectMagnetTarget()
-{
- Unit* target = m_targets.getUnitTarget();
-
- if(target && target->HasAuraType(SPELL_AURA_SPELL_MAGNET) && !(m_spellInfo->Attributes & 0x10))
- {
- Unit::AuraList const& magnetAuras = target->GetAurasByType(SPELL_AURA_SPELL_MAGNET);
- for(Unit::AuraList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr)
- {
- if(Unit* magnet = (*itr)->GetCaster())
- {
- if((*itr)->m_procCharges>0 && magnet->IsWithinLOSInMap(m_caster))
- {
- (*itr)->SetAuraProcCharges((*itr)->m_procCharges-1);
- m_magnetPair.first = true;
- m_magnetPair.second = magnet;
-
- target = magnet;
- m_targets.setUnitTarget(target);
- break;
- }
- }
- }
- }
-
- return target;
-}
-
-bool Spell::IsNeedSendToClient() const
-{
- return m_spellInfo->SpellVisual!=0 || IsChanneledSpell(m_spellInfo) ||
- m_spellInfo->speed > 0.0f || !m_triggeredByAuraSpell && !m_IsTriggeredSpell;
-}
-
-bool Spell::HaveTargetsForEffect( uint8 effect ) const
-{
- for(std::list<TargetInfo>::const_iterator itr= m_UniqueTargetInfo.begin();itr != m_UniqueTargetInfo.end();++itr)
- if(itr->effectMask & (1<<effect))
- return true;
-
- for(std::list<GOTargetInfo>::const_iterator itr= m_UniqueGOTargetInfo.begin();itr != m_UniqueGOTargetInfo.end();++itr)
- if(itr->effectMask & (1<<effect))
- return true;
-
- for(std::list<ItemTargetInfo>::const_iterator itr= m_UniqueItemInfo.begin();itr != m_UniqueItemInfo.end();++itr)
- if(itr->effectMask & (1<<effect))
- return true;
-
- return false;
-}
-
-SpellEvent::SpellEvent(Spell* spell) : BasicEvent()
-{
- m_Spell = spell;
-}
-
-SpellEvent::~SpellEvent()
-{
- if (m_Spell->getState() != SPELL_STATE_FINISHED)
- m_Spell->cancel();
-
- if (m_Spell->IsDeletable())
- {
- delete m_Spell;
- }
- else
- {
- sLog.outError("~SpellEvent: %s %u tried to delete non-deletable spell %u. Was not deleted, causes memory leak.",
- (m_Spell->GetCaster()->GetTypeId()==TYPEID_PLAYER?"Player":"Creature"), m_Spell->GetCaster()->GetGUIDLow(),m_Spell->m_spellInfo->Id);
- }
-}
-
-bool SpellEvent::Execute(uint64 e_time, uint32 p_time)
-{
- // update spell if it is not finished
- if (m_Spell->getState() != SPELL_STATE_FINISHED)
- m_Spell->update(p_time);
-
- // check spell state to process
- switch (m_Spell->getState())
- {
- case SPELL_STATE_FINISHED:
- {
- // spell was finished, check deletable state
- if (m_Spell->IsDeletable())
- {
- // check, if we do have unfinished triggered spells
-
- return(true); // spell is deletable, finish event
- }
- // event will be re-added automatically at the end of routine)
- } break;
-
- case SPELL_STATE_CASTING:
- {
- // this spell is in channeled state, process it on the next update
- // event will be re-added automatically at the end of routine)
- } break;
-
- case SPELL_STATE_DELAYED:
- {
- // first, check, if we have just started
- if (m_Spell->GetDelayStart() != 0)
- {
- // no, we aren't, do the typical update
- // check, if we have channeled spell on our hands
- if (IsChanneledSpell(m_Spell->m_spellInfo))
- {
- // evented channeled spell is processed separately, casted once after delay, and not destroyed till finish
- // check, if we have casting anything else except this channeled spell and autorepeat
- if (m_Spell->GetCaster()->IsNonMeleeSpellCasted(false, true, true))
- {
- // another non-melee non-delayed spell is casted now, abort
- m_Spell->cancel();
- }
- else
- {
- // do the action (pass spell to channeling state)
- m_Spell->handle_immediate();
- }
- // event will be re-added automatically at the end of routine)
- }
- else
- {
- // run the spell handler and think about what we can do next
- uint64 t_offset = e_time - m_Spell->GetDelayStart();
- uint64 n_offset = m_Spell->handle_delayed(t_offset);
- if (n_offset)
- {
- // re-add us to the queue
- m_Spell->GetCaster()->m_Events.AddEvent(this, m_Spell->GetDelayStart() + n_offset, false);
- return(false); // event not complete
- }
- // event complete
- // finish update event will be re-added automatically at the end of routine)
- }
- }
- else
- {
- // delaying had just started, record the moment
- m_Spell->SetDelayStart(e_time);
- // re-plan the event for the delay moment
- m_Spell->GetCaster()->m_Events.AddEvent(this, e_time + m_Spell->GetDelayMoment(), false);
- return(false); // event not complete
- }
- } break;
-
- default:
- {
- // all other states
- // event will be re-added automatically at the end of routine)
- } break;
- }
-
- // spell processing not complete, plan event on the next update interval
- m_Spell->GetCaster()->m_Events.AddEvent(this, e_time + 1, false);
- return(false); // event not complete
-}
-
-void SpellEvent::Abort(uint64 /*e_time*/)
-{
- // oops, the spell we try to do is aborted
- if (m_Spell->getState() != SPELL_STATE_FINISHED)
- m_Spell->cancel();
-}
+/*
+ * 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 "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "UpdateMask.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Player.h"
+#include "Pet.h"
+#include "Unit.h"
+#include "Spell.h"
+#include "DynamicObject.h"
+#include "SpellAuras.h"
+#include "Group.h"
+#include "UpdateData.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "CellImpl.h"
+#include "Policies/SingletonImp.h"
+#include "SharedDefines.h"
+#include "Tools.h"
+#include "LootMgr.h"
+#include "VMapFactory.h"
+#include "BattleGround.h"
+#include "Util.h"
+
+#define SPELL_CHANNEL_UPDATE_INTERVAL 1000
+
+extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS];
+
+bool IsQuestTameSpell(uint32 spellId)
+{
+ SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
+ if (!spellproto) return false;
+
+ return spellproto->Effect[0] == SPELL_EFFECT_THREAT
+ && spellproto->Effect[1] == SPELL_EFFECT_APPLY_AURA && spellproto->EffectApplyAuraName[1] == SPELL_AURA_DUMMY;
+}
+
+SpellCastTargets::SpellCastTargets()
+{
+ m_unitTarget = NULL;
+ m_itemTarget = NULL;
+ m_GOTarget = NULL;
+
+ m_unitTargetGUID = 0;
+ m_GOTargetGUID = 0;
+ m_CorpseTargetGUID = 0;
+ m_itemTargetGUID = 0;
+ m_itemTargetEntry = 0;
+
+ m_srcX = m_srcY = m_srcZ = m_destX = m_destY = m_destZ = 0;
+ m_strTarget = "";
+ m_targetMask = 0;
+}
+
+SpellCastTargets::~SpellCastTargets()
+{
+}
+
+void SpellCastTargets::setUnitTarget(Unit *target)
+{
+ if (!target)
+ return;
+
+ m_destX = target->GetPositionX();
+ m_destY = target->GetPositionY();
+ m_destZ = target->GetPositionZ();
+ m_unitTarget = target;
+ m_unitTargetGUID = target->GetGUID();
+ m_targetMask |= TARGET_FLAG_UNIT;
+}
+
+void SpellCastTargets::setDestination(float x, float y, float z)
+{
+ m_destX = x;
+ m_destY = y;
+ m_destZ = z;
+ m_targetMask |= TARGET_FLAG_DEST_LOCATION;
+}
+
+void SpellCastTargets::setGOTarget(GameObject *target)
+{
+ m_GOTarget = target;
+ m_GOTargetGUID = target->GetGUID();
+ // m_targetMask |= TARGET_FLAG_OBJECT;
+}
+
+void SpellCastTargets::setItemTarget(Item* item)
+{
+ if(!item)
+ return;
+
+ m_itemTarget = item;
+ m_itemTargetGUID = item->GetGUID();
+ m_itemTargetEntry = item->GetEntry();
+ m_targetMask |= TARGET_FLAG_ITEM;
+}
+
+void SpellCastTargets::setCorpseTarget(Corpse* corpse)
+{
+ m_CorpseTargetGUID = corpse->GetGUID();
+}
+
+void SpellCastTargets::Update(Unit* caster)
+{
+ m_GOTarget = m_GOTargetGUID ? ObjectAccessor::GetGameObject(*caster,m_GOTargetGUID) : NULL;
+ m_unitTarget = m_unitTargetGUID ?
+ ( m_unitTargetGUID==caster->GetGUID() ? caster : ObjectAccessor::GetUnit(*caster, m_unitTargetGUID) ) :
+ NULL;
+
+ m_itemTarget = NULL;
+ if(caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(m_targetMask & TARGET_FLAG_ITEM)
+ m_itemTarget = ((Player*)caster)->GetItemByGuid(m_itemTargetGUID);
+ else
+ {
+ Player* pTrader = ((Player*)caster)->GetTrader();
+ if(pTrader && m_itemTargetGUID < TRADE_SLOT_COUNT)
+ m_itemTarget = pTrader->GetItemByPos(pTrader->GetItemPosByTradeSlot(m_itemTargetGUID));
+ }
+ if(m_itemTarget)
+ m_itemTargetEntry = m_itemTarget->GetEntry();
+ }
+}
+
+bool SpellCastTargets::read ( WorldPacket * data, Unit *caster )
+{
+ if(data->rpos()+4 > data->size())
+ return false;
+
+ *data >> m_targetMask;
+
+ if(m_targetMask == TARGET_FLAG_SELF)
+ {
+ m_destX = caster->GetPositionX();
+ m_destY = caster->GetPositionY();
+ m_destZ = caster->GetPositionZ();
+ m_unitTarget = caster;
+ m_unitTargetGUID = caster->GetGUID();
+ return true;
+ }
+ // TARGET_FLAG_UNK2 is used for non-combat pets, maybe other?
+ if( m_targetMask & (TARGET_FLAG_UNIT|TARGET_FLAG_UNK2) )
+ if(!readGUID(*data, m_unitTargetGUID))
+ return false;
+
+ if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK ))
+ if(!readGUID(*data, m_GOTargetGUID))
+ return false;
+
+ if(( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM )) && caster->GetTypeId() == TYPEID_PLAYER)
+ if(!readGUID(*data, m_itemTargetGUID))
+ return false;
+
+ if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
+ {
+ if(data->rpos()+4+4+4 > data->size())
+ return false;
+
+ *data >> m_srcX >> m_srcY >> m_srcZ;
+ if(!MaNGOS::IsValidMapCoord(m_srcX, m_srcY, m_srcZ))
+ return false;
+ }
+
+ if( m_targetMask & TARGET_FLAG_DEST_LOCATION )
+ {
+ if(data->rpos()+4+4+4 > data->size())
+ return false;
+
+ *data >> m_destX >> m_destY >> m_destZ;
+ if(!MaNGOS::IsValidMapCoord(m_destX, m_destY, m_destZ))
+ return false;
+ }
+
+ if( m_targetMask & TARGET_FLAG_STRING )
+ {
+ if(data->rpos()+1 > data->size())
+ return false;
+
+ *data >> m_strTarget;
+ }
+
+ if( m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) )
+ if(!readGUID(*data, m_CorpseTargetGUID))
+ return false;
+
+ // find real units/GOs
+ Update(caster);
+ return true;
+}
+
+void SpellCastTargets::write ( WorldPacket * data )
+{
+ *data << uint32(m_targetMask);
+
+ if( m_targetMask & ( TARGET_FLAG_UNIT | TARGET_FLAG_PVP_CORPSE | TARGET_FLAG_OBJECT | TARGET_FLAG_CORPSE | TARGET_FLAG_UNK2 ) )
+ {
+ if(m_targetMask & TARGET_FLAG_UNIT)
+ {
+ if(m_unitTarget)
+ data->append(m_unitTarget->GetPackGUID());
+ else
+ *data << uint8(0);
+ }
+ else if( m_targetMask & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK ) )
+ {
+ if(m_GOTarget)
+ data->append(m_GOTarget->GetPackGUID());
+ else
+ *data << uint8(0);
+ }
+ else if( m_targetMask & ( TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) )
+ data->appendPackGUID(m_CorpseTargetGUID);
+ else
+ *data << uint8(0);
+ }
+
+ if( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM ) )
+ {
+ if(m_itemTarget)
+ data->append(m_itemTarget->GetPackGUID());
+ else
+ *data << uint8(0);
+ }
+
+ if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
+ *data << m_srcX << m_srcY << m_srcZ;
+
+ if( m_targetMask & TARGET_FLAG_DEST_LOCATION )
+ *data << m_destX << m_destY << m_destZ;
+
+ if( m_targetMask & TARGET_FLAG_STRING )
+ *data << m_strTarget;
+}
+
+Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 originalCasterGUID, Spell** triggeringContainer )
+{
+ ASSERT( Caster != NULL && info != NULL );
+ ASSERT( info == sSpellStore.LookupEntry( info->Id ) && "`info` must be pointer to sSpellStore element");
+
+ m_spellInfo = info;
+ m_caster = Caster;
+ m_selfContainer = NULL;
+ m_triggeringContainer = triggeringContainer;
+ m_magnetPair.first = false;
+ m_magnetPair.second = NULL;
+ m_deletable = true;
+ m_delayAtDamageCount = 0;
+
+ m_applyMultiplierMask = 0;
+
+ // Get data for type of attack
+ switch (m_spellInfo->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_MELEE:
+ if (m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND)
+ m_attackType = OFF_ATTACK;
+ else
+ m_attackType = BASE_ATTACK;
+ break;
+ case SPELL_DAMAGE_CLASS_RANGED:
+ m_attackType = RANGED_ATTACK;
+ break;
+ default:
+ // Wands
+ if (m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_WAND)
+ m_attackType = RANGED_ATTACK;
+ else
+ m_attackType = BASE_ATTACK;
+ break;
+ }
+
+ m_spellSchoolMask = GetSpellSchoolMask(info); // Can be override for some spell (wand shoot for example)
+
+ if(m_attackType == RANGED_ATTACK)
+ {
+ // wand case
+ if((m_caster->getClassMask() & CLASSMASK_WAND_USERS) != 0 && m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(Item* pItem = ((Player*)m_caster)->GetWeaponForAttack(RANGED_ATTACK))
+ m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->Damage->DamageType);
+ }
+ }
+
+ if(originalCasterGUID)
+ m_originalCasterGUID = originalCasterGUID;
+ else
+ m_originalCasterGUID = m_caster->GetGUID();
+
+ if(m_originalCasterGUID==m_caster->GetGUID())
+ m_originalCaster = m_caster;
+ else
+ {
+ m_originalCaster = ObjectAccessor::GetUnit(*m_caster,m_originalCasterGUID);
+ if(m_originalCaster && !m_originalCaster->IsInWorld()) m_originalCaster = NULL;
+ }
+
+ for(int i=0; i <3; ++i)
+ m_currentBasePoints[i] = m_spellInfo->EffectBasePoints[i];
+
+ m_spellState = SPELL_STATE_NULL;
+
+ m_castPositionX = m_castPositionY = m_castPositionZ = 0;
+ m_TriggerSpells.clear();
+ m_IsTriggeredSpell = triggered;
+ //m_AreaAura = false;
+ m_CastItem = NULL;
+
+ unitTarget = NULL;
+ itemTarget = NULL;
+ gameObjTarget = NULL;
+ focusObject = NULL;
+ m_cast_count = 0;
+ m_triggeredByAuraSpell = NULL;
+
+ //Auto Shot & Shoot
+ if( m_spellInfo->AttributesEx2 == 0x000020 && !triggered )
+ m_autoRepeat = true;
+ else
+ m_autoRepeat = false;
+
+ m_powerCost = 0; // setup to correct value in Spell::prepare, don't must be used before.
+ m_casttime = 0; // setup to correct value in Spell::prepare, don't must be used before.
+ m_timer = 0; // will set to castime in preper
+
+ m_needAliveTargetMask = 0;
+
+ // determine reflection
+ m_canReflect = false;
+
+ if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && (m_spellInfo->AttributesEx2 & 0x4)==0)
+ {
+ for(int j=0;j<3;j++)
+ {
+ if (m_spellInfo->Effect[j]==0)
+ continue;
+
+ if(!IsPositiveTarget(m_spellInfo->EffectImplicitTargetA[j],m_spellInfo->EffectImplicitTargetB[j]))
+ m_canReflect = true;
+ else
+ m_canReflect = (m_spellInfo->AttributesEx & (1<<7)) ? true : false;
+
+ if(m_canReflect)
+ continue;
+ else
+ break;
+ }
+ }
+
+ CleanupTargetList();
+}
+
+Spell::~Spell()
+{
+}
+
+void Spell::FillTargetMap()
+{
+ // TODO: ADD the correct target FILLS!!!!!!
+
+ for(uint32 i=0;i<3;i++)
+ {
+ // not call for empty effect.
+ // Also some spells use not used effect targets for store targets for dummy effect in triggered spells
+ if(m_spellInfo->Effect[i]==0)
+ continue;
+
+ // targets for TARGET_SCRIPT_COORDINATES (A) and TARGET_SCRIPT filled in Spell::canCast call
+ if( m_spellInfo->EffectImplicitTargetA[i] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetA[i] == TARGET_SCRIPT ||
+ m_spellInfo->EffectImplicitTargetB[i] == TARGET_SCRIPT && m_spellInfo->EffectImplicitTargetA[i] != TARGET_SELF )
+ continue;
+
+ // TODO: find a way so this is not needed?
+ // for area auras always add caster as target (needed for totems for example)
+ if(IsAreaAuraEffect(m_spellInfo->Effect[i]))
+ AddUnitTarget(m_caster, i);
+
+ std::list<Unit*> tmpUnitMap;
+
+ // TargetA/TargetB dependent from each other, we not switch to full support this dependences
+ // but need it support in some know cases
+ switch(m_spellInfo->EffectImplicitTargetA[i])
+ {
+ case TARGET_ALL_AROUND_CASTER:
+ if( m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_PARTY ||
+ m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER ||
+ m_spellInfo->EffectImplicitTargetB[i]==TARGET_RANDOM_RAID_MEMBER )
+ {
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap);
+ }
+ // Note: this hack with search required until GO casting not implemented
+ // environment damage spells already have around enemies targeting but this not help in case not existed GO casting support
+ // currently each enemy selected explicitly and self cast damage
+ else if(m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_ENEMY_IN_AREA && m_spellInfo->Effect[i]==SPELL_EFFECT_ENVIRONMENTAL_DAMAGE)
+ {
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ }
+ else
+ {
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap);
+ }
+ break;
+ case TARGET_TABLE_X_Y_Z_COORDINATES:
+ // Only if target A, for target B (used in teleports) dest select in effect
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
+ break;
+ default:
+ switch(m_spellInfo->EffectImplicitTargetB[i])
+ {
+ case TARGET_SCRIPT_COORDINATES: // B case filled in canCast but we need fill unit list base at A case
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
+ break;
+ default:
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap);
+ SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap);
+ break;
+ }
+ break;
+ }
+
+ if( (m_spellInfo->EffectImplicitTargetA[i]==0 || m_spellInfo->EffectImplicitTargetA[i]==TARGET_EFFECT_SELECT) &&
+ (m_spellInfo->EffectImplicitTargetB[i]==0 || m_spellInfo->EffectImplicitTargetB[i]==TARGET_EFFECT_SELECT) )
+ {
+ // add here custom effects that need default target.
+ // FOR EVERY TARGET TYPE THERE IS A DIFFERENT FILL!!
+ switch(m_spellInfo->Effect[i])
+ {
+ case SPELL_EFFECT_DUMMY:
+ {
+ switch(m_spellInfo->Id)
+ {
+ case 20577: // Cannibalize
+ {
+ // non-standard target selection
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
+ float max_range = GetSpellMaxRange(srange);
+
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ WorldObject* result = NULL;
+
+ MaNGOS::CannibalizeObjectCheck u_check(m_caster, max_range);
+ MaNGOS::WorldObjectSearcher<MaNGOS::CannibalizeObjectCheck > searcher(result, u_check);
+
+ TypeContainerVisitor<MaNGOS::WorldObjectSearcher<MaNGOS::CannibalizeObjectCheck >, GridTypeMapContainer > grid_searcher(searcher);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+
+ if(!result)
+ {
+ TypeContainerVisitor<MaNGOS::WorldObjectSearcher<MaNGOS::CannibalizeObjectCheck >, WorldTypeMapContainer > world_searcher(searcher);
+ cell_lock->Visit(cell_lock, world_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+
+ if(result)
+ {
+ switch(result->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ case TYPEID_PLAYER:
+ tmpUnitMap.push_back((Unit*)result);
+ break;
+ case TYPEID_CORPSE:
+ m_targets.setCorpseTarget((Corpse*)result);
+ if(Player* owner = ObjectAccessor::FindPlayer(((Corpse*)result)->GetOwnerGUID()))
+ tmpUnitMap.push_back(owner);
+ break;
+ }
+ }
+ else
+ {
+ // clear cooldown at fail
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id);
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(m_spellInfo->Id);
+ data << uint64(m_caster->GetGUID());
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+
+ SendCastResult(SPELL_FAILED_NO_EDIBLE_CORPSES);
+ finish(false);
+ }
+ break;
+ }
+ default:
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ break;
+ }
+ break;
+ }
+ case SPELL_EFFECT_RESURRECT:
+ case SPELL_EFFECT_PARRY:
+ case SPELL_EFFECT_BLOCK:
+ case SPELL_EFFECT_CREATE_ITEM:
+ case SPELL_EFFECT_TRIGGER_SPELL:
+ case SPELL_EFFECT_TRIGGER_MISSILE:
+ case SPELL_EFFECT_LEARN_SPELL:
+ case SPELL_EFFECT_SKILL_STEP:
+ case SPELL_EFFECT_PROFICIENCY:
+ case SPELL_EFFECT_SUMMON_POSSESSED:
+ case SPELL_EFFECT_SUMMON_OBJECT_WILD:
+ case SPELL_EFFECT_SELF_RESURRECT:
+ case SPELL_EFFECT_REPUTATION:
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ break;
+ case SPELL_EFFECT_SUMMON_PLAYER:
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->GetSelection())
+ {
+ Player* target = objmgr.GetPlayer(((Player*)m_caster)->GetSelection());
+ if(target)
+ tmpUnitMap.push_back(target);
+ }
+ break;
+ case SPELL_EFFECT_RESURRECT_NEW:
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ if(m_targets.getCorpseTargetGUID())
+ {
+ Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID());
+ if(corpse)
+ {
+ Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGUID());
+ if(owner)
+ tmpUnitMap.push_back(owner);
+ }
+ }
+ break;
+ case SPELL_EFFECT_SUMMON:
+ if(m_spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED || m_spellInfo->EffectMiscValueB[i] == SUMMON_TYPE_POSESSED2)
+ {
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ }
+ else
+ tmpUnitMap.push_back(m_caster);
+ break;
+ case SPELL_EFFECT_SUMMON_CHANGE_ITEM:
+ case SPELL_EFFECT_SUMMON_WILD:
+ case SPELL_EFFECT_SUMMON_GUARDIAN:
+ case SPELL_EFFECT_TRANS_DOOR:
+ case SPELL_EFFECT_ADD_FARSIGHT:
+ case SPELL_EFFECT_STUCK:
+ case SPELL_EFFECT_DESTROY_ALL_TOTEMS:
+ case SPELL_EFFECT_SUMMON_DEMON:
+ case SPELL_EFFECT_SKILL:
+ tmpUnitMap.push_back(m_caster);
+ break;
+ case SPELL_EFFECT_LEARN_PET_SPELL:
+ if(Pet* pet = m_caster->GetPet())
+ tmpUnitMap.push_back(pet);
+ break;
+ case SPELL_EFFECT_ENCHANT_ITEM:
+ case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY:
+ case SPELL_EFFECT_DISENCHANT:
+ case SPELL_EFFECT_FEED_PET:
+ case SPELL_EFFECT_PROSPECTING:
+ if(m_targets.getItemTarget())
+ AddItemTarget(m_targets.getItemTarget(), i);
+ break;
+ case SPELL_EFFECT_APPLY_AURA:
+ switch(m_spellInfo->EffectApplyAuraName[i])
+ {
+ case SPELL_AURA_ADD_FLAT_MODIFIER: // some spell mods auras have 0 target modes instead expected TARGET_SELF(1) (and present for other ranks for same spell for example)
+ case SPELL_AURA_ADD_PCT_MODIFIER:
+ tmpUnitMap.push_back(m_caster);
+ break;
+ default: // apply to target in other case
+ if(m_targets.getUnitTarget())
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ break;
+ }
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_PARTY:
+ // AreaAura
+ if(m_spellInfo->Attributes == 0x9050000 || m_spellInfo->Attributes == 0x10000)
+ SetTargetMap(i,TARGET_AREAEFFECT_PARTY,tmpUnitMap);
+ break;
+ case SPELL_EFFECT_SKIN_PLAYER_CORPSE:
+ if(m_targets.getUnitTarget())
+ {
+ tmpUnitMap.push_back(m_targets.getUnitTarget());
+ }
+ else if (m_targets.getCorpseTargetGUID())
+ {
+ Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID());
+ if(corpse)
+ {
+ Player* owner = ObjectAccessor::FindPlayer(corpse->GetOwnerGUID());
+ if(owner)
+ tmpUnitMap.push_back(owner);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if(IsChanneledSpell(m_spellInfo) && !tmpUnitMap.empty())
+ m_needAliveTargetMask |= (1<<i);
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ Player *me = (Player*)m_caster;
+ for (std::list<Unit*>::const_iterator itr = tmpUnitMap.begin(); itr != tmpUnitMap.end(); itr++)
+ {
+ Unit *owner = (*itr)->GetOwner();
+ Unit *u = owner ? owner : (*itr);
+ if(u!=m_caster && u->IsPvP() && (!me->duel || me->duel->opponent != u))
+ {
+ me->UpdatePvP(true);
+ me->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
+ break;
+ }
+ }
+ }
+
+ for (std::list<Unit*>::iterator itr = tmpUnitMap.begin() ; itr != tmpUnitMap.end();)
+ {
+ if(!CheckTarget(*itr, i, false ))
+ {
+ itr = tmpUnitMap.erase(itr);
+ continue;
+ }
+ else
+ ++itr;
+ }
+
+ for(std::list<Unit*>::iterator iunit= tmpUnitMap.begin();iunit != tmpUnitMap.end();++iunit)
+ AddUnitTarget((*iunit), i);
+ }
+}
+
+void Spell::CleanupTargetList()
+{
+ m_UniqueTargetInfo.clear();
+ m_UniqueGOTargetInfo.clear();
+ m_UniqueItemInfo.clear();
+ m_countOfHit = 0;
+ m_countOfMiss = 0;
+ m_delayMoment = 0;
+}
+
+void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
+{
+ if( m_spellInfo->Effect[effIndex]==0 )
+ return;
+
+ uint64 targetGUID = pVictim->GetGUID();
+
+ // Lookup target in already in list
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if (targetGUID == ihit->targetGUID) // Found in list
+ {
+ ihit->effectMask |= 1<<effIndex; // Add only effect mask
+ return;
+ }
+ }
+
+ // This is new target calculate data for him
+
+ // Get spell hit result on target
+ TargetInfo target;
+ target.targetGUID = targetGUID; // Store target GUID
+ target.effectMask = 1<<effIndex; // Store index of effect
+ target.processed = false; // Effects not apply on target
+
+ // Calculate hit result
+ target.missCondition = m_caster->SpellHitResult(pVictim, m_spellInfo, m_canReflect);
+ if (target.missCondition == SPELL_MISS_NONE)
+ ++m_countOfHit;
+ else
+ ++m_countOfMiss;
+
+ // Spell have speed - need calculate incoming time
+ if (m_spellInfo->speed > 0.0f)
+ {
+ // calculate spell incoming interval
+ float dist = m_caster->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ());
+ if (dist < 5.0f) dist = 5.0f;
+ target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f);
+
+ // Calculate minimum incoming time
+ if (m_delayMoment==0 || m_delayMoment>target.timeDelay)
+ m_delayMoment = target.timeDelay;
+ }
+ else
+ target.timeDelay = 0LL;
+
+ // If target reflect spell back to caster
+ if (target.missCondition==SPELL_MISS_REFLECT)
+ {
+ // Calculate reflected spell result on caster
+ target.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, m_canReflect);
+
+ if (target.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell
+ target.reflectResult = SPELL_MISS_PARRY;
+
+ // Increase time interval for reflected spells by 1.5
+ target.timeDelay+=target.timeDelay>>1;
+ }
+ else
+ target.reflectResult = SPELL_MISS_NONE;
+
+ // Add target to list
+ m_UniqueTargetInfo.push_back(target);
+}
+
+void Spell::AddUnitTarget(uint64 unitGUID, uint32 effIndex)
+{
+ Unit* unit = m_caster->GetGUID()==unitGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, unitGUID);
+ if (unit)
+ AddUnitTarget(unit, effIndex);
+}
+
+void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex)
+{
+ if( m_spellInfo->Effect[effIndex]==0 )
+ return;
+
+ uint64 targetGUID = pVictim->GetGUID();
+
+ // Lookup target in already in list
+ for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit)
+ {
+ if (targetGUID == ihit->targetGUID) // Found in list
+ {
+ ihit->effectMask |= 1<<effIndex; // Add only effect mask
+ return;
+ }
+ }
+
+ // This is new target calculate data for him
+
+ GOTargetInfo target;
+ target.targetGUID = targetGUID;
+ target.effectMask = 1<<effIndex;
+ target.processed = false; // Effects not apply on target
+
+ // Spell have speed - need calculate incoming time
+ if (m_spellInfo->speed > 0.0f)
+ {
+ // calculate spell incoming interval
+ float dist = m_caster->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ());
+ if (dist < 5.0f) dist = 5.0f;
+ target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f);
+ if (m_delayMoment==0 || m_delayMoment>target.timeDelay)
+ m_delayMoment = target.timeDelay;
+ }
+ else
+ target.timeDelay = 0LL;
+
+ ++m_countOfHit;
+
+ // Add target to list
+ m_UniqueGOTargetInfo.push_back(target);
+}
+
+void Spell::AddGOTarget(uint64 goGUID, uint32 effIndex)
+{
+ GameObject* go = ObjectAccessor::GetGameObject(*m_caster, goGUID);
+ if (go)
+ AddGOTarget(go, effIndex);
+}
+
+void Spell::AddItemTarget(Item* pitem, uint32 effIndex)
+{
+ if( m_spellInfo->Effect[effIndex]==0 )
+ return;
+
+ // Lookup target in already in list
+ for(std::list<ItemTargetInfo>::iterator ihit= m_UniqueItemInfo.begin();ihit != m_UniqueItemInfo.end();++ihit)
+ {
+ if (pitem == ihit->item) // Found in list
+ {
+ ihit->effectMask |= 1<<effIndex; // Add only effect mask
+ return;
+ }
+ }
+
+ // This is new target add data
+
+ ItemTargetInfo target;
+ target.item = pitem;
+ target.effectMask = 1<<effIndex;
+ m_UniqueItemInfo.push_back(target);
+}
+
+void Spell::doTriggers(SpellMissInfo missInfo, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 block, uint32 absorb, bool crit)
+{
+ // Do triggers depends from hit result (triggers on hit do in effects)
+ // Set aura states depends from hit result
+ if (missInfo!=SPELL_MISS_NONE)
+ {
+ // Miss/dodge/parry/block only for melee based spells
+ // Resist only for magic based spells
+ switch (missInfo)
+ {
+ case SPELL_MISS_MISS:
+ if(m_caster->GetTypeId()== TYPEID_PLAYER)
+ ((Player*)m_caster)->UpdateWeaponSkill(BASE_ATTACK);
+
+ m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_MISS, m_spellInfo, m_IsTriggeredSpell);
+ break;
+ case SPELL_MISS_RESIST:
+ m_caster->ProcDamageAndSpell(unitTarget, PROC_FLAG_TARGET_RESISTS, PROC_FLAG_RESIST_SPELL, 0, damageSchoolMask, m_spellInfo, m_IsTriggeredSpell);
+ break;
+ case SPELL_MISS_DODGE:
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->UpdateDefense();
+
+ // Overpower
+ if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARRIOR)
+ {
+ ((Player*) m_caster)->AddComboPoints(unitTarget, 1);
+ m_caster->StartReactiveTimer( REACTIVE_OVERPOWER );
+ }
+
+ // Riposte
+ if (unitTarget->getClass() != CLASS_ROGUE)
+ {
+ unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+
+ m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_DODGE, m_spellInfo, m_IsTriggeredSpell);
+ break;
+ case SPELL_MISS_PARRY:
+ // Update victim defense ?
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->UpdateDefense();
+ // Mongoose bite - set only Counterattack here
+ if (unitTarget->getClass() == CLASS_HUNTER)
+ {
+ unitTarget->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true);
+ unitTarget->StartReactiveTimer( REACTIVE_HUNTER_PARRY );
+ }
+ else
+ {
+ unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+ m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_PARRY, m_spellInfo, m_IsTriggeredSpell);
+ break;
+ case SPELL_MISS_BLOCK:
+ unitTarget->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ unitTarget->StartReactiveTimer( REACTIVE_DEFENSE );
+
+ m_caster->CastMeleeProcDamageAndSpell(unitTarget, 0, damageSchoolMask, m_attackType, MELEE_HIT_BLOCK, m_spellInfo, m_IsTriggeredSpell);
+ break;
+ // Trigger from this events not supported
+ case SPELL_MISS_EVADE:
+ case SPELL_MISS_IMMUNE:
+ case SPELL_MISS_IMMUNE2:
+ case SPELL_MISS_DEFLECT:
+ case SPELL_MISS_ABSORB:
+ // Trigger from reflects need do after get reflect result
+ case SPELL_MISS_REFLECT:
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Spell::DoAllEffectOnTarget(TargetInfo *target)
+{
+ if (target->processed) // Check target
+ return;
+ target->processed = true; // Target checked in apply effects procedure
+
+ // Get mask of effects for target
+ uint32 mask = target->effectMask;
+ if (mask == 0) // No effects
+ return;
+
+ Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID);
+ if (!unit)
+ return;
+
+ SpellMissInfo missInfo = target->missCondition;
+ // Need init unitTarget by default unit (can changed in code on reflect)
+ // Or on missInfo!=SPELL_MISS_NONE unitTarget undefined (but need in trigger subsystem)
+ unitTarget = unit;
+
+ if (missInfo==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target
+ DoSpellHitOnUnit(unit, mask);
+ else if (missInfo == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit)
+ {
+ if (target->reflectResult == SPELL_MISS_NONE) // If reflected spell hit caster -> do all effect on him
+ DoSpellHitOnUnit(m_caster, mask);
+ }
+
+ // Do triggers only on miss/resist/parry/dodge
+ if (missInfo!=SPELL_MISS_NONE)
+ doTriggers(missInfo);
+
+ // Call scripted function for AI if this spell is casted upon a creature (except pets)
+ if(IS_CREATURE_GUID(target->targetGUID))
+ {
+ // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished)
+ // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... )
+ if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() )
+ ((Player*)m_caster)->CastedCreatureOrGO(unit->GetEntry(),unit->GetGUID(),m_spellInfo->Id);
+
+ if(((Creature*)unit)->AI())
+ ((Creature*)unit)->AI()->SpellHit(m_caster ,m_spellInfo);
+ }
+}
+
+void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask)
+{
+ if(!unit || !effectMask)
+ return;
+
+ // remove spell_magnet aura after first spell redirect and destroy target if its totem
+ if(m_magnetPair.first && m_magnetPair.second && m_magnetPair.second == unit)
+ {
+ if(unit->GetTypeId() == TYPEID_UNIT && ((Creature*)unit)->isTotem())
+ unit->DealDamage(unit,unit->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ return;
+ }
+
+ // Recheck immune (only for delayed spells)
+ if( m_spellInfo->speed && (
+ unit->IsImmunedToDamage(GetSpellSchoolMask(m_spellInfo),true) ||
+ unit->IsImmunedToSpell(m_spellInfo,true) ))
+ {
+ m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_IMMUNE);
+ return;
+ }
+
+ if( m_caster != unit )
+ {
+ if( !m_caster->IsFriendlyTo(unit) )
+ {
+ // for delayed spells ignore not visible explicit target
+ if(m_spellInfo->speed > 0.0f && unit==m_targets.getUnitTarget() && !unit->isVisibleForOrDetect(m_caster,false))
+ {
+ m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE);
+ return;
+ }
+
+ // exclude Arcane Missiles Dummy Aura aura for now (attack on hit)
+ // TODO: find way to not need this?
+ if(!(m_spellInfo->SpellFamilyName == SPELLFAMILY_MAGE &&
+ m_spellInfo->SpellFamilyFlags & 0x800LL))
+ {
+ unit->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+
+ if( !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) )
+ {
+ if(!unit->IsStandState() && !unit->hasUnitState(UNIT_STAT_STUNNED))
+ unit->SetStandState(PLAYER_STATE_NONE);
+
+ if(!unit->isInCombat() && unit->GetTypeId() != TYPEID_PLAYER && ((Creature*)unit)->AI())
+ ((Creature*)unit)->AI()->AttackStart(m_caster);
+
+ unit->SetInCombatWith(m_caster);
+ m_caster->SetInCombatWith(unit);
+
+ if(Player *attackedPlayer = unit->GetCharmerOrOwnerPlayerOrPlayerItself())
+ {
+ m_caster->SetContestedPvP(attackedPlayer);
+ }
+ unit->AddThreat(m_caster, 0.0f);
+ }
+ }
+ }
+ else
+ {
+ // for delayed spells ignore negative spells (after duel end) for friendly targets
+ if(m_spellInfo->speed > 0.0f && !IsPositiveSpell(m_spellInfo->Id))
+ {
+ m_caster->SendSpellMiss(unit, m_spellInfo->Id, SPELL_MISS_EVADE);
+ return;
+ }
+
+ // assisting case, healing and resurrection
+ if(unit->hasUnitState(UNIT_STAT_ATTACK_PLAYER))
+ m_caster->SetContestedPvP();
+ if( unit->isInCombat() && !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO) )
+ {
+ m_caster->SetInCombatState(unit->GetCombatTimer() > 0);
+ unit->getHostilRefManager().threatAssist(m_caster, 0.0f);
+ }
+ }
+ }
+
+ // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add
+ m_diminishGroup = GetDiminishingReturnsGroupForSpell(m_spellInfo,m_triggeredByAuraSpell);
+ m_diminishLevel = unit->GetDiminishing(m_diminishGroup);
+ // Increase Diminishing on unit, current informations for actually casts will use values above
+ if((GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_PLAYER && unit->GetTypeId() == TYPEID_PLAYER) || GetDiminishingReturnsGroupType(m_diminishGroup) == DRTYPE_ALL)
+ unit->IncrDiminishing(m_diminishGroup);
+
+ for(uint32 effectNumber=0;effectNumber<3;effectNumber++)
+ {
+ if (effectMask & (1<<effectNumber))
+ {
+ HandleEffects(unit,NULL,NULL,effectNumber,m_damageMultipliers[effectNumber]);
+ if ( m_applyMultiplierMask & (1 << effectNumber) )
+ {
+ // Get multiplier
+ float multiplier = m_spellInfo->DmgMultiplier[effectNumber];
+ // Apply multiplier mods
+ if(Player* modOwner = m_originalCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier,this);
+ m_damageMultipliers[effectNumber] *= multiplier;
+ }
+ }
+ }
+}
+
+void Spell::DoAllEffectOnTarget(GOTargetInfo *target)
+{
+ if (target->processed) // Check target
+ return;
+ target->processed = true; // Target checked in apply effects procedure
+
+ uint32 effectMask = target->effectMask;
+ if(!effectMask)
+ return;
+
+ GameObject* go = ObjectAccessor::GetGameObject(*m_caster, target->targetGUID);
+ if(!go)
+ return;
+
+ for(uint32 effectNumber=0;effectNumber<3;effectNumber++)
+ if (effectMask & (1<<effectNumber))
+ HandleEffects(NULL,NULL,go,effectNumber);
+
+ // cast at creature (or GO) quest objectives update at successful cast finished (+channel finished)
+ // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... )
+ if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() )
+ ((Player*)m_caster)->CastedCreatureOrGO(go->GetEntry(),go->GetGUID(),m_spellInfo->Id);
+}
+
+void Spell::DoAllEffectOnTarget(ItemTargetInfo *target)
+{
+ uint32 effectMask = target->effectMask;
+ if(!target->item || !effectMask)
+ return;
+
+ for(uint32 effectNumber=0;effectNumber<3;effectNumber++)
+ if (effectMask & (1<<effectNumber))
+ HandleEffects(NULL, target->item, NULL, effectNumber);
+}
+
+bool Spell::IsAliveUnitPresentInTargetList()
+{
+ // Not need check return true
+ if (m_needAliveTargetMask == 0)
+ return true;
+
+ uint8 needAliveTargetMask = m_needAliveTargetMask;
+
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if( ihit->missCondition == SPELL_MISS_NONE && (needAliveTargetMask & ihit->effectMask) )
+ {
+ Unit *unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
+
+ if (unit && unit->isAlive())
+ needAliveTargetMask &= ~ihit->effectMask; // remove from need alive mask effect that have alive target
+ }
+ }
+
+ // is all effects from m_needAliveTargetMask have alive targets
+ return needAliveTargetMask==0;
+}
+
+// Helper for Chain Healing
+// Spell target first
+// Raidmates then descending by injury suffered (MaxHealth - Health)
+// Other players/mobs then descending by injury suffered (MaxHealth - Health)
+struct ChainHealingOrder : public std::binary_function<const Unit*, const Unit*, bool>
+{
+ const Unit* MainTarget;
+ ChainHealingOrder(Unit const* Target) : MainTarget(Target) {};
+ // functor for operator ">"
+ bool operator()(Unit const* _Left, Unit const* _Right) const
+ {
+ return (ChainHealingHash(_Left) < ChainHealingHash(_Right));
+ }
+ int32 ChainHealingHash(Unit const* Target) const
+ {
+ if (Target == MainTarget)
+ return 0;
+ else if (Target->GetTypeId() == TYPEID_PLAYER && MainTarget->GetTypeId() == TYPEID_PLAYER &&
+ ((Player const*)Target)->IsInSameRaidWith((Player const*)MainTarget))
+ {
+ if (Target->GetHealth() == Target->GetMaxHealth())
+ return 40000;
+ else
+ return 20000 - Target->GetMaxHealth() + Target->GetHealth();
+ }
+ else
+ return 40000 - Target->GetMaxHealth() + Target->GetHealth();
+ }
+};
+
+class ChainHealingFullHealth: std::unary_function<const Unit*, bool>
+{
+ public:
+ const Unit* MainTarget;
+ ChainHealingFullHealth(const Unit* Target) : MainTarget(Target) {};
+
+ bool operator()(const Unit* Target)
+ {
+ return (Target != MainTarget && Target->GetHealth() == Target->GetMaxHealth());
+ }
+};
+
+// Helper for targets nearest to the spell target
+// The spell target is always first unless there is a target at _completely_ the same position (unbelievable case)
+struct TargetDistanceOrder : public std::binary_function<const Unit, const Unit, bool>
+{
+ const Unit* MainTarget;
+ TargetDistanceOrder(const Unit* Target) : MainTarget(Target) {};
+ // functor for operator ">"
+ bool operator()(const Unit* _Left, const Unit* _Right) const
+ {
+ return (MainTarget->GetDistance(_Left) < MainTarget->GetDistance(_Right));
+ }
+};
+
+void Spell::SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap)
+{
+ float radius;
+ if (m_spellInfo->EffectRadiusIndex[i])
+ radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ else
+ radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
+
+ if(m_originalCaster)
+ if(Player* modOwner = m_originalCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius,this);
+
+ uint32 EffectChainTarget = m_spellInfo->EffectChainTarget[i];
+ if(m_originalCaster)
+ if(Player* modOwner = m_originalCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this);
+
+ uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets;
+ switch(cur)
+ {
+ case TARGET_TOTEM_EARTH:
+ case TARGET_TOTEM_WATER:
+ case TARGET_TOTEM_AIR:
+ case TARGET_TOTEM_FIRE:
+ case TARGET_SELF:
+ case TARGET_SELF2:
+ case TARGET_DYNAMIC_OBJECT:
+ case TARGET_AREAEFFECT_CUSTOM:
+ case TARGET_AREAEFFECT_CUSTOM_2:
+ case TARGET_SUMMON:
+ {
+ TagUnitMap.push_back(m_caster);
+ break;
+ }
+ case TARGET_RANDOM_ENEMY_CHAIN_IN_AREA:
+ {
+ m_targets.m_targetMask = 0;
+ unMaxTargets = EffectChainTarget;
+ float max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS;
+
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ std::list<Unit *> tempUnitMap;
+
+ {
+ MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(m_caster, m_caster, max_range);
+ MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck> searcher(tempUnitMap, 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(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+
+ if(tempUnitMap.empty())
+ break;
+
+ tempUnitMap.sort(TargetDistanceOrder(m_caster));
+
+ //Now to get us a random target that's in the initial range of the spell
+ uint32 t = 0;
+ std::list<Unit *>::iterator itr = tempUnitMap.begin();
+ while(itr!= tempUnitMap.end() && (*itr)->GetDistance(m_caster) < radius)
+ ++t, ++itr;
+
+ if(!t)
+ break;
+
+ itr = tempUnitMap.begin();
+ std::advance(itr, rand()%t);
+ Unit *pUnitTarget = *itr;
+ TagUnitMap.push_back(pUnitTarget);
+
+ tempUnitMap.erase(itr);
+
+ tempUnitMap.sort(TargetDistanceOrder(pUnitTarget));
+
+ t = unMaxTargets - 1;
+ Unit *prev = pUnitTarget;
+ std::list<Unit*>::iterator next = tempUnitMap.begin();
+
+ while(t && next != tempUnitMap.end() )
+ {
+ if(prev->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS)
+ break;
+
+ if(!prev->IsWithinLOSInMap(*next))
+ {
+ ++next;
+ continue;
+ }
+
+ prev = *next;
+ TagUnitMap.push_back(prev);
+ tempUnitMap.erase(next);
+ tempUnitMap.sort(TargetDistanceOrder(prev));
+ next = tempUnitMap.begin();
+
+ --t;
+ }
+ }break;
+ case TARGET_PET:
+ {
+ Pet* tmpUnit = m_caster->GetPet();
+ if (!tmpUnit) break;
+ TagUnitMap.push_back(tmpUnit);
+ break;
+ }
+ case TARGET_CHAIN_DAMAGE:
+ {
+ if (EffectChainTarget <= 1)
+ {
+ Unit* pUnitTarget = SelectMagnetTarget();
+ if(pUnitTarget)
+ TagUnitMap.push_back(pUnitTarget);
+ }
+ else
+ {
+ Unit* pUnitTarget = m_targets.getUnitTarget();
+ if(!pUnitTarget)
+ break;
+
+ unMaxTargets = EffectChainTarget;
+
+ float max_range;
+ if(m_spellInfo->DmgClass==SPELL_DAMAGE_CLASS_MELEE)
+ max_range = radius; //
+ else
+ //FIXME: This very like horrible hack and wrong for most spells
+ max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS;
+
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ Unit* originalCaster = GetOriginalCaster();
+ if(originalCaster)
+ {
+ std::list<Unit *> tempUnitMap;
+
+ {
+ MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(pUnitTarget, originalCaster, max_range);
+ MaNGOS::UnitListSearcher<MaNGOS::AnyAoETargetUnitInObjectRangeCheck> searcher(tempUnitMap, 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(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+
+ tempUnitMap.sort(TargetDistanceOrder(pUnitTarget));
+
+ if(tempUnitMap.empty())
+ break;
+
+ if(*tempUnitMap.begin() == pUnitTarget)
+ tempUnitMap.erase(tempUnitMap.begin());
+
+ TagUnitMap.push_back(pUnitTarget);
+ uint32 t = unMaxTargets - 1;
+ Unit *prev = pUnitTarget;
+ std::list<Unit*>::iterator next = tempUnitMap.begin();
+
+ while(t && next != tempUnitMap.end() )
+ {
+ if(prev->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS)
+ break;
+
+ if(!prev->IsWithinLOSInMap(*next))
+ {
+ ++next;
+ continue;
+ }
+
+ prev = *next;
+ TagUnitMap.push_back(prev);
+ tempUnitMap.erase(next);
+ tempUnitMap.sort(TargetDistanceOrder(prev));
+ next = tempUnitMap.begin();
+
+ --t;
+ }
+ }
+ }
+ }break;
+ case TARGET_ALL_ENEMY_IN_AREA:
+ {
+ }break;
+ case TARGET_ALL_ENEMY_IN_AREA_INSTANT:
+ {
+ // targets the ground, not the units in the area
+ if (m_spellInfo->Effect[i]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_targets.m_destX, m_targets.m_destY));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_DEST_CENTER,SPELL_TARGETS_AOE_DAMAGE);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+
+ // exclude caster (this can be important if this not original caster)
+ TagUnitMap.remove(m_caster);
+ }
+ }break;
+ case TARGET_DUELVSPLAYER_COORDINATES:
+ {
+ if(Unit* currentTarget = m_targets.getUnitTarget())
+ {
+ m_targets.setDestination(currentTarget->GetPositionX(), currentTarget->GetPositionY(), currentTarget->GetPositionZ());
+ TagUnitMap.push_back(currentTarget);
+ }
+ }break;
+ case TARGET_ALL_PARTY_AROUND_CASTER:
+ case TARGET_ALL_PARTY_AROUND_CASTER_2:
+ case TARGET_ALL_PARTY:
+ {
+ Player *pTarget = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself();
+ Group *pGroup = pTarget ? pTarget->GetGroup() : NULL;
+
+ if(pGroup)
+ {
+ uint8 subgroup = pTarget->GetSubGroup();
+
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+
+ // IsHostileTo check duel and controlled by enemy
+ if( Target && Target->GetSubGroup()==subgroup && !m_caster->IsHostileTo(Target) )
+ {
+ if( m_caster->IsWithinDistInMap(Target, radius) )
+ TagUnitMap.push_back(Target);
+
+ if(Pet* pet = Target->GetPet())
+ if( m_caster->IsWithinDistInMap(pet, radius) )
+ TagUnitMap.push_back(pet);
+ }
+ }
+ }
+ else
+ {
+ Unit* ownerOrSelf = pTarget ? pTarget : m_caster->GetCharmerOrOwnerOrSelf();
+ if(ownerOrSelf==m_caster || m_caster->IsWithinDistInMap(ownerOrSelf, radius))
+ TagUnitMap.push_back(ownerOrSelf);
+ if(Pet* pet = ownerOrSelf->GetPet())
+ if( m_caster->IsWithinDistInMap(pet, radius) )
+ TagUnitMap.push_back(pet);
+ }
+ }break;
+ case TARGET_RANDOM_RAID_MEMBER:
+ {
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ if(Player* target = ((Player*)m_caster)->GetNextRandomRaidMember(radius))
+ TagUnitMap.push_back(target);
+ }break;
+ case TARGET_SINGLE_FRIEND:
+ case TARGET_SINGLE_FRIEND_2:
+ {
+ if(m_targets.getUnitTarget())
+ TagUnitMap.push_back(m_targets.getUnitTarget());
+ }break;
+ case TARGET_NONCOMBAT_PET:
+ {
+ if(Unit* target = m_targets.getUnitTarget())
+ if( target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->isPet() && ((Pet*)target)->getPetType() == MINI_PET)
+ TagUnitMap.push_back(target);
+ }break;
+ case TARGET_ALL_AROUND_CASTER:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_SELF_CENTER,SPELL_TARGETS_AOE_DAMAGE);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }break;
+ case TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_SELF_CENTER,SPELL_TARGETS_FRIENDLY);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }break;
+ case TARGET_ALL_FRIENDLY_UNITS_IN_AREA:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_targets.m_destX, m_targets.m_destY));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_DEST_CENTER,SPELL_TARGETS_FRIENDLY);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }break;
+ // TARGET_SINGLE_PARTY means that the spells can only be casted on a party member and not on the caster (some sceals, fire shield from imp, etc..)
+ case TARGET_SINGLE_PARTY:
+ {
+ Unit *target = m_targets.getUnitTarget();
+ // Thoses spells apparently can't be casted on the caster.
+ if( target && target != m_caster)
+ {
+ // Can only be casted on group's members or its pets
+ Group *pGroup = NULL;
+
+ Unit* owner = m_caster->GetCharmerOrOwner();
+ Unit *targetOwner = target->GetCharmerOrOwner();
+ if(owner)
+ {
+ if(owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ if( target == owner )
+ {
+ TagUnitMap.push_back(target);
+ break;
+ }
+ pGroup = ((Player*)owner)->GetGroup();
+ }
+ }
+ else if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ if( targetOwner == m_caster && target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->isPet())
+ {
+ TagUnitMap.push_back(target);
+ break;
+ }
+ pGroup = ((Player*)m_caster)->GetGroup();
+ }
+
+ if(pGroup)
+ {
+ // Our target can also be a player's pet who's grouped with us or our pet. But can't be controlled player
+ if(targetOwner)
+ {
+ if( targetOwner->GetTypeId() == TYPEID_PLAYER &&
+ target->GetTypeId()==TYPEID_UNIT && (((Creature*)target)->isPet()) &&
+ target->GetOwnerGUID()==targetOwner->GetGUID() &&
+ pGroup->IsMember(((Player*)targetOwner)->GetGUID()))
+ {
+ TagUnitMap.push_back(target);
+ }
+ }
+ // 1Our target can be a player who is on our group
+ else if (target->GetTypeId() == TYPEID_PLAYER && pGroup->IsMember(((Player*)target)->GetGUID()))
+ {
+ TagUnitMap.push_back(target);
+ }
+ }
+ }
+ }break;
+ case TARGET_GAMEOBJECT:
+ {
+ if(m_targets.getGOTarget())
+ AddGOTarget(m_targets.getGOTarget(), i);
+ }break;
+ case TARGET_IN_FRONT_OF_CASTER:
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ bool inFront = m_spellInfo->SpellVisual != 3879;
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, inFront ? PUSH_IN_FRONT : PUSH_IN_BACK,SPELL_TARGETS_AOE_DAMAGE);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }break;
+ case TARGET_DUELVSPLAYER:
+ {
+ Unit *target = m_targets.getUnitTarget();
+ if(target)
+ {
+ if(m_caster->IsFriendlyTo(target))
+ {
+ TagUnitMap.push_back(target);
+ }
+ else
+ {
+ Unit* pUnitTarget = SelectMagnetTarget();
+ if(pUnitTarget)
+ TagUnitMap.push_back(pUnitTarget);
+ }
+ }
+ }break;
+ case TARGET_GAMEOBJECT_ITEM:
+ {
+ if(m_targets.getGOTargetGUID())
+ AddGOTarget(m_targets.getGOTarget(), i);
+ else if(m_targets.getItemTarget())
+ AddItemTarget(m_targets.getItemTarget(), i);
+ break;
+ }
+ case TARGET_MASTER:
+ {
+ if(Unit* owner = m_caster->GetCharmerOrOwner())
+ TagUnitMap.push_back(owner);
+ break;
+ }
+ case TARGET_ALL_ENEMY_IN_AREA_CHANNELED:
+ {
+ // targets the ground, not the units in the area
+ if (m_spellInfo->Effect[i]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius, PUSH_DEST_CENTER,SPELL_TARGETS_AOE_DAMAGE);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+ }break;
+ case TARGET_MINION:
+ {
+ if(m_spellInfo->Effect[i] != SPELL_EFFECT_DUEL)
+ TagUnitMap.push_back(m_caster);
+ }break;
+ case TARGET_SINGLE_ENEMY:
+ {
+ Unit* pUnitTarget = SelectMagnetTarget();
+ if(pUnitTarget)
+ TagUnitMap.push_back(pUnitTarget);
+ }break;
+ case TARGET_AREAEFFECT_PARTY:
+ {
+ Unit* owner = m_caster->GetCharmerOrOwner();
+ Player *pTarget = NULL;
+
+ if(owner)
+ {
+ TagUnitMap.push_back(m_caster);
+ if(owner->GetTypeId() == TYPEID_PLAYER)
+ pTarget = (Player*)owner;
+ }
+ else if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ if(Unit* target = m_targets.getUnitTarget())
+ {
+ if( target->GetTypeId() != TYPEID_PLAYER)
+ {
+ if(((Creature*)target)->isPet())
+ {
+ Unit *targetOwner = target->GetOwner();
+ if(targetOwner->GetTypeId() == TYPEID_PLAYER)
+ pTarget = (Player*)targetOwner;
+ }
+ }
+ else
+ pTarget = (Player*)target;
+ }
+ }
+
+ Group* pGroup = pTarget ? pTarget->GetGroup() : NULL;
+
+ if(pGroup)
+ {
+ uint8 subgroup = pTarget->GetSubGroup();
+
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+
+ // IsHostileTo check duel and controlled by enemy
+ if(Target && Target->GetSubGroup()==subgroup && !m_caster->IsHostileTo(Target))
+ {
+ if( pTarget->IsWithinDistInMap(Target, radius) )
+ TagUnitMap.push_back(Target);
+
+ if(Pet* pet = Target->GetPet())
+ if( pTarget->IsWithinDistInMap(pet, radius) )
+ TagUnitMap.push_back(pet);
+ }
+ }
+ }
+ else if (owner)
+ {
+ if(m_caster->IsWithinDistInMap(owner, radius))
+ TagUnitMap.push_back(owner);
+ }
+ else if(pTarget)
+ {
+ TagUnitMap.push_back(pTarget);
+
+ if(Pet* pet = pTarget->GetPet())
+ if( m_caster->IsWithinDistInMap(pet, radius) )
+ TagUnitMap.push_back(pet);
+ }
+
+ }break;
+ case TARGET_SCRIPT:
+ {
+ if(m_targets.getUnitTarget())
+ TagUnitMap.push_back(m_targets.getUnitTarget());
+ if(m_targets.getItemTarget())
+ AddItemTarget(m_targets.getItemTarget(), i);
+ }break;
+ case TARGET_SELF_FISHING:
+ {
+ TagUnitMap.push_back(m_caster);
+ }break;
+ case TARGET_CHAIN_HEAL:
+ {
+ Unit* pUnitTarget = m_targets.getUnitTarget();
+ if(!pUnitTarget)
+ break;
+
+ if (EffectChainTarget <= 1)
+ TagUnitMap.push_back(pUnitTarget);
+ else
+ {
+ unMaxTargets = EffectChainTarget;
+ float max_range = radius + unMaxTargets * CHAIN_SPELL_JUMP_RADIUS;
+
+ std::list<Unit *> tempUnitMap;
+
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, tempUnitMap, max_range, PUSH_SELF_CENTER, SPELL_TARGETS_FRIENDLY);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_object_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_object_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+
+ }
+
+ if(m_caster != pUnitTarget && std::find(tempUnitMap.begin(),tempUnitMap.end(),m_caster) == tempUnitMap.end() )
+ tempUnitMap.push_front(m_caster);
+
+ tempUnitMap.sort(TargetDistanceOrder(pUnitTarget));
+
+ if(tempUnitMap.empty())
+ break;
+
+ if(*tempUnitMap.begin() == pUnitTarget)
+ tempUnitMap.erase(tempUnitMap.begin());
+
+ TagUnitMap.push_back(pUnitTarget);
+ uint32 t = unMaxTargets - 1;
+ Unit *prev = pUnitTarget;
+ std::list<Unit*>::iterator next = tempUnitMap.begin();
+
+ while(t && next != tempUnitMap.end() )
+ {
+ if(prev->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS)
+ break;
+
+ if(!prev->IsWithinLOSInMap(*next))
+ {
+ ++next;
+ continue;
+ }
+
+ if((*next)->GetHealth() == (*next)->GetMaxHealth())
+ {
+ next = tempUnitMap.erase(next);
+ continue;
+ }
+
+ prev = *next;
+ TagUnitMap.push_back(prev);
+ tempUnitMap.erase(next);
+ tempUnitMap.sort(TargetDistanceOrder(prev));
+ next = tempUnitMap.begin();
+
+ --t;
+ }
+ }
+ }break;
+ case TARGET_CURRENT_ENEMY_COORDINATES:
+ {
+ Unit* currentTarget = m_targets.getUnitTarget();
+ if(currentTarget)
+ {
+ TagUnitMap.push_back(currentTarget);
+ m_targets.setDestination(currentTarget->GetPositionX(), currentTarget->GetPositionY(), currentTarget->GetPositionZ());
+ if(m_spellInfo->EffectImplicitTargetB[i]==TARGET_ALL_ENEMY_IN_AREA_INSTANT)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(currentTarget->GetPositionX(), currentTarget->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius,PUSH_TARGET_CENTER, SPELL_TARGETS_AOE_DAMAGE);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_notifier(notifier);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+ }
+ }break;
+ case TARGET_AREAEFFECT_PARTY_AND_CLASS:
+ {
+ Player* targetPlayer = m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER
+ ? (Player*)m_targets.getUnitTarget() : NULL;
+
+ Group* pGroup = targetPlayer ? targetPlayer->GetGroup() : NULL;
+ if(pGroup)
+ {
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ {
+ Player* Target = itr->getSource();
+
+ // IsHostileTo check duel and controlled by enemy
+ if( Target && targetPlayer->IsWithinDistInMap(Target, radius) &&
+ targetPlayer->getClass() == Target->getClass() &&
+ !m_caster->IsHostileTo(Target) )
+ {
+ TagUnitMap.push_back(Target);
+ }
+ }
+ }
+ else if(m_targets.getUnitTarget())
+ TagUnitMap.push_back(m_targets.getUnitTarget());
+ break;
+ }
+ case TARGET_TABLE_X_Y_Z_COORDINATES:
+ {
+ SpellTargetPosition const* st = spellmgr.GetSpellTargetPosition(m_spellInfo->Id);
+ if(st)
+ {
+ if (st->target_mapId == m_caster->GetMapId())
+ m_targets.setDestination(st->target_X, st->target_Y, st->target_Z);
+
+ // if B==TARGET_TABLE_X_Y_Z_COORDINATES then A already fill all required targets
+ if (m_spellInfo->EffectImplicitTargetB[i] && m_spellInfo->EffectImplicitTargetB[i]!=TARGET_TABLE_X_Y_Z_COORDINATES)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ SpellTargets targetB = SPELL_TARGETS_AOE_DAMAGE;
+ // Select friendly targets for positive effect
+ if (IsPositiveEffect(m_spellInfo->Id, i))
+ targetB = SPELL_TARGETS_FRIENDLY;
+
+ MaNGOS::SpellNotifierCreatureAndPlayer notifier(*this, TagUnitMap, radius,PUSH_DEST_CENTER, targetB);
+
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, WorldTypeMapContainer > world_notifier(notifier);
+ TypeContainerVisitor<MaNGOS::SpellNotifierCreatureAndPlayer, GridTypeMapContainer > grid_notifier(notifier);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ cell_lock->Visit(cell_lock, grid_notifier, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+ }
+ }
+ else
+ sLog.outError( "SPELL: unknown target coordinates for spell ID %u\n", m_spellInfo->Id );
+ }break;
+ case TARGET_BEHIND_VICTIM:
+ {
+ Unit *pTarget = m_caster->getVictim();
+ if(!pTarget && m_caster->GetTypeId() == TYPEID_PLAYER)
+ pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection());
+
+ if(pTarget)
+ {
+ float _target_x, _target_y, _target_z;
+ pTarget->GetClosePoint(_target_x, _target_y, _target_z, m_caster->GetObjectSize(), CONTACT_DISTANCE, M_PI);
+ if(pTarget->IsWithinLOS(_target_x,_target_y,_target_z))
+ m_targets.setDestination(_target_x, _target_y, _target_z);
+ }
+ }break;
+ default:
+ break;
+ }
+
+ if (unMaxTargets && TagUnitMap.size() > unMaxTargets)
+ {
+ // make sure one unit is always removed per iteration
+ uint32 removed_utarget = 0;
+ for (std::list<Unit*>::iterator itr = TagUnitMap.begin(), next; itr != TagUnitMap.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+ if (!*itr) continue;
+ if ((*itr) == m_targets.getUnitTarget())
+ {
+ TagUnitMap.erase(itr);
+ removed_utarget = 1;
+ // break;
+ }
+ }
+ // remove random units from the map
+ while (TagUnitMap.size() > unMaxTargets - removed_utarget)
+ {
+ uint32 poz = urand(0, TagUnitMap.size()-1);
+ for (std::list<Unit*>::iterator itr = TagUnitMap.begin(); itr != TagUnitMap.end(); ++itr, --poz)
+ {
+ if (!*itr) continue;
+ if (!poz)
+ {
+ TagUnitMap.erase(itr);
+ break;
+ }
+ }
+ }
+ // the player's target will always be added to the map
+ if (removed_utarget && m_targets.getUnitTarget())
+ TagUnitMap.push_back(m_targets.getUnitTarget());
+ }
+}
+
+void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura)
+{
+ m_targets = *targets;
+
+ m_spellState = SPELL_STATE_PREPARING;
+
+ m_castPositionX = m_caster->GetPositionX();
+ m_castPositionY = m_caster->GetPositionY();
+ m_castPositionZ = m_caster->GetPositionZ();
+ m_castOrientation = m_caster->GetOrientation();
+
+ if(triggeredByAura)
+ m_triggeredByAuraSpell = triggeredByAura->GetSpellProto();
+
+ // create and add update event for this spell
+ SpellEvent* Event = new SpellEvent(this);
+ m_caster->m_Events.AddEvent(Event, m_caster->m_Events.CalculateTime(1));
+
+ //Prevent casting at cast another spell (ServerSide check)
+ if(m_caster->IsNonMeleeSpellCasted(false, true) && m_cast_count)
+ {
+ SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS);
+ finish(false);
+ return;
+ }
+
+ // Fill cost data
+ m_powerCost = CalculatePowerCost();
+
+ uint8 result = CanCast(true);
+ if(result != 0 && !IsAutoRepeat()) //always cast autorepeat dummy for triggering
+ {
+ if(triggeredByAura)
+ {
+ SendChannelUpdate(0);
+ triggeredByAura->SetAuraDuration(0);
+ }
+ SendCastResult(result);
+ finish(false);
+ return;
+ }
+
+ // calculate cast time (calculated after first CanCast check to prevent charge counting for first CanCast fail)
+ m_casttime = GetSpellCastTime(m_spellInfo, this);
+
+ // set timer base at cast time
+ ReSetTimer();
+
+ // stealth must be removed at cast starting (at show channel bar)
+ // skip triggered spell (item equip spell casting and other not explicit character casts/item uses)
+ if ( !m_IsTriggeredSpell && isSpellBreakStealth(m_spellInfo) )
+ {
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+ }
+
+ if(m_IsTriggeredSpell)
+ cast(true);
+ else
+ {
+ m_caster->SetCurrentCastedSpell( this );
+ m_selfContainer = &(m_caster->m_currentSpells[GetCurrentContainer()]);
+ SendSpellStart();
+ }
+}
+
+void Spell::cancel()
+{
+ if(m_spellState == SPELL_STATE_FINISHED)
+ return;
+
+ m_autoRepeat = false;
+ switch (m_spellState)
+ {
+ case SPELL_STATE_PREPARING:
+ case SPELL_STATE_DELAYED:
+ {
+ SendInterrupted(0);
+ SendCastResult(SPELL_FAILED_INTERRUPTED);
+ } break;
+
+ case SPELL_STATE_CASTING:
+ {
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if( ihit->missCondition == SPELL_MISS_NONE )
+ {
+ Unit* unit = m_caster->GetGUID()==(*ihit).targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
+ if( unit && unit->isAlive() )
+ unit->RemoveAurasDueToSpell(m_spellInfo->Id);
+ }
+ }
+
+ m_caster->RemoveAurasDueToSpell(m_spellInfo->Id);
+ SendChannelUpdate(0);
+ SendInterrupted(0);
+ SendCastResult(SPELL_FAILED_INTERRUPTED);
+ } break;
+
+ default:
+ {
+ } break;
+ }
+
+ finish(false);
+ m_caster->RemoveDynObject(m_spellInfo->Id);
+ m_caster->RemoveGameObject(m_spellInfo->Id,true);
+}
+
+void Spell::cast(bool skipCheck)
+{
+ uint8 castResult = 0;
+
+ // update pointers base at GUIDs to prevent access to non-existed already object
+ UpdatePointers();
+
+ // cancel at lost main target unit
+ if(!m_targets.getUnitTarget() && m_targets.getUnitTargetGUID() && m_targets.getUnitTargetGUID() != m_caster->GetGUID())
+ {
+ cancel();
+ return;
+ }
+
+ if(m_caster->GetTypeId() != TYPEID_PLAYER && m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster)
+ m_caster->SetInFront(m_targets.getUnitTarget());
+
+ castResult = CheckPower();
+ if(castResult != 0)
+ {
+ SendCastResult(castResult);
+ finish(false);
+ return;
+ }
+
+ // triggered cast called from Spell::prepare where it was already checked
+ if(!skipCheck)
+ {
+ castResult = CanCast(false);
+ if(castResult != 0)
+ {
+ SendCastResult(castResult);
+ finish(false);
+ return;
+ }
+ }
+
+ // Conflagrate - consumes immolate
+ if ((m_spellInfo->TargetAuraState == AURA_STATE_IMMOLATE) && m_targets.getUnitTarget())
+ {
+ // for caster applied auras only
+ Unit::AuraList const &mPeriodic = m_targets.getUnitTarget()->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
+ for(Unit::AuraList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i)
+ {
+ if( (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && ((*i)->GetSpellProto()->SpellFamilyFlags & 4) &&
+ (*i)->GetCasterGUID()==m_caster->GetGUID() )
+ {
+ m_targets.getUnitTarget()->RemoveAura((*i)->GetId(), (*i)->GetEffIndex());
+ break;
+ }
+ }
+ }
+
+ // traded items have trade slot instead of guid in m_itemTargetGUID
+ // set to real guid to be sent later to the client
+ m_targets.updateTradeSlotItem();
+
+ // CAST SPELL
+ SendSpellCooldown();
+
+ TakePower();
+ TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot
+ FillTargetMap();
+
+ if(m_spellState == SPELL_STATE_FINISHED) // stop cast if spell marked as finish somewhere in Take*/FillTargetMap
+ return;
+
+ SendCastResult(castResult);
+ SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()...
+
+ // Pass cast spell event to handler (not send triggered by aura spells)
+ if (m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE && m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_RANGED && !m_triggeredByAuraSpell)
+ {
+ m_caster->ProcDamageAndSpell(m_targets.getUnitTarget(), PROC_FLAG_CAST_SPELL, PROC_FLAG_NONE, 0, SPELL_SCHOOL_MASK_NONE, m_spellInfo, m_IsTriggeredSpell);
+
+ // update pointers base at GUIDs to prevent access to non-existed already object
+ UpdatePointers(); // pointers can be invalidate at triggered spell casting
+ }
+
+ // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells
+ if (m_spellInfo->speed > 0.0f)
+ {
+
+ // Remove used for cast item if need (it can be already NULL after TakeReagents call
+ // in case delayed spell remove item at cast delay start
+ TakeCastItem();
+
+ // Okay, maps created, now prepare flags
+ m_immediateHandled = false;
+ m_spellState = SPELL_STATE_DELAYED;
+ SetDelayStart(0);
+ }
+ else
+ {
+ // Immediate spell, no big deal
+ handle_immediate();
+ }
+}
+
+void Spell::handle_immediate()
+{
+ // start channeling if applicable
+ if(IsChanneledSpell(m_spellInfo))
+ {
+ m_spellState = SPELL_STATE_CASTING;
+ SendChannelStart(GetSpellDuration(m_spellInfo));
+ }
+
+ // process immediate effects (items, ground, etc.) also initialize some variables
+ _handle_immediate_phase();
+
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ DoAllEffectOnTarget(&(*ihit));
+
+ for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit)
+ DoAllEffectOnTarget(&(*ihit));
+
+ // spell is finished, perform some last features of the spell here
+ _handle_finish_phase();
+
+ // Remove used for cast item if need (it can be already NULL after TakeReagents call
+ TakeCastItem();
+
+ if(m_spellState != SPELL_STATE_CASTING)
+ finish(true); // successfully finish spell cast (not last in case autorepeat or channel spell)
+}
+
+uint64 Spell::handle_delayed(uint64 t_offset)
+{
+ uint64 next_time = 0;
+
+ if (!m_immediateHandled)
+ {
+ _handle_immediate_phase();
+ m_immediateHandled = true;
+ }
+
+ // now recheck units targeting correctness (need before any effects apply to prevent adding immunity at first effect not allow apply second spell effect and similar cases)
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if (ihit->processed == false)
+ {
+ if ( ihit->timeDelay <= t_offset )
+ DoAllEffectOnTarget(&(*ihit));
+ else if( next_time == 0 || ihit->timeDelay < next_time )
+ next_time = ihit->timeDelay;
+ }
+ }
+
+ // now recheck gameobject targeting correctness
+ for(std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end();++ighit)
+ {
+ if (ighit->processed == false)
+ {
+ if ( ighit->timeDelay <= t_offset )
+ DoAllEffectOnTarget(&(*ighit));
+ else if( next_time == 0 || ighit->timeDelay < next_time )
+ next_time = ighit->timeDelay;
+ }
+ }
+ // All targets passed - need finish phase
+ if (next_time == 0)
+ {
+ // spell is finished, perform some last features of the spell here
+ _handle_finish_phase();
+
+ finish(true); // successfully finish spell cast
+
+ // return zero, spell is finished now
+ return 0;
+ }
+ else
+ {
+ // spell is unfinished, return next execution time
+ return next_time;
+ }
+}
+
+void Spell::_handle_immediate_phase()
+{
+ // handle some immediate features of the spell here
+ HandleThreatSpells(m_spellInfo->Id);
+
+ m_needSpellLog = IsNeedSendToClient();
+ for(uint32 j = 0;j<3;j++)
+ {
+ if(m_spellInfo->Effect[j]==0)
+ continue;
+
+ // apply Send Event effect to ground in case empty target lists
+ if( m_spellInfo->Effect[j] == SPELL_EFFECT_SEND_EVENT && !HaveTargetsForEffect(j) )
+ {
+ HandleEffects(NULL,NULL,NULL, j);
+ continue;
+ }
+
+ // Don't do spell log, if is school damage spell
+ if(m_spellInfo->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE || m_spellInfo->Effect[j] == 0)
+ m_needSpellLog = false;
+
+ uint32 EffectChainTarget = m_spellInfo->EffectChainTarget[j];
+ if(m_originalCaster)
+ if(Player* modOwner = m_originalCaster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, EffectChainTarget, this);
+
+ // initialize multipliers
+ m_damageMultipliers[j] = 1.0f;
+ if( (m_spellInfo->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE || m_spellInfo->EffectImplicitTargetA[j] == TARGET_CHAIN_HEAL) &&
+ (EffectChainTarget > 1) )
+ m_applyMultiplierMask |= 1 << j;
+ }
+
+ // initialize Diminishing Returns Data
+ m_diminishLevel = DIMINISHING_LEVEL_1;
+ m_diminishGroup = DIMINISHING_NONE;
+
+ // process items
+ for(std::list<ItemTargetInfo>::iterator ihit= m_UniqueItemInfo.begin();ihit != m_UniqueItemInfo.end();++ihit)
+ DoAllEffectOnTarget(&(*ihit));
+
+ // process ground
+ for(uint32 j = 0;j<3;j++)
+ {
+ // persistent area auras target only the ground
+ if(m_spellInfo->Effect[j] == SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ HandleEffects(NULL,NULL,NULL, j);
+ }
+}
+
+void Spell::_handle_finish_phase()
+{
+ // spell log
+ if(m_needSpellLog)
+ SendLogExecute();
+}
+
+void Spell::SendSpellCooldown()
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* _player = (Player*)m_caster;
+ // Add cooldown for max (disable spell)
+ // Cooldown started on SendCooldownEvent call
+ if (m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
+ {
+ _player->AddSpellCooldown(m_spellInfo->Id, 0, time(NULL) - 1);
+ return;
+ }
+
+ // init cooldown values
+ uint32 cat = 0;
+ int32 rec = -1;
+ int32 catrec = -1;
+
+ // some special item spells without correct cooldown in SpellInfo
+ // cooldown information stored in item prototype
+ // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client.
+
+ if(m_CastItem)
+ {
+ ItemPrototype const* proto = m_CastItem->GetProto();
+ if(proto)
+ {
+ for(int idx = 0; idx < 5; ++idx)
+ {
+ if(proto->Spells[idx].SpellId == m_spellInfo->Id)
+ {
+ cat = proto->Spells[idx].SpellCategory;
+ rec = proto->Spells[idx].SpellCooldown;
+ catrec = proto->Spells[idx].SpellCategoryCooldown;
+ break;
+ }
+ }
+ }
+ }
+
+ // if no cooldown found above then base at DBC data
+ if(rec < 0 && catrec < 0)
+ {
+ cat = m_spellInfo->Category;
+ rec = m_spellInfo->RecoveryTime;
+ catrec = m_spellInfo->CategoryRecoveryTime;
+ }
+
+ // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
+ // prevent 0 cooldowns set by another way
+ if (rec <= 0 && catrec <= 0 && (cat == 76 || cat == 351))
+ rec = _player->GetAttackTime(RANGED_ATTACK);
+
+ // Now we have cooldown data (if found any), time to apply mods
+ if(rec > 0)
+ _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, rec, this);
+
+ if(catrec > 0)
+ _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, catrec, this);
+
+ // replace negative cooldowns by 0
+ if (rec < 0) rec = 0;
+ if (catrec < 0) catrec = 0;
+
+ // no cooldown after applying spell mods
+ if( rec == 0 && catrec == 0)
+ return;
+
+ time_t curTime = time(NULL);
+
+ time_t catrecTime = catrec ? curTime+catrec/1000 : 0; // in secs
+ time_t recTime = rec ? curTime+rec/1000 : catrecTime;// in secs
+
+ // self spell cooldown
+ if(recTime > 0)
+ _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, recTime);
+
+ // category spells
+ if (catrec > 0)
+ {
+ SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat);
+ if(i_scstore != sSpellCategoryStore.end())
+ {
+ for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
+ {
+ if(*i_scset == m_spellInfo->Id) // skip main spell, already handled above
+ continue;
+
+ _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, catrecTime);
+ }
+ }
+ }
+}
+
+void Spell::update(uint32 difftime)
+{
+ // update pointers based at it's GUIDs
+ UpdatePointers();
+
+ if(m_targets.getUnitTargetGUID() && !m_targets.getUnitTarget())
+ {
+ cancel();
+ return;
+ }
+
+ // check if the player caster has moved before the spell finished
+ if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) &&
+ (m_castPositionX != m_caster->GetPositionX() || m_castPositionY != m_caster->GetPositionY() || m_castPositionZ != m_caster->GetPositionZ()) &&
+ (m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)))
+ {
+ // always cancel for channeled spells
+ if( m_spellState == SPELL_STATE_CASTING )
+ cancel();
+ // don't cancel for melee, autorepeat, triggered and instant spells
+ else if(!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_IsTriggeredSpell && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT))
+ cancel();
+ }
+
+ switch(m_spellState)
+ {
+ case SPELL_STATE_PREPARING:
+ {
+ if(m_timer)
+ {
+ if(difftime >= m_timer)
+ m_timer = 0;
+ else
+ m_timer -= difftime;
+ }
+
+ if(m_timer == 0 && !IsNextMeleeSwingSpell() && !IsAutoRepeat())
+ cast();
+ } break;
+ case SPELL_STATE_CASTING:
+ {
+ if(m_timer > 0)
+ {
+ if( m_caster->GetTypeId() == TYPEID_PLAYER )
+ {
+ // check if player has jumped before the channeling finished
+ if(m_caster->HasUnitMovementFlag(MOVEMENTFLAG_JUMPING))
+ cancel();
+
+ // check for incapacitating player states
+ if( m_caster->hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_CONFUSED))
+ cancel();
+
+ // check if player has turned if flag is set
+ if( m_spellInfo->ChannelInterruptFlags & CHANNEL_FLAG_TURNING && m_castOrientation != m_caster->GetOrientation() )
+ cancel();
+ }
+
+ // check if there are alive targets left
+ if (!IsAliveUnitPresentInTargetList())
+ {
+ SendChannelUpdate(0);
+ finish();
+ }
+
+ if(difftime >= m_timer)
+ m_timer = 0;
+ else
+ m_timer -= difftime;
+ }
+
+ if(m_timer == 0)
+ {
+ SendChannelUpdate(0);
+
+ // channeled spell processed independently for quest targeting
+ // cast at creature (or GO) quest objectives update at successful cast channel finished
+ // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... )
+ if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsNextMeleeSwingSpell() )
+ {
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ TargetInfo* target = &*ihit;
+ if(!IS_CREATURE_GUID(target->targetGUID))
+ continue;
+
+ Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID);
+ if (unit==NULL)
+ continue;
+
+ ((Player*)m_caster)->CastedCreatureOrGO(unit->GetEntry(),unit->GetGUID(),m_spellInfo->Id);
+ }
+
+ for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit)
+ {
+ GOTargetInfo* target = &*ihit;
+
+ GameObject* go = ObjectAccessor::GetGameObject(*m_caster, target->targetGUID);
+ if(!go)
+ continue;
+
+ ((Player*)m_caster)->CastedCreatureOrGO(go->GetEntry(),go->GetGUID(),m_spellInfo->Id);
+ }
+ }
+
+ finish();
+ }
+ } break;
+ default:
+ {
+ }break;
+ }
+}
+
+void Spell::finish(bool ok)
+{
+ if(!m_caster)
+ return;
+
+ if(m_spellState == SPELL_STATE_FINISHED)
+ return;
+
+ m_spellState = SPELL_STATE_FINISHED;
+
+ //remove spell mods
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_caster)->RemoveSpellMods(this);
+
+ // other code related only to successfully finished spells
+ if(!ok)
+ return;
+
+ //handle SPELL_AURA_ADD_TARGET_TRIGGER auras
+ Unit::AuraList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER);
+ for(Unit::AuraList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i)
+ {
+ SpellEntry const *auraSpellInfo = (*i)->GetSpellProto();
+ uint32 auraSpellIdx = (*i)->GetEffIndex();
+ if (IsAffectedBy(auraSpellInfo, auraSpellIdx))
+ {
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ if( ihit->effectMask & (1<<auraSpellIdx) )
+ {
+ // check m_caster->GetGUID() let load auras at login and speedup most often case
+ Unit *unit = m_caster->GetGUID()== ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
+ if (unit && unit->isAlive())
+ {
+ // Calculate chance at that moment (can be depend for example from combo points)
+ int32 chance = m_caster->CalculateSpellDamage(auraSpellInfo, auraSpellIdx, (*i)->GetBasePoints(),unit);
+
+ if(roll_chance_i(chance))
+ m_caster->CastSpell(unit, auraSpellInfo->EffectTriggerSpell[auraSpellIdx], true, NULL, (*i));
+ }
+ }
+ }
+ }
+
+ if (IsMeleeAttackResetSpell())
+ {
+ m_caster->resetAttackTimer(BASE_ATTACK);
+ if(m_caster->haveOffhandWeapon())
+ m_caster->resetAttackTimer(OFF_ATTACK);
+ }
+
+ /*if (IsRangedAttackResetSpell())
+ m_caster->resetAttackTimer(RANGED_ATTACK);*/
+
+ // Clear combo at finish state
+ if(m_caster->GetTypeId() == TYPEID_PLAYER && NeedsComboPoints(m_spellInfo))
+ ((Player*)m_caster)->ClearComboPoints();
+
+ // call triggered spell only at successful cast (after clear combo points -> for add some if need)
+ if(!m_TriggerSpells.empty())
+ TriggerSpell();
+
+ // Stop Attack for some spells
+ if( m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET )
+ m_caster->AttackStop();
+}
+
+void Spell::SendCastResult(uint8 result)
+{
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(((Player*)m_caster)->GetSession()->PlayerLoading()) // don't send cast results at loading time
+ return;
+
+ if(result != 0)
+ {
+ WorldPacket data(SMSG_CAST_FAILED, (4+1+1));
+ data << uint32(m_spellInfo->Id);
+ data << uint8(result); // problem
+ data << uint8(m_cast_count); // single cast or multi 2.3 (0/1)
+ switch (result)
+ {
+ case SPELL_FAILED_REQUIRES_SPELL_FOCUS:
+ data << uint32(m_spellInfo->RequiresSpellFocus);
+ break;
+ case SPELL_FAILED_REQUIRES_AREA:
+ // hardcode areas limitation case
+ if( m_spellInfo->Id==41618 || m_spellInfo->Id==41620 )
+ data << uint32(3842);
+ else if( m_spellInfo->Id==41617 || m_spellInfo->Id==41619 )
+ data << uint32(3905);
+ // normal case
+ else
+ data << uint32(m_spellInfo->AreaId);
+ break;
+ case SPELL_FAILED_TOTEMS:
+ if(m_spellInfo->Totem[0])
+ data << uint32(m_spellInfo->Totem[0]);
+ if(m_spellInfo->Totem[1])
+ data << uint32(m_spellInfo->Totem[1]);
+ break;
+ case SPELL_FAILED_TOTEM_CATEGORY:
+ if(m_spellInfo->TotemCategory[0])
+ data << uint32(m_spellInfo->TotemCategory[0]);
+ if(m_spellInfo->TotemCategory[1])
+ data << uint32(m_spellInfo->TotemCategory[1]);
+ break;
+ case SPELL_FAILED_EQUIPPED_ITEM_CLASS:
+ data << uint32(m_spellInfo->EquippedItemClass);
+ data << uint32(m_spellInfo->EquippedItemSubClassMask);
+ data << uint32(m_spellInfo->EquippedItemInventoryTypeMask);
+ break;
+ }
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+ else
+ {
+ WorldPacket data(SMSG_CLEAR_EXTRA_AURA_INFO, (8+4));
+ data.append(m_caster->GetPackGUID());
+ data << uint32(m_spellInfo->Id);
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+}
+
+void Spell::SendSpellStart()
+{
+ if(!IsNeedSendToClient())
+ return;
+
+ sLog.outDebug("Sending SMSG_SPELL_START id=%u",m_spellInfo->Id);
+
+ uint16 castFlags = CAST_FLAG_UNKNOWN1;
+ if(IsRangedSpell())
+ castFlags |= CAST_FLAG_AMMO;
+
+ Unit * target;
+ if(!m_targets.getUnitTarget())
+ target = m_caster;
+ else
+ target = m_targets.getUnitTarget();
+
+ WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2));
+ if(m_CastItem)
+ data.append(m_CastItem->GetPackGUID());
+ else
+ data.append(m_caster->GetPackGUID());
+
+ data.append(m_caster->GetPackGUID());
+ data << uint32(m_spellInfo->Id);
+ data << uint8(m_cast_count); // single cast or multi 2.3 (0/1)
+ data << uint16(castFlags);
+ data << uint32(m_timer);
+
+ m_targets.write(&data);
+
+ if( castFlags & CAST_FLAG_AMMO )
+ WriteAmmoToPacket(&data);
+
+ m_caster->SendMessageToSet(&data, true);
+}
+
+void Spell::SendSpellGo()
+{
+ // not send invisible spell casting
+ if(!IsNeedSendToClient())
+ return;
+
+ sLog.outDebug("Sending SMSG_SPELL_GO id=%u",m_spellInfo->Id);
+
+ Unit * target;
+ if(!m_targets.getUnitTarget())
+ target = m_caster;
+ else
+ target = m_targets.getUnitTarget();
+
+ uint16 castFlags = CAST_FLAG_UNKNOWN3;
+ if(IsRangedSpell())
+ castFlags |= CAST_FLAG_AMMO;
+
+ WorldPacket data(SMSG_SPELL_GO, 50); // guess size
+ if(m_CastItem)
+ data.append(m_CastItem->GetPackGUID());
+ else
+ data.append(m_caster->GetPackGUID());
+
+ data.append(m_caster->GetPackGUID());
+ data << uint32(m_spellInfo->Id);
+ data << uint16(castFlags);
+ data << uint32(getMSTime()); // timestamp
+
+ WriteSpellGoTargets(&data);
+
+ m_targets.write(&data);
+
+ if( castFlags & CAST_FLAG_AMMO )
+ WriteAmmoToPacket(&data);
+
+ m_caster->SendMessageToSet(&data, true);
+}
+
+void Spell::WriteAmmoToPacket( WorldPacket * data )
+{
+ uint32 ammoInventoryType = 0;
+ uint32 ammoDisplayID = 0;
+
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK );
+ if(pItem)
+ {
+ ammoInventoryType = pItem->GetProto()->InventoryType;
+ if( ammoInventoryType == INVTYPE_THROWN )
+ ammoDisplayID = pItem->GetProto()->DisplayInfoID;
+ else
+ {
+ uint32 ammoID = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID);
+ if(ammoID)
+ {
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( ammoID );
+ if(pProto)
+ {
+ ammoDisplayID = pProto->DisplayInfoID;
+ ammoInventoryType = pProto->InventoryType;
+ }
+ }
+ else if(m_caster->GetDummyAura(46699)) // Requires No Ammo
+ {
+ ammoDisplayID = 5996; // normal arrow
+ ammoInventoryType = INVTYPE_AMMO;
+ }
+ }
+ }
+ }
+ // TODO: implement selection ammo data based at ranged weapon stored in equipmodel/equipinfo/equipslot fields
+
+ *data << uint32(ammoDisplayID);
+ *data << uint32(ammoInventoryType);
+}
+
+void Spell::WriteSpellGoTargets( WorldPacket * data )
+{
+ *data << (uint8)m_countOfHit;
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ if ((*ihit).missCondition == SPELL_MISS_NONE) // Add only hits
+ *data << uint64(ihit->targetGUID);
+
+ for(std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin();ighit != m_UniqueGOTargetInfo.end();++ighit)
+ *data << uint64(ighit->targetGUID); // Always hits
+
+ *data << (uint8)m_countOfMiss;
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if( ihit->missCondition != SPELL_MISS_NONE ) // Add only miss
+ {
+ *data << uint64(ihit->targetGUID);
+ *data << uint8(ihit->missCondition);
+ if( ihit->missCondition == SPELL_MISS_REFLECT )
+ *data << uint8(ihit->reflectResult);
+ }
+ }
+}
+
+void Spell::SendLogExecute()
+{
+ Unit *target = m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster;
+
+ WorldPacket data(SMSG_SPELLLOGEXECUTE, (8+4+4+4+4+8));
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ data.append(m_caster->GetPackGUID());
+ else
+ data.append(target->GetPackGUID());
+
+ data << uint32(m_spellInfo->Id);
+ uint32 count1 = 1;
+ data << uint32(count1); // count1 (effect count?)
+ for(uint32 i = 0; i < count1; ++i)
+ {
+ data << uint32(m_spellInfo->Effect[0]); // spell effect?
+ uint32 count2 = 1;
+ data << uint32(count2); // count2 (target count?)
+ for(uint32 j = 0; j < count2; ++j)
+ {
+ switch(m_spellInfo->Effect[0])
+ {
+ case SPELL_EFFECT_POWER_DRAIN:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << float(0);
+ break;
+ case SPELL_EFFECT_ADD_EXTRA_ATTACKS:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ data << uint32(0); // count?
+ break;
+ case SPELL_EFFECT_INTERRUPT_CAST:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ data << uint32(0); // spellid
+ break;
+ case SPELL_EFFECT_DURABILITY_DAMAGE:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ data << uint32(0);
+ data << uint32(0);
+ break;
+ case SPELL_EFFECT_OPEN_LOCK:
+ case SPELL_EFFECT_OPEN_LOCK_ITEM:
+ if(Item *item = m_targets.getItemTarget())
+ data.append(item->GetPackGUID());
+ else
+ data << uint8(0);
+ break;
+ case SPELL_EFFECT_CREATE_ITEM:
+ data << uint32(m_spellInfo->EffectItemType[0]);
+ break;
+ case SPELL_EFFECT_SUMMON:
+ case SPELL_EFFECT_SUMMON_WILD:
+ case SPELL_EFFECT_SUMMON_GUARDIAN:
+ case SPELL_EFFECT_TRANS_DOOR:
+ case SPELL_EFFECT_SUMMON_PET:
+ case SPELL_EFFECT_SUMMON_POSSESSED:
+ case SPELL_EFFECT_SUMMON_TOTEM:
+ case SPELL_EFFECT_SUMMON_OBJECT_WILD:
+ case SPELL_EFFECT_CREATE_HOUSE:
+ case SPELL_EFFECT_DUEL:
+ case SPELL_EFFECT_SUMMON_TOTEM_SLOT1:
+ case SPELL_EFFECT_SUMMON_TOTEM_SLOT2:
+ case SPELL_EFFECT_SUMMON_TOTEM_SLOT3:
+ case SPELL_EFFECT_SUMMON_TOTEM_SLOT4:
+ case SPELL_EFFECT_SUMMON_PHANTASM:
+ case SPELL_EFFECT_SUMMON_CRITTER:
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT1:
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT2:
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT3:
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT4:
+ case SPELL_EFFECT_SUMMON_DEMON:
+ case SPELL_EFFECT_150:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else if(m_targets.getItemTargetGUID())
+ data.appendPackGUID(m_targets.getItemTargetGUID());
+ else if(GameObject *go = m_targets.getGOTarget())
+ data.append(go->GetPackGUID());
+ else
+ data << uint8(0); // guid
+ break;
+ case SPELL_EFFECT_FEED_PET:
+ data << uint32(m_targets.getItemTargetEntry());
+ break;
+ case SPELL_EFFECT_DISMISS_PET:
+ if(Unit *unit = m_targets.getUnitTarget())
+ data.append(unit->GetPackGUID());
+ else
+ data << uint8(0);
+ break;
+ default:
+ return;
+ }
+ }
+ }
+
+ m_caster->SendMessageToSet(&data, true);
+}
+
+void Spell::SendInterrupted(uint8 result)
+{
+ WorldPacket data(SMSG_SPELL_FAILURE, (8+4+1));
+ data.append(m_caster->GetPackGUID());
+ data << m_spellInfo->Id;
+ data << result;
+ m_caster->SendMessageToSet(&data, true);
+
+ data.Initialize(SMSG_SPELL_FAILED_OTHER, (8+4));
+ data.append(m_caster->GetPackGUID());
+ data << m_spellInfo->Id;
+ m_caster->SendMessageToSet(&data, true);
+}
+
+void Spell::SendChannelUpdate(uint32 time)
+{
+ if(time == 0)
+ {
+ m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,0);
+ m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL,0);
+ }
+
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data( MSG_CHANNEL_UPDATE, 8+4 );
+ data.append(m_caster->GetPackGUID());
+ data << time;
+
+ ((Player*)m_caster)->GetSession()->SendPacket( &data );
+}
+
+void Spell::SendChannelStart(uint32 duration)
+{
+ WorldObject* target = NULL;
+
+ // select first not rsusted target from target list for _0_ effect
+ if(!m_UniqueTargetInfo.empty())
+ {
+ for(std::list<TargetInfo>::iterator itr= m_UniqueTargetInfo.begin();itr != m_UniqueTargetInfo.end();++itr)
+ {
+ if( (itr->effectMask & (1<<0)) && itr->reflectResult==SPELL_MISS_NONE && itr->targetGUID != m_caster->GetGUID())
+ {
+ target = ObjectAccessor::GetUnit(*m_caster, itr->targetGUID);
+ break;
+ }
+ }
+ }
+ else if(!m_UniqueGOTargetInfo.empty())
+ {
+ for(std::list<GOTargetInfo>::iterator itr= m_UniqueGOTargetInfo.begin();itr != m_UniqueGOTargetInfo.end();++itr)
+ {
+ if(itr->effectMask & (1<<0) )
+ {
+ target = ObjectAccessor::GetGameObject(*m_caster, itr->targetGUID);
+ break;
+ }
+ }
+ }
+
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data( MSG_CHANNEL_START, (8+4+4) );
+ data.append(m_caster->GetPackGUID());
+ data << m_spellInfo->Id;
+ data << duration;
+
+ ((Player*)m_caster)->GetSession()->SendPacket( &data );
+ }
+
+ m_timer = duration;
+ if(target)
+ m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, target->GetGUID());
+ m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL, m_spellInfo->Id);
+}
+
+void Spell::SendResurrectRequest(Player* target)
+{
+ WorldPacket data(SMSG_RESURRECT_REQUEST, (8+4+2+4));
+ data << m_caster->GetGUID();
+ data << uint32(1) << uint16(0) << uint32(1);
+
+ target->GetSession()->SendPacket(&data);
+}
+
+void Spell::SendPlaySpellVisual(uint32 SpellID)
+{
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 12);
+ data << m_caster->GetGUID();
+ data << SpellID;
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+}
+
+void Spell::TakeCastItem()
+{
+ if(!m_CastItem || m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // not remove cast item at triggered spell (equipping, weapon damage, etc)
+ if(m_IsTriggeredSpell)
+ return;
+
+ ItemPrototype const *proto = m_CastItem->GetProto();
+
+ if(!proto)
+ {
+ // This code is to avoid a crash
+ // I'm not sure, if this is really an error, but I guess every item needs a prototype
+ sLog.outError("Cast item has no item prototype highId=%d, lowId=%d",m_CastItem->GetGUIDHigh(), m_CastItem->GetGUIDLow());
+ return;
+ }
+
+ bool expendable = false;
+ bool withoutCharges = false;
+
+ for (int i = 0; i<5; i++)
+ {
+ if (proto->Spells[i].SpellId)
+ {
+ // item has limited charges
+ if (proto->Spells[i].SpellCharges)
+ {
+ if (proto->Spells[i].SpellCharges < 0)
+ expendable = true;
+
+ int32 charges = m_CastItem->GetSpellCharges(i);
+
+ // item has charges left
+ if (charges)
+ {
+ (charges > 0) ? --charges : ++charges; // abs(charges) less at 1 after use
+ if (proto->Stackable < 2)
+ m_CastItem->SetSpellCharges(i, charges);
+ m_CastItem->SetState(ITEM_CHANGED, (Player*)m_caster);
+ }
+
+ // all charges used
+ withoutCharges = (charges == 0);
+ }
+ }
+ }
+
+ if (expendable && withoutCharges)
+ {
+ uint32 count = 1;
+ ((Player*)m_caster)->DestroyItemCount(m_CastItem, count, true);
+
+ // prevent crash at access to deleted m_targets.getItemTarget
+ if(m_CastItem==m_targets.getItemTarget())
+ m_targets.setItemTarget(NULL);
+
+ m_CastItem = NULL;
+ }
+}
+
+void Spell::TakePower()
+{
+ if(m_CastItem || m_triggeredByAuraSpell)
+ return;
+
+ // health as power used
+ if(m_spellInfo->powerType == POWER_HEALTH)
+ {
+ m_caster->ModifyHealth( -(int32)m_powerCost );
+ return;
+ }
+
+ if(m_spellInfo->powerType >= MAX_POWERS)
+ {
+ sLog.outError("Spell::TakePower: Unknown power type '%d'", m_spellInfo->powerType);
+ return;
+ }
+
+ Powers powerType = Powers(m_spellInfo->powerType);
+
+ m_caster->ModifyPower(powerType, -(int32)m_powerCost);
+
+ // Set the five second timer
+ if (powerType == POWER_MANA && m_powerCost > 0)
+ m_caster->SetLastManaUse(getMSTime());
+}
+
+void Spell::TakeReagents()
+{
+ if(m_IsTriggeredSpell) // reagents used in triggered spell removed by original spell or don't must be removed.
+ return;
+
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if (m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP &&
+ m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))
+ return;
+
+ Player* p_caster = (Player*)m_caster;
+
+ for(uint32 x=0;x<8;x++)
+ {
+ if(m_spellInfo->Reagent[x] <= 0)
+ continue;
+
+ uint32 itemid = m_spellInfo->Reagent[x];
+ uint32 itemcount = m_spellInfo->ReagentCount[x];
+
+ // if CastItem is also spell reagent
+ if (m_CastItem)
+ {
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if( proto && proto->ItemId == itemid )
+ {
+ for(int s=0;s<5;s++)
+ {
+ // CastItem will be used up and does not count as reagent
+ int32 charges = m_CastItem->GetSpellCharges(s);
+ if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2)
+ {
+ ++itemcount;
+ break;
+ }
+ }
+
+ m_CastItem = NULL;
+ }
+ }
+
+ // if getItemTarget is also spell reagent
+ if (m_targets.getItemTargetEntry()==itemid)
+ m_targets.setItemTarget(NULL);
+
+ p_caster->DestroyItemCount(itemid, itemcount, true);
+ }
+}
+
+void Spell::HandleThreatSpells(uint32 spellId)
+{
+ if(!m_targets.getUnitTarget() || !spellId)
+ return;
+
+ if(!m_targets.getUnitTarget()->CanHaveThreatList())
+ return;
+
+ SpellThreatEntry const *threatSpell = sSpellThreatStore.LookupEntry<SpellThreatEntry>(spellId);
+ if(!threatSpell)
+ return;
+
+ m_targets.getUnitTarget()->AddThreat(m_caster, float(threatSpell->threat));
+
+ DEBUG_LOG("Spell %u, rank %u, added an additional %i threat", spellId, spellmgr.GetSpellRank(spellId), threatSpell->threat);
+}
+
+void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i, float DamageMultiplier)
+{
+ unitTarget = pUnitTarget;
+ itemTarget = pItemTarget;
+ gameObjTarget = pGOTarget;
+
+ uint8 eff = m_spellInfo->Effect[i];
+ uint32 mechanic = m_spellInfo->EffectMechanic[i];
+
+ damage = int32(CalculateDamage((uint8)i,unitTarget)*DamageMultiplier);
+
+ sLog.outDebug( "Spell: Effect : %u", eff);
+
+ //Simply return. Do not display "immune" in red text on client
+ if(unitTarget && unitTarget->IsImmunedToSpellEffect(eff, mechanic))
+ return;
+
+ if(eff<TOTAL_SPELL_EFFECTS)
+ {
+ //sLog.outDebug( "WORLD: Spell FX %d < TOTAL_SPELL_EFFECTS ", eff);
+ (*this.*SpellEffects[eff])(i);
+ }
+ /*
+ else
+ {
+ sLog.outDebug( "WORLD: Spell FX %d > TOTAL_SPELL_EFFECTS ", eff);
+ if (m_CastItem)
+ EffectEnchantItemTmp(i);
+ else
+ {
+ sLog.outError("SPELL: unknown effect %u spell id %u\n",
+ eff, m_spellInfo->Id);
+ }
+ }
+ */
+}
+
+void Spell::TriggerSpell()
+{
+ for(TriggerSpells::iterator si=m_TriggerSpells.begin(); si!=m_TriggerSpells.end(); ++si)
+ {
+ Spell* spell = new Spell(m_caster, (*si), true, m_originalCasterGUID, this->m_selfContainer);
+ spell->prepare(&m_targets); // use original spell original targets
+ }
+}
+
+uint8 Spell::CanCast(bool strict)
+{
+ // check cooldowns to prevent cheating
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id))
+ {
+ if(m_triggeredByAuraSpell)
+ return SPELL_FAILED_DONT_REPORT;
+ else
+ return SPELL_FAILED_NOT_READY;
+ }
+
+ // only allow triggered spells if at an ended battleground
+ if( !m_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER)
+ if(BattleGround * bg = ((Player*)m_caster)->GetBattleGround())
+ if(bg->GetStatus() == STATUS_WAIT_LEAVE)
+ return SPELL_FAILED_DONT_REPORT;
+
+ // only check at first call, Stealth auras are already removed at second call
+ // for now, ignore triggered spells
+ if( strict && !m_IsTriggeredSpell)
+ {
+ // Cannot be used in this stance/form
+ if(uint8 shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form))
+ return shapeError;
+
+ if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura()))
+ return SPELL_FAILED_ONLY_STEALTHED;
+ }
+
+ // caster state requirements
+ if(m_spellInfo->CasterAuraState && !m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraState)))
+ return SPELL_FAILED_CASTER_AURASTATE;
+ if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot)))
+ return SPELL_FAILED_CASTER_AURASTATE;
+
+ // cancel autorepeat spells if cast start when moving
+ // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code)
+ if( m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->isMoving() )
+ {
+ // skip stuck spell to allow use it in falling case and apply spell limitations at movement
+ if( (!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING) || m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK) &&
+ (IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0) )
+ return SPELL_FAILED_MOVING;
+ }
+
+ Unit *target = m_targets.getUnitTarget();
+
+ if(target)
+ {
+ // target state requirements (not allowed state), apply to self also
+ if(m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot)))
+ return SPELL_FAILED_TARGET_AURASTATE;
+
+
+ if(target != m_caster)
+ {
+ // target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds
+ if(m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState)))
+ return SPELL_FAILED_TARGET_AURASTATE;
+
+ // Not allow casting on flying player
+ if (target->isInFlight())
+ return SPELL_FAILED_BAD_TARGETS;
+
+ if(VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target))
+ return SPELL_FAILED_LINE_OF_SIGHT;
+
+ // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode
+ // this case can be triggered if rank not found (too low-level target for first rank)
+ if(m_caster->GetTypeId() == TYPEID_PLAYER && !IsPassiveSpell(m_spellInfo->Id) && !m_CastItem)
+ {
+ for(int i=0;i<3;i++)
+ {
+ if(IsPositiveEffect(m_spellInfo->Id, i) && m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA)
+ if(target->getLevel() + 10 < m_spellInfo->spellLevel)
+ return SPELL_FAILED_LOWLEVEL;
+ }
+ }
+ }
+
+ // check pet presents
+ for(int j=0;j<3;j++)
+ {
+ if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_PET)
+ {
+ target = m_caster->GetPet();
+ if(!target)
+ {
+ if(m_triggeredByAuraSpell) // not report pet not existence for triggered spells
+ return SPELL_FAILED_DONT_REPORT;
+ else
+ return SPELL_FAILED_NO_PET;
+ }
+ break;
+ }
+ }
+
+ //check creature type
+ //ignore self casts (including area casts when caster selected as target)
+ if(target != m_caster)
+ {
+ if(!CheckTargetCreatureType(target))
+ {
+ if(target->GetTypeId()==TYPEID_PLAYER)
+ return SPELL_FAILED_TARGET_IS_PLAYER;
+ else
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ }
+
+ // TODO: this check can be applied and for player to prevent cheating when IsPositiveSpell will return always correct result.
+ // check target for pet/charmed casts (not self targeted), self targeted cast used for area effects and etc
+ if(m_caster != target && m_caster->GetTypeId()==TYPEID_UNIT && m_caster->GetCharmerOrOwnerGUID())
+ {
+ // check correctness positive/negative cast target (pet cast real check and cheating check)
+ if(IsPositiveSpell(m_spellInfo->Id))
+ {
+ if(m_caster->IsHostileTo(target))
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ else
+ {
+ if(m_caster->IsFriendlyTo(target))
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ }
+
+ if(IsPositiveSpell(m_spellInfo->Id))
+ {
+ if(target->IsImmunedToSpell(m_spellInfo,false))
+ return SPELL_FAILED_TARGET_AURASTATE;
+ }
+
+ //Must be behind the target.
+ if( m_spellInfo->AttributesEx2 == 0x100000 && (m_spellInfo->AttributesEx & 0x200) == 0x200 && target->HasInArc(M_PI, m_caster) )
+ {
+ SendInterrupted(2);
+ return SPELL_FAILED_NOT_BEHIND;
+ }
+
+ //Target must be facing you.
+ if((m_spellInfo->Attributes == 0x150010) && !target->HasInArc(M_PI, m_caster) )
+ {
+ SendInterrupted(2);
+ return SPELL_FAILED_NOT_INFRONT;
+ }
+
+ // check if target is in combat
+ if (target != m_caster && (m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET) && target->isInCombat())
+ {
+ return SPELL_FAILED_TARGET_AFFECTING_COMBAT;
+ }
+ }
+ // Spell casted only on battleground
+ if((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND) && m_caster->GetTypeId()==TYPEID_PLAYER)
+ if(!((Player*)m_caster)->InBattleGround())
+ return SPELL_FAILED_ONLY_BATTLEGROUNDS;
+
+ // do not allow spells to be cast in arenas
+ // - with greater than 15 min CD without SPELL_ATTR_EX4_USABLE_IN_ARENA flag
+ // - with SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA flag
+ if( (m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) ||
+ GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * 1000 && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) )
+ if(MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId()))
+ if(mapEntry->IsBattleArena())
+ return SPELL_FAILED_NOT_IN_ARENA;
+
+ // zone check
+ if(!IsSpellAllowedInLocation(m_spellInfo,m_caster->GetMapId(),m_caster->GetZoneId(),m_caster->GetAreaId()))
+ return SPELL_FAILED_REQUIRES_AREA;
+
+ // not let players cast spells at mount (and let do it to creatures)
+ if( m_caster->IsMounted() && m_caster->GetTypeId()==TYPEID_PLAYER && !m_IsTriggeredSpell &&
+ !IsPassiveSpell(m_spellInfo->Id) && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_MOUNTED) )
+ {
+ if(m_caster->isInFlight())
+ return SPELL_FAILED_NOT_FLYING;
+ else
+ return SPELL_FAILED_NOT_MOUNTED;
+ }
+
+ // always (except passive spells) check items (focus object can be required for any type casts)
+ if(!IsPassiveSpell(m_spellInfo->Id))
+ if(uint8 castResult = CheckItems())
+ return castResult;
+
+ //ImpliciteTargetA-B = 38, If fact there is 0 Spell with ImpliciteTargetB=38
+ if(m_UniqueTargetInfo.empty()) // skip second canCast apply (for delayed spells for example)
+ {
+ for(uint8 j = 0; j < 3; j++)
+ {
+ if( m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT && m_spellInfo->EffectImplicitTargetA[j] != TARGET_SELF ||
+ m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
+ {
+ bool okDoo = false;
+
+ SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(m_spellInfo->Id);
+ SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id);
+ if(lower==upper)
+ sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = TARGET_SCRIPT or TARGET_SCRIPT_COORDINATES, but does not have record in `spell_script_target`",m_spellInfo->Id);
+
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
+ float range = GetSpellMaxRange(srange);
+
+ Creature* creatureScriptTarget = NULL;
+ GameObject* goScriptTarget = NULL;
+
+ for(SpellScriptTarget::const_iterator i_spellST = lower; i_spellST != upper; ++i_spellST)
+ {
+ switch(i_spellST->second.type)
+ {
+ case SPELL_TARGET_TYPE_GAMEOBJECT:
+ {
+ GameObject* p_GameObject = NULL;
+
+ if(i_spellST->second.targetEntry)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range);
+ MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(p_GameObject,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+
+ if(p_GameObject)
+ {
+ // remember found target and range, next attempt will find more near target with another entry
+ creatureScriptTarget = NULL;
+ goScriptTarget = p_GameObject;
+ range = go_check.GetLastRange();
+ }
+ }
+ else if( focusObject ) //Focus Object
+ {
+ float frange = m_caster->GetDistance(focusObject);
+ if(range >= frange)
+ {
+ creatureScriptTarget = NULL;
+ goScriptTarget = focusObject;
+ range = frange;
+ }
+ }
+ break;
+ }
+ case SPELL_TARGET_TYPE_CREATURE:
+ case SPELL_TARGET_TYPE_DEAD:
+ default:
+ {
+ Creature *p_Creature = NULL;
+
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate(); // Really don't know what is that???
+
+ MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range);
+ MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(p_Creature, u_check);
+
+ TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_creature_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+
+ if(p_Creature )
+ {
+ creatureScriptTarget = p_Creature;
+ goScriptTarget = NULL;
+ range = u_check.GetLastRange();
+ }
+ break;
+ }
+ }
+ }
+
+ if(creatureScriptTarget)
+ {
+ // store coordinates for TARGET_SCRIPT_COORDINATES
+ if (m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
+ {
+ m_targets.setDestination(creatureScriptTarget->GetPositionX(),creatureScriptTarget->GetPositionY(),creatureScriptTarget->GetPositionZ());
+
+ if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES && m_spellInfo->EffectImplicitTargetB[j] == 0 && m_spellInfo->Effect[j]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ AddUnitTarget(creatureScriptTarget, j);
+ }
+ // store explicit target for TARGET_SCRIPT
+ else
+ AddUnitTarget(creatureScriptTarget, j);
+ }
+ else if(goScriptTarget)
+ {
+ // store coordinates for TARGET_SCRIPT_COORDINATES
+ if (m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
+ {
+ m_targets.setDestination(goScriptTarget->GetPositionX(),goScriptTarget->GetPositionY(),goScriptTarget->GetPositionZ());
+
+ if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES && m_spellInfo->EffectImplicitTargetB[j] == 0 && m_spellInfo->Effect[j]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ AddGOTarget(goScriptTarget, j);
+ }
+ // store explicit target for TARGET_SCRIPT
+ else
+ AddGOTarget(goScriptTarget, j);
+ }
+ //Missing DB Entry or targets for this spellEffect.
+ else
+ {
+ // not report target not existence for triggered spells
+ if(m_triggeredByAuraSpell || m_IsTriggeredSpell)
+ return SPELL_FAILED_DONT_REPORT;
+ else
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ }
+ }
+ }
+
+ if(uint8 castResult = CheckRange(strict))
+ return castResult;
+
+ {
+ if(uint8 castResult = CheckPower())
+ return castResult;
+ }
+
+ if(!m_triggeredByAuraSpell) // triggered spell not affected by stun/etc
+ if(uint8 castResult = CheckCasterAuras())
+ return castResult;
+
+ for (int i = 0; i < 3; i++)
+ {
+ // for effects of spells that have only one target
+ switch(m_spellInfo->Effect[i])
+ {
+ case SPELL_EFFECT_DUMMY:
+ {
+ if(m_spellInfo->SpellIconID == 1648) // Execute
+ {
+ if(!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2)
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ else if (m_spellInfo->Id == 51582) // Rocket Boots Engaged
+ {
+ if(m_caster->IsInWater())
+ return SPELL_FAILED_ONLY_ABOVEWATER;
+ }
+ else if(m_spellInfo->SpellIconID==156) // Holy Shock
+ {
+ // spell different for friends and enemies
+ // hart version required facing
+ if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, target ))
+ return SPELL_FAILED_UNIT_NOT_INFRONT;
+ }
+ break;
+ }
+ case SPELL_EFFECT_SCHOOL_DAMAGE:
+ {
+ // Hammer of Wrath
+ if(m_spellInfo->SpellVisual == 7250)
+ {
+ if (!m_targets.getUnitTarget())
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
+ if(m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth()*0.2)
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ break;
+ }
+ case SPELL_EFFECT_TAMECREATURE:
+ {
+ if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
+ if (m_targets.getUnitTarget()->getLevel() > m_caster->getLevel())
+ return SPELL_FAILED_HIGHLEVEL;
+
+ CreatureInfo const *cinfo = ((Creature*)m_targets.getUnitTarget())->GetCreatureInfo();
+ if( cinfo->type != CREATURE_TYPE_BEAST )
+ return SPELL_FAILED_BAD_TARGETS;
+
+ // use SMSG_PET_TAME_FAILURE?
+ if( !(cinfo->flag1 & 1) || !(cinfo->family) )
+ return SPELL_FAILED_BAD_TARGETS;
+
+ if(m_caster->GetPetGUID())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+
+ break;
+ }
+ case SPELL_EFFECT_LEARN_SPELL:
+ {
+ if(m_spellInfo->EffectImplicitTargetA[i] != TARGET_PET)
+ break;
+
+ Pet* pet = m_caster->GetPet();
+
+ if(!pet)
+ return SPELL_FAILED_NO_PET;
+
+ SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(m_spellInfo->EffectTriggerSpell[i]);
+
+ if(!learn_spellproto)
+ return SPELL_FAILED_NOT_KNOWN;
+
+ if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id))
+ return SPELL_FAILED_TOO_MANY_SKILLS;
+
+ if(m_spellInfo->spellLevel > pet->getLevel())
+ return SPELL_FAILED_LOWLEVEL;
+
+ if(!pet->HasTPForSpell(learn_spellproto->Id))
+ return SPELL_FAILED_TRAINING_POINTS;
+
+ break;
+ }
+ case SPELL_EFFECT_LEARN_PET_SPELL:
+ {
+ Pet* pet = m_caster->GetPet();
+
+ if(!pet)
+ return SPELL_FAILED_NO_PET;
+
+ SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(m_spellInfo->EffectTriggerSpell[i]);
+
+ if(!learn_spellproto)
+ return SPELL_FAILED_NOT_KNOWN;
+
+ if(!pet->CanTakeMoreActiveSpells(learn_spellproto->Id))
+ return SPELL_FAILED_TOO_MANY_SKILLS;
+
+ if(m_spellInfo->spellLevel > pet->getLevel())
+ return SPELL_FAILED_LOWLEVEL;
+
+ if(!pet->HasTPForSpell(learn_spellproto->Id))
+ return SPELL_FAILED_TRAINING_POINTS;
+
+ break;
+ }
+ case SPELL_EFFECT_FEED_PET:
+ {
+ if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getItemTarget() )
+ return SPELL_FAILED_BAD_TARGETS;
+
+ Pet* pet = m_caster->GetPet();
+
+ if(!pet)
+ return SPELL_FAILED_NO_PET;
+
+ if(!pet->HaveInDiet(m_targets.getItemTarget()->GetProto()))
+ return SPELL_FAILED_WRONG_PET_FOOD;
+
+ if(!pet->GetCurrentFoodBenefitLevel(m_targets.getItemTarget()->GetProto()->ItemLevel))
+ return SPELL_FAILED_FOOD_LOWLEVEL;
+
+ if(m_caster->isInCombat() || pet->isInCombat())
+ return SPELL_FAILED_AFFECTING_COMBAT;
+
+ break;
+ }
+ case SPELL_EFFECT_POWER_BURN:
+ case SPELL_EFFECT_POWER_DRAIN:
+ {
+ // Can be area effect, Check only for players and not check if target - caster (spell can have multiply drain/burn effects)
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ if(Unit* target = m_targets.getUnitTarget())
+ if(target!=m_caster && target->getPowerType()!=m_spellInfo->EffectMiscValue[i])
+ return SPELL_FAILED_BAD_TARGETS;
+ break;
+ }
+ case SPELL_EFFECT_CHARGE:
+ {
+ if (m_caster->hasUnitState(UNIT_STAT_ROOT))
+ return SPELL_FAILED_ROOTED;
+
+ break;
+ }
+ case SPELL_EFFECT_SKINNING:
+ {
+ if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() != TYPEID_UNIT)
+ return SPELL_FAILED_BAD_TARGETS;
+
+ if( !(m_targets.getUnitTarget()->GetUInt32Value(UNIT_FIELD_FLAGS) & UNIT_FLAG_SKINNABLE) )
+ return SPELL_FAILED_TARGET_UNSKINNABLE;
+
+ Creature* creature = (Creature*)m_targets.getUnitTarget();
+ if ( creature->GetCreatureType() != CREATURE_TYPE_CRITTER && ( !creature->lootForBody || !creature->loot.empty() ) )
+ {
+ return SPELL_FAILED_TARGET_NOT_LOOTED;
+ }
+
+ uint32 skill;
+ if(creature->GetCreatureInfo()->flag1 & 256)
+ skill = SKILL_HERBALISM; // special case
+ else if(creature->GetCreatureInfo()->flag1 & 512)
+ skill = SKILL_MINING; // special case
+ else
+ skill = SKILL_SKINNING; // normal case
+
+ int32 skillValue = ((Player*)m_caster)->GetSkillValue(skill);
+ int32 TargetLevel = m_targets.getUnitTarget()->getLevel();
+ int32 ReqValue = (skillValue < 100 ? (TargetLevel-10)*10 : TargetLevel*5);
+ if (ReqValue > skillValue)
+ return SPELL_FAILED_LOW_CASTLEVEL;
+
+ // chance for fail at orange skinning attempt
+ if( (m_selfContainer && (*m_selfContainer) == this) &&
+ skillValue < sWorld.GetConfigMaxSkillValue() &&
+ (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue-25, skillValue+37) )
+ return SPELL_FAILED_TRY_AGAIN;
+
+ break;
+ }
+ case SPELL_EFFECT_OPEN_LOCK_ITEM:
+ case SPELL_EFFECT_OPEN_LOCK:
+ {
+ if( m_spellInfo->EffectImplicitTargetA[i] != TARGET_GAMEOBJECT &&
+ m_spellInfo->EffectImplicitTargetA[i] != TARGET_GAMEOBJECT_ITEM )
+ break;
+
+ if( m_caster->GetTypeId() != TYPEID_PLAYER // only players can open locks, gather etc.
+ // we need a go target in case of TARGET_GAMEOBJECT
+ || m_spellInfo->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT && !m_targets.getGOTarget()
+ // we need a go target, or an openable item target in case of TARGET_GAMEOBJECT_ITEM
+ || m_spellInfo->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT_ITEM && !m_targets.getGOTarget() &&
+ (!m_targets.getItemTarget() || !m_targets.getItemTarget()->GetProto()->LockID || m_targets.getItemTarget()->GetOwner() != m_caster ) )
+ return SPELL_FAILED_BAD_TARGETS;
+
+ // In BattleGround players can use only flags and banners
+ if( ((Player*)m_caster)->InBattleGround() &&
+ !((Player*)m_caster)->isAllowUseBattleGroundObject() )
+ return SPELL_FAILED_TRY_AGAIN;
+
+ // get the lock entry
+ LockEntry const *lockInfo = NULL;
+ if (GameObject* go=m_targets.getGOTarget())
+ lockInfo = sLockStore.LookupEntry(go->GetLockId());
+ else if(Item* itm=m_targets.getItemTarget())
+ lockInfo = sLockStore.LookupEntry(itm->GetProto()->LockID);
+
+ // check lock compatibility
+ if (lockInfo)
+ {
+ // check for lock - key pair (checked by client also, just prevent cheating
+ bool ok_key = false;
+ for(int it = 0; it < 5; ++it)
+ {
+ switch(lockInfo->keytype[it])
+ {
+ case LOCK_KEY_NONE:
+ break;
+ case LOCK_KEY_ITEM:
+ {
+ if(lockInfo->key[it])
+ {
+ if(m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it])
+ ok_key =true;
+ break;
+ }
+ }
+ case LOCK_KEY_SKILL:
+ {
+ if(uint32(m_spellInfo->EffectMiscValue[i])!=lockInfo->key[it])
+ break;
+
+ switch(lockInfo->key[it])
+ {
+ case LOCKTYPE_HERBALISM:
+ if(((Player*)m_caster)->HasSkill(SKILL_HERBALISM))
+ ok_key =true;
+ break;
+ case LOCKTYPE_MINING:
+ if(((Player*)m_caster)->HasSkill(SKILL_MINING))
+ ok_key =true;
+ break;
+ default:
+ ok_key =true;
+ break;
+ }
+ }
+ }
+ if(ok_key)
+ break;
+ }
+
+ if(!ok_key)
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+
+ // chance for fail at orange mining/herb/LockPicking gathering attempt
+ if (!m_selfContainer || ((*m_selfContainer) != this))
+ break;
+
+ // get the skill value of the player
+ int32 SkillValue = 0;
+ bool canFailAtMax = true;
+ if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_HERBALISM)
+ {
+ SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_HERBALISM);
+ canFailAtMax = false;
+ }
+ else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_MINING)
+ {
+ SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_MINING);
+ canFailAtMax = false;
+ }
+ else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK)
+ SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_LOCKPICKING);
+
+ // castitem check: rogue using skeleton keys. the skill values should not be added in this case.
+ if(m_CastItem)
+ SkillValue = 0;
+
+ // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value)
+ SkillValue += m_currentBasePoints[i]+1;
+
+ // get the required lock value
+ int32 ReqValue=0;
+ if (lockInfo)
+ {
+ // check for lock - key pair
+ bool ok = false;
+ for(int it = 0; it < 5; ++it)
+ {
+ if(lockInfo->keytype[it]==LOCK_KEY_ITEM && lockInfo->key[it] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[it])
+ {
+ // if so, we're good to go
+ ok = true;
+ break;
+ }
+ }
+ if(ok)
+ break;
+
+ if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK)
+ ReqValue = lockInfo->requiredlockskill;
+ else
+ ReqValue = lockInfo->requiredminingskill;
+ }
+
+ // skill doesn't meet the required value
+ if (ReqValue > SkillValue)
+ return SPELL_FAILED_LOW_CASTLEVEL;
+
+ // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill)
+ if((canFailAtMax || SkillValue < sWorld.GetConfigMaxSkillValue()) && ReqValue > irand(SkillValue-25, SkillValue+37))
+ return SPELL_FAILED_TRY_AGAIN;
+
+ break;
+ }
+ case SPELL_EFFECT_SUMMON_DEAD_PET:
+ {
+ Creature *pet = m_caster->GetPet();
+ if(!pet)
+ return SPELL_FAILED_NO_PET;
+
+ if(pet->isAlive())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ break;
+ }
+ // This is generic summon effect now and don't make this check for summon types similar
+ // SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN.
+ // These won't show up in m_caster->GetPetGUID()
+ case SPELL_EFFECT_SUMMON:
+ {
+ switch(m_spellInfo->EffectMiscValueB[i])
+ {
+ case SUMMON_TYPE_POSESSED:
+ case SUMMON_TYPE_POSESSED2:
+ case SUMMON_TYPE_DEMON:
+ case SUMMON_TYPE_SUMMON:
+ {
+ if(m_caster->GetPetGUID())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+ break;
+ }
+ }
+ break;
+ }
+ // Don't make this check for SPELL_EFFECT_SUMMON_CRITTER, SPELL_EFFECT_SUMMON_WILD or SPELL_EFFECT_SUMMON_GUARDIAN.
+ // These won't show up in m_caster->GetPetGUID()
+ case SPELL_EFFECT_SUMMON_POSSESSED:
+ case SPELL_EFFECT_SUMMON_PHANTASM:
+ case SPELL_EFFECT_SUMMON_DEMON:
+ {
+ if(m_caster->GetPetGUID())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+
+ break;
+ }
+ case SPELL_EFFECT_SUMMON_PET:
+ {
+ if(m_caster->GetPetGUID()) //let warlock do a replacement summon
+ {
+
+ Pet* pet = ((Player*)m_caster)->GetPet();
+
+ if (m_caster->GetTypeId()==TYPEID_PLAYER && m_caster->getClass()==CLASS_WARLOCK)
+ {
+ if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player)
+ pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID());
+ }
+ else
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+ }
+
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+
+ break;
+ }
+ case SPELL_EFFECT_SUMMON_PLAYER:
+ {
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_TARGETS;
+ if(!((Player*)m_caster)->GetSelection())
+ return SPELL_FAILED_BAD_TARGETS;
+
+ Player* target = objmgr.GetPlayer(((Player*)m_caster)->GetSelection());
+ if( !target || ((Player*)m_caster)==target || !target->IsInSameRaidWith((Player*)m_caster) )
+ return SPELL_FAILED_BAD_TARGETS;
+
+ // check if our map is dungeon
+ if( sMapStore.LookupEntry(m_caster->GetMapId())->IsDungeon() )
+ {
+ InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(m_caster->GetMapId());
+ if(!instance)
+ return SPELL_FAILED_TARGET_NOT_IN_INSTANCE;
+ if ( instance->levelMin > target->getLevel() )
+ return SPELL_FAILED_LOWLEVEL;
+ if ( instance->levelMax && instance->levelMax < target->getLevel() )
+ return SPELL_FAILED_HIGHLEVEL;
+ }
+ break;
+ }
+ case SPELL_EFFECT_LEAP:
+ case SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER:
+ {
+ float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ float fx = m_caster->GetPositionX() + dis * cos(m_caster->GetOrientation());
+ float fy = m_caster->GetPositionY() + dis * sin(m_caster->GetOrientation());
+ // teleport a bit above terrain level to avoid falling below it
+ float fz = MapManager::Instance().GetBaseMap(m_caster->GetMapId())->GetHeight(fx,fy,m_caster->GetPositionZ(),true);
+ if(fz <= INVALID_HEIGHT) // note: this also will prevent use effect in instances without vmaps height enabled
+ return SPELL_FAILED_TRY_AGAIN;
+
+ float caster_pos_z = m_caster->GetPositionZ();
+ // Control the caster to not climb or drop when +-fz > 8
+ if(!(fz<=caster_pos_z+8 && fz>=caster_pos_z-8))
+ return SPELL_FAILED_TRY_AGAIN;
+
+ // not allow use this effect at battleground until battleground start
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ if(BattleGround const *bg = ((Player*)m_caster)->GetBattleGround())
+ if(bg->GetStatus() != STATUS_IN_PROGRESS)
+ return SPELL_FAILED_TRY_AGAIN;
+ break;
+ }
+ case SPELL_EFFECT_STEAL_BENEFICIAL_BUFF:
+ {
+ if (m_targets.getUnitTarget()==m_caster)
+ return SPELL_FAILED_BAD_TARGETS;
+ break;
+ }
+ default:break;
+ }
+ }
+
+ for (int i = 0; i < 3; i++)
+ {
+ switch(m_spellInfo->EffectApplyAuraName[i])
+ {
+ case SPELL_AURA_MOD_POSSESS:
+ case SPELL_AURA_MOD_CHARM:
+ {
+ if(m_caster->GetPetGUID())
+ return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
+ if(m_caster->GetCharmGUID())
+ return SPELL_FAILED_ALREADY_HAVE_CHARM;
+
+ if(m_caster->GetCharmerGUID())
+ return SPELL_FAILED_CHARMED;
+
+ if(!m_targets.getUnitTarget())
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
+ if(m_targets.getUnitTarget()->GetCharmerGUID())
+ return SPELL_FAILED_CHARMED;
+
+ if(int32(m_targets.getUnitTarget()->getLevel()) > CalculateDamage(i,m_targets.getUnitTarget()))
+ return SPELL_FAILED_HIGHLEVEL;
+ };break;
+ case SPELL_AURA_MOUNTED:
+ {
+ if (m_caster->IsInWater())
+ return SPELL_FAILED_ONLY_ABOVEWATER;
+
+ if (m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->GetTransport())
+ return SPELL_FAILED_NO_MOUNTS_ALLOWED;
+
+ // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells
+ if (m_caster->GetTypeId()==TYPEID_PLAYER && !sMapStore.LookupEntry(m_caster->GetMapId())->IsMountAllowed() && !m_IsTriggeredSpell && !m_spellInfo->AreaId)
+ return SPELL_FAILED_NO_MOUNTS_ALLOWED;
+
+ if (m_caster->GetAreaId()==35)
+ return SPELL_FAILED_NO_MOUNTS_ALLOWED;
+
+ ShapeshiftForm form = m_caster->m_form;
+ if( form == FORM_CAT || form == FORM_TREE || form == FORM_TRAVEL ||
+ form == FORM_AQUA || form == FORM_BEAR || form == FORM_DIREBEAR ||
+ form == FORM_CREATUREBEAR || form == FORM_GHOSTWOLF || form == FORM_FLIGHT ||
+ form == FORM_FLIGHT_EPIC || form == FORM_MOONKIN )
+ return SPELL_FAILED_NOT_SHAPESHIFT;
+
+ break;
+ }
+ case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS:
+ {
+ if(!m_targets.getUnitTarget())
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
+ // can be casted at non-friendly unit or own pet/charm
+ if(m_caster->IsFriendlyTo(m_targets.getUnitTarget()))
+ return SPELL_FAILED_TARGET_FRIENDLY;
+ };break;
+ case SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED:
+ case SPELL_AURA_FLY:
+ {
+ // not allow cast fly spells at old maps by players (all spells is self target)
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if( !((Player*)m_caster)->isGameMaster() &&
+ GetVirtualMapForMapAndZone(m_caster->GetMapId(),m_caster->GetZoneId()) != 530)
+ return SPELL_FAILED_NOT_HERE;
+ }
+ };break;
+ case SPELL_AURA_PERIODIC_MANA_LEECH:
+ {
+ if (!m_targets.getUnitTarget())
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
+ if (m_caster->GetTypeId()!=TYPEID_PLAYER || m_CastItem)
+ break;
+
+ if(m_targets.getUnitTarget()->getPowerType()!=POWER_MANA)
+ return SPELL_FAILED_BAD_TARGETS;
+ break;
+ }
+ default:break;
+ }
+ }
+
+ // all ok
+ return 0;
+}
+
+int16 Spell::PetCanCast(Unit* target)
+{
+ if(!m_caster->isAlive())
+ return SPELL_FAILED_CASTER_DEAD;
+
+ if(m_caster->IsNonMeleeSpellCasted(false)) //prevent spellcast interuption by another spellcast
+ return SPELL_FAILED_SPELL_IN_PROGRESS;
+ if(m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo))
+ return SPELL_FAILED_AFFECTING_COMBAT;
+
+ if(m_caster->GetTypeId()==TYPEID_UNIT && (((Creature*)m_caster)->isPet() || m_caster->isCharmed()))
+ {
+ //dead owner (pets still alive when owners ressed?)
+ if(m_caster->GetCharmerOrOwner() && !m_caster->GetCharmerOrOwner()->isAlive())
+ return SPELL_FAILED_CASTER_DEAD;
+
+ if(!target && m_targets.getUnitTarget())
+ target = m_targets.getUnitTarget();
+
+ bool need = false;
+ for(uint32 i = 0;i<3;i++)
+ {
+ if(m_spellInfo->EffectImplicitTargetA[i] == TARGET_CHAIN_DAMAGE || m_spellInfo->EffectImplicitTargetA[i] == TARGET_SINGLE_FRIEND || m_spellInfo->EffectImplicitTargetA[i] == TARGET_DUELVSPLAYER || m_spellInfo->EffectImplicitTargetA[i] == TARGET_SINGLE_PARTY || m_spellInfo->EffectImplicitTargetA[i] == TARGET_CURRENT_ENEMY_COORDINATES)
+ {
+ need = true;
+ if(!target)
+ return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+ break;
+ }
+ }
+ if(need)
+ m_targets.setUnitTarget(target);
+
+ Unit* _target = m_targets.getUnitTarget();
+
+ if(_target) //for target dead/target not valid
+ {
+ if(!_target->isAlive())
+ return SPELL_FAILED_BAD_TARGETS;
+
+ if(IsPositiveSpell(m_spellInfo->Id))
+ {
+ if(m_caster->IsHostileTo(_target))
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ else
+ {
+ bool duelvsplayertar = false;
+ for(int j=0;j<3;j++)
+ {
+ //TARGET_DUELVSPLAYER is positive AND negative
+ duelvsplayertar |= (m_spellInfo->EffectImplicitTargetA[j] == TARGET_DUELVSPLAYER);
+ }
+ if(m_caster->IsFriendlyTo(target) && !duelvsplayertar)
+ {
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ }
+ }
+ //cooldown
+ if(((Creature*)m_caster)->HasSpellCooldown(m_spellInfo->Id))
+ return SPELL_FAILED_NOT_READY;
+ }
+
+ uint16 result = CanCast(true);
+ if(result != 0)
+ return result;
+ else
+ return -1; //this allows to check spell fail 0, in combat
+}
+
+uint8 Spell::CheckCasterAuras() const
+{
+ // Flag drop spells totally immuned to caster auras
+ // FIXME: find more nice check for all totally immuned spells
+ // AttributesEx3 & 0x10000000?
+ if(m_spellInfo->Id==23336 || m_spellInfo->Id==23334 || m_spellInfo->Id==34991)
+ return 0;
+
+ uint8 school_immune = 0;
+ uint32 mechanic_immune = 0;
+ uint32 dispel_immune = 0;
+
+ //Check if the spell grants school or mechanic immunity.
+ //We use bitmasks so the loop is done only once and not on every aura check below.
+ if ( m_spellInfo->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY )
+ {
+ for(int i = 0;i < 3; i ++)
+ {
+ if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_SCHOOL_IMMUNITY)
+ school_immune |= uint32(m_spellInfo->EffectMiscValue[i]);
+ else if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MECHANIC_IMMUNITY)
+ mechanic_immune |= 1 << uint32(m_spellInfo->EffectMiscValue[i]);
+ else if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_DISPEL_IMMUNITY)
+ dispel_immune |= GetDispellMask(DispelType(m_spellInfo->EffectMiscValue[i]));
+ }
+ //immune movement impairement and loss of control
+ if(m_spellInfo->Id==(uint32)42292)
+ mechanic_immune = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
+ }
+
+ //Check whether the cast should be prevented by any state you might have.
+ uint8 prevented_reason = 0;
+ // Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out
+ if(!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED) && m_caster->HasAuraType(SPELL_AURA_MOD_STUN))
+ prevented_reason = SPELL_FAILED_STUNNED;
+ else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED))
+ prevented_reason = SPELL_FAILED_CONFUSED;
+ else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING) && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED))
+ prevented_reason = SPELL_FAILED_FLEEING;
+ else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE)
+ prevented_reason = SPELL_FAILED_SILENCED;
+ else if(m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) && m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY)
+ prevented_reason = SPELL_FAILED_PACIFIED;
+
+ // Attr must make flag drop spell totally immuned from all effects
+ if(prevented_reason)
+ {
+ if(school_immune || mechanic_immune || dispel_immune)
+ {
+ //Checking auras is needed now, because you are prevented by some state but the spell grants immunity.
+ Unit::AuraMap const& auras = m_caster->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); itr++)
+ {
+ if(itr->second)
+ {
+ if( GetSpellMechanicMask(itr->second->GetSpellProto(), itr->second->GetEffIndex()) & mechanic_immune )
+ continue;
+ if( GetSpellSchoolMask(itr->second->GetSpellProto()) & school_immune )
+ continue;
+ if( (1<<(itr->second->GetSpellProto()->Dispel)) & dispel_immune)
+ continue;
+
+ //Make a second check for spell failed so the right SPELL_FAILED message is returned.
+ //That is needed when your casting is prevented by multiple states and you are only immune to some of them.
+ switch(itr->second->GetModifier()->m_auraname)
+ {
+ case SPELL_AURA_MOD_STUN:
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED))
+ return SPELL_FAILED_STUNNED;
+ break;
+ case SPELL_AURA_MOD_CONFUSE:
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED))
+ return SPELL_FAILED_CONFUSED;
+ break;
+ case SPELL_AURA_MOD_FEAR:
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_FEARED))
+ return SPELL_FAILED_FLEEING;
+ break;
+ case SPELL_AURA_MOD_SILENCE:
+ case SPELL_AURA_MOD_PACIFY:
+ case SPELL_AURA_MOD_PACIFY_SILENCE:
+ if( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_PACIFY)
+ return SPELL_FAILED_PACIFIED;
+ else if ( m_spellInfo->PreventionType==SPELL_PREVENTION_TYPE_SILENCE)
+ return SPELL_FAILED_SILENCED;
+ break;
+ }
+ }
+ }
+ }
+ //You are prevented from casting and the spell casted does not grant immunity. Return a failed error.
+ else
+ return prevented_reason;
+ }
+ return 0; // all ok
+}
+
+bool Spell::CanAutoCast(Unit* target)
+{
+ uint64 targetguid = target->GetGUID();
+
+ for(uint32 j = 0;j<3;j++)
+ {
+ if(m_spellInfo->Effect[j] == SPELL_EFFECT_APPLY_AURA)
+ {
+ if( m_spellInfo->StackAmount <= 1)
+ {
+ if( target->HasAura(m_spellInfo->Id, j) )
+ return false;
+ }
+ else
+ {
+ if( target->GetAuras().count(Unit::spellEffectPair(m_spellInfo->Id, j)) >= m_spellInfo->StackAmount)
+ return false;
+ }
+ }
+ else if ( IsAreaAuraEffect( m_spellInfo->Effect[j] ))
+ {
+ if( target->HasAura(m_spellInfo->Id, j) )
+ return false;
+ }
+ }
+
+ int16 result = PetCanCast(target);
+
+ if(result == -1 || result == SPELL_FAILED_UNIT_NOT_INFRONT)
+ {
+ FillTargetMap();
+ //check if among target units, our WANTED target is as well (->only self cast spells return false)
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ if( ihit->targetGUID == targetguid )
+ return true;
+ }
+ return false; //target invalid
+}
+
+uint8 Spell::CheckRange(bool strict)
+{
+ float range_mod;
+
+ // self cast doesn't need range checking -- also for Starshards fix
+ if (m_spellInfo->rangeIndex == 1) return 0;
+
+ if (strict) //add radius of caster
+ range_mod = 1.25;
+ else //add radius of caster and ~5 yds "give"
+ range_mod = 6.25;
+
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
+ float max_range = GetSpellMaxRange(srange) + range_mod;
+ float min_range = GetSpellMinRange(srange);
+
+ if(Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this);
+
+ Unit *target = m_targets.getUnitTarget();
+
+ if(target && target != m_caster)
+ {
+ // distance from target center in checks
+ float dist = m_caster->GetDistance(target->GetPositionX(),target->GetPositionY(),target->GetPositionZ());
+ if(dist > max_range)
+ return SPELL_FAILED_OUT_OF_RANGE; //0x5A;
+ if(dist < min_range)
+ return SPELL_FAILED_TOO_CLOSE;
+ if( m_caster->GetTypeId() == TYPEID_PLAYER &&
+ (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc( M_PI, target ) )
+ return SPELL_FAILED_UNIT_NOT_INFRONT;
+ }
+
+ if(m_targets.m_targetMask == TARGET_FLAG_DEST_LOCATION && m_targets.m_destX != 0 && m_targets.m_destY != 0 && m_targets.m_destZ != 0)
+ {
+ float dist = m_caster->GetDistance(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ);
+ if(dist > max_range)
+ return SPELL_FAILED_OUT_OF_RANGE;
+ if(dist < min_range)
+ return SPELL_FAILED_TOO_CLOSE;
+ }
+
+ return 0; // ok
+}
+
+int32 Spell::CalculatePowerCost()
+{
+ // item cast not used power
+ if(m_CastItem)
+ return 0;
+
+ // Spell drain all exist power on cast (Only paladin lay of Hands)
+ if (m_spellInfo->AttributesEx & SPELL_ATTR_EX_DRAIN_ALL_POWER)
+ {
+ // If power type - health drain all
+ if (m_spellInfo->powerType == POWER_HEALTH)
+ return m_caster->GetHealth();
+ // Else drain all power
+ if (m_spellInfo->powerType < MAX_POWERS)
+ return m_caster->GetPower(Powers(m_spellInfo->powerType));
+ sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", m_spellInfo->powerType, m_spellInfo->Id);
+ return 0;
+ }
+
+ // Base powerCost
+ int32 powerCost = m_spellInfo->manaCost;
+ // PCT cost from total amount
+ if (m_spellInfo->ManaCostPercentage)
+ {
+ switch (m_spellInfo->powerType)
+ {
+ // health as power used
+ case POWER_HEALTH:
+ powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetCreateHealth() / 100;
+ break;
+ case POWER_MANA:
+ powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetCreateMana() / 100;
+ break;
+ case POWER_RAGE:
+ case POWER_FOCUS:
+ case POWER_ENERGY:
+ case POWER_HAPPINESS:
+ // case POWER_RUNES:
+ powerCost += m_spellInfo->ManaCostPercentage * m_caster->GetMaxPower(Powers(m_spellInfo->powerType)) / 100;
+ break;
+ default:
+ sLog.outError("Spell::CalculateManaCost: Unknown power type '%d' in spell %d", m_spellInfo->powerType, m_spellInfo->Id);
+ return 0;
+ }
+ }
+ SpellSchools school = GetFirstSchoolInMask(m_spellSchoolMask);
+ // Flat mod from caster auras by spell school
+ powerCost += m_caster->GetInt32Value(UNIT_FIELD_POWER_COST_MODIFIER + school);
+ // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost)
+ if ( m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_SPELL_VS_EXTEND_COST )
+ powerCost += m_caster->GetAttackTime(OFF_ATTACK)/100;
+ // Apply cost mod by spell
+ if(Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, powerCost, this);
+
+ if(m_spellInfo->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION)
+ powerCost = int32(powerCost/ (1.117f* m_spellInfo->spellLevel / m_caster->getLevel() -0.1327f));
+
+ // PCT mod from user auras by school
+ powerCost = int32(powerCost * (1.0f+m_caster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+school)));
+ if (powerCost < 0)
+ powerCost = 0;
+ return powerCost;
+}
+
+uint8 Spell::CheckPower()
+{
+ // item cast not used power
+ if(m_CastItem)
+ return 0;
+
+ // health as power used - need check health amount
+ if(m_spellInfo->powerType == POWER_HEALTH)
+ {
+ if(m_caster->GetHealth() <= m_powerCost)
+ return SPELL_FAILED_CASTER_AURASTATE;
+ return 0;
+ }
+ // Check valid power type
+ if( m_spellInfo->powerType >= MAX_POWERS )
+ {
+ sLog.outError("Spell::CheckMana: Unknown power type '%d'", m_spellInfo->powerType);
+ return SPELL_FAILED_UNKNOWN;
+ }
+ // Check power amount
+ Powers powerType = Powers(m_spellInfo->powerType);
+ if(m_caster->GetPower(powerType) < m_powerCost)
+ return SPELL_FAILED_NO_POWER;
+ else
+ return 0;
+}
+
+uint8 Spell::CheckItems()
+{
+ if (m_caster->GetTypeId() != TYPEID_PLAYER)
+ return 0;
+
+ uint32 itemid, itemcount;
+ Player* p_caster = (Player*)m_caster;
+
+ if(m_CastItem)
+ {
+ itemid = m_CastItem->GetEntry();
+ if( !p_caster->HasItemCount(itemid,1) )
+ return SPELL_FAILED_ITEM_NOT_READY;
+ else
+ {
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if(!proto)
+ return SPELL_FAILED_ITEM_NOT_READY;
+
+ for (int i = 0; i<5; i++)
+ {
+ if (proto->Spells[i].SpellCharges)
+ {
+ if(m_CastItem->GetSpellCharges(i)==0)
+ return SPELL_FAILED_NO_CHARGES_REMAIN;
+ }
+ }
+
+ uint32 ItemClass = proto->Class;
+ if (ItemClass == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget())
+ {
+ for (int i = 0; i < 3; i++)
+ {
+ // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster
+ if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET)
+ continue;
+
+ if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL)
+ if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth())
+ return (uint8)SPELL_FAILED_ALREADY_AT_FULL_HEALTH;
+
+ // Mana Potion, Rage Potion, Thistle Tea(Rogue), ...
+ if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE)
+ {
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ return (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER;
+
+ Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
+
+ if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power))
+ return (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER;
+ }
+ }
+ }
+ }
+ }
+
+ if(m_targets.getItemTargetGUID())
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return SPELL_FAILED_BAD_TARGETS;
+
+ if(!m_targets.getItemTarget())
+ return SPELL_FAILED_ITEM_GONE;
+
+ if(!m_targets.getItemTarget()->IsFitToSpellRequirements(m_spellInfo))
+ return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
+ }
+ // if not item target then required item must be equipped
+ else
+ {
+ if(m_caster->GetTypeId() == TYPEID_PLAYER && !((Player*)m_caster)->HasItemFitToSpellReqirements(m_spellInfo))
+ return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
+ }
+
+ if(m_spellInfo->RequiresSpellFocus)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ GameObject* ok = NULL;
+ MaNGOS::GameObjectFocusCheck go_check(m_caster,m_spellInfo->RequiresSpellFocus);
+ MaNGOS::GameObjectSearcher<MaNGOS::GameObjectFocusCheck> checker(ok,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectFocusCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster));
+
+ if(!ok)
+ return (uint8)SPELL_FAILED_REQUIRES_SPELL_FOCUS;
+
+ focusObject = ok; // game object found in range
+ }
+
+ if (!(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP &&
+ m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION)))
+ {
+ for(uint32 i=0;i<8;i++)
+ {
+ if(m_spellInfo->Reagent[i] <= 0)
+ continue;
+
+ itemid = m_spellInfo->Reagent[i];
+ itemcount = m_spellInfo->ReagentCount[i];
+
+ // if CastItem is also spell reagent
+ if( m_CastItem && m_CastItem->GetEntry() == itemid )
+ {
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if(!proto)
+ return SPELL_FAILED_ITEM_NOT_READY;
+ for(int s=0;s<5;s++)
+ {
+ // CastItem will be used up and does not count as reagent
+ int32 charges = m_CastItem->GetSpellCharges(s);
+ if (proto->Spells[s].SpellCharges < 0 && abs(charges) < 2)
+ {
+ ++itemcount;
+ break;
+ }
+ }
+ }
+ if( !p_caster->HasItemCount(itemid,itemcount) )
+ return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54
+ }
+ }
+
+ uint32 totems = 2;
+ for(int i=0;i<2;++i)
+ {
+ if(m_spellInfo->Totem[i] != 0)
+ {
+ if( p_caster->HasItemCount(m_spellInfo->Totem[i],1) )
+ {
+ totems -= 1;
+ continue;
+ }
+ }else
+ totems -= 1;
+ }
+ if(totems != 0)
+ return (uint8)SPELL_FAILED_TOTEMS; //0x7C
+
+ //Check items for TotemCategory
+ uint32 TotemCategory = 2;
+ for(int i=0;i<2;++i)
+ {
+ if(m_spellInfo->TotemCategory[i] != 0)
+ {
+ if( p_caster->HasItemTotemCategory(m_spellInfo->TotemCategory[i]) )
+ {
+ TotemCategory -= 1;
+ continue;
+ }
+ }
+ else
+ TotemCategory -= 1;
+ }
+ if(TotemCategory != 0)
+ return (uint8)SPELL_FAILED_TOTEM_CATEGORY; //0x7B
+
+ for(int i = 0; i < 3; i++)
+ {
+ switch (m_spellInfo->Effect[i])
+ {
+ case SPELL_EFFECT_CREATE_ITEM:
+ {
+ if (!m_IsTriggeredSpell && m_spellInfo->EffectItemType[i])
+ {
+ ItemPosCountVec dest;
+ uint8 msg = p_caster->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, m_spellInfo->EffectItemType[i], 1 );
+ if (msg != EQUIP_ERR_OK )
+ {
+ p_caster->SendEquipError( msg, NULL, NULL );
+ return SPELL_FAILED_DONT_REPORT;
+ }
+ }
+ break;
+ }
+ case SPELL_EFFECT_ENCHANT_ITEM:
+ {
+ Item* targetItem = m_targets.getItemTarget();
+ if(!targetItem)
+ return SPELL_FAILED_ITEM_NOT_FOUND;
+
+ if( targetItem->GetProto()->ItemLevel < m_spellInfo->baseLevel )
+ return SPELL_FAILED_LOWLEVEL;
+ // Not allow enchant in trade slot for some enchant type
+ if( targetItem->GetOwner() != m_caster )
+ {
+ uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return SPELL_FAILED_ERROR;
+ if (pEnchant->slot & ENCHANTMENT_CAN_SOULBOUND)
+ return SPELL_FAILED_NOT_TRADEABLE;
+ }
+ break;
+ }
+ case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY:
+ {
+ Item *item = m_targets.getItemTarget();
+ if(!item)
+ return SPELL_FAILED_ITEM_NOT_FOUND;
+ // Not allow enchant in trade slot for some enchant type
+ if( item->GetOwner() != m_caster )
+ {
+ uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return SPELL_FAILED_ERROR;
+ if (pEnchant->slot & ENCHANTMENT_CAN_SOULBOUND)
+ return SPELL_FAILED_NOT_TRADEABLE;
+ }
+ break;
+ }
+ case SPELL_EFFECT_ENCHANT_HELD_ITEM:
+ // check item existence in effect code (not output errors at offhand hold item effect to main hand for example
+ break;
+ case SPELL_EFFECT_DISENCHANT:
+ {
+ if(!m_targets.getItemTarget())
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+
+ // prevent disenchanting in trade slot
+ if( m_targets.getItemTarget()->GetOwnerGUID() != m_caster->GetGUID() )
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+
+ ItemPrototype const* itemProto = m_targets.getItemTarget()->GetProto();
+ if(!itemProto)
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+
+ uint32 item_quality = itemProto->Quality;
+ // 2.0.x addon: Check player enchanting level agains the item desenchanting requirements
+ uint32 item_disenchantskilllevel = itemProto->RequiredDisenchantSkill;
+ if (item_disenchantskilllevel == uint32(-1))
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+ if (item_disenchantskilllevel > p_caster->GetSkillValue(SKILL_ENCHANTING))
+ return SPELL_FAILED_LOW_CASTLEVEL;
+ if(item_quality > 4 || item_quality < 2)
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+ if(itemProto->Class != ITEM_CLASS_WEAPON && itemProto->Class != ITEM_CLASS_ARMOR)
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+ if (!itemProto->DisenchantID)
+ return SPELL_FAILED_CANT_BE_DISENCHANTED;
+ break;
+ }
+ case SPELL_EFFECT_PROSPECTING:
+ {
+ if(!m_targets.getItemTarget())
+ return SPELL_FAILED_CANT_BE_PROSPECTED;
+ //ensure item is a prospectable ore
+ if(!(m_targets.getItemTarget()->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP) || m_targets.getItemTarget()->GetProto()->Class != ITEM_CLASS_TRADE_GOODS)
+ return SPELL_FAILED_CANT_BE_PROSPECTED;
+ //prevent prospecting in trade slot
+ if( m_targets.getItemTarget()->GetOwnerGUID() != m_caster->GetGUID() )
+ return SPELL_FAILED_CANT_BE_PROSPECTED;
+ //Check for enough skill in jewelcrafting
+ uint32 item_prospectingskilllevel = m_targets.getItemTarget()->GetProto()->RequiredSkillRank;
+ if(item_prospectingskilllevel >p_caster->GetSkillValue(SKILL_JEWELCRAFTING))
+ return SPELL_FAILED_LOW_CASTLEVEL;
+ //make sure the player has the required ores in inventory
+ if(m_targets.getItemTarget()->GetCount() < 5)
+ return SPELL_FAILED_PROSPECT_NEED_MORE;
+
+ if(!LootTemplates_Prospecting.HaveLootFor(m_targets.getItemTargetEntry()))
+ return SPELL_FAILED_CANT_BE_PROSPECTED;
+
+ break;
+ }
+ case SPELL_EFFECT_WEAPON_DAMAGE:
+ case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_TARGET_NOT_PLAYER;
+ if( m_attackType != RANGED_ATTACK )
+ break;
+ Item *pItem = ((Player*)m_caster)->GetWeaponForAttack(m_attackType);
+ if(!pItem || pItem->IsBroken())
+ return SPELL_FAILED_EQUIPPED_ITEM;
+
+ switch(pItem->GetProto()->SubClass)
+ {
+ case ITEM_SUBCLASS_WEAPON_THROWN:
+ {
+ uint32 ammo = pItem->GetEntry();
+ if( !((Player*)m_caster)->HasItemCount( ammo, 1 ) )
+ return SPELL_FAILED_NO_AMMO;
+ }; break;
+ case ITEM_SUBCLASS_WEAPON_GUN:
+ case ITEM_SUBCLASS_WEAPON_BOW:
+ case ITEM_SUBCLASS_WEAPON_CROSSBOW:
+ {
+ uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID);
+ if(!ammo)
+ {
+ // Requires No Ammo
+ if(m_caster->GetDummyAura(46699))
+ break; // skip other checks
+
+ return SPELL_FAILED_NO_AMMO;
+ }
+
+ ItemPrototype const *ammoProto = objmgr.GetItemPrototype( ammo );
+ if(!ammoProto)
+ return SPELL_FAILED_NO_AMMO;
+
+ if(ammoProto->Class != ITEM_CLASS_PROJECTILE)
+ return SPELL_FAILED_NO_AMMO;
+
+ // check ammo ws. weapon compatibility
+ switch(pItem->GetProto()->SubClass)
+ {
+ case ITEM_SUBCLASS_WEAPON_BOW:
+ case ITEM_SUBCLASS_WEAPON_CROSSBOW:
+ if(ammoProto->SubClass!=ITEM_SUBCLASS_ARROW)
+ return SPELL_FAILED_NO_AMMO;
+ break;
+ case ITEM_SUBCLASS_WEAPON_GUN:
+ if(ammoProto->SubClass!=ITEM_SUBCLASS_BULLET)
+ return SPELL_FAILED_NO_AMMO;
+ break;
+ default:
+ return SPELL_FAILED_NO_AMMO;
+ }
+
+ if( !((Player*)m_caster)->HasItemCount( ammo, 1 ) )
+ return SPELL_FAILED_NO_AMMO;
+ }; break;
+ case ITEM_SUBCLASS_WEAPON_WAND:
+ default:
+ break;
+ }
+ break;
+ }
+ default:break;
+ }
+ }
+
+ return uint8(0);
+}
+
+void Spell::Delayed()
+{
+ if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if (m_spellState == SPELL_STATE_DELAYED)
+ return; // spell is active and can't be time-backed
+
+ // spells not loosing casting time ( slam, dynamites, bombs.. )
+ if(!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE))
+ return;
+
+ //check resist chance
+ int32 resistChance = 100; //must be initialized to 100 for percent modifiers
+ ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this);
+ resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100;
+ if (roll_chance_i(resistChance))
+ return;
+
+ int32 delaytime = GetNextDelayAtDamageMsTime();
+
+ if(int32(m_timer) + delaytime > m_casttime)
+ {
+ delaytime = m_casttime - m_timer;
+ m_timer = m_casttime;
+ }
+ else
+ m_timer += delaytime;
+
+ sLog.outDetail("Spell %u partially interrupted for (%d) ms at damage",m_spellInfo->Id,delaytime);
+
+ WorldPacket data(SMSG_SPELL_DELAYED, 8+4);
+ data.append(m_caster->GetPackGUID());
+ data << uint32(delaytime);
+
+ m_caster->SendMessageToSet(&data,true);
+}
+
+void Spell::DelayedChannel()
+{
+ if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER || getState() != SPELL_STATE_CASTING)
+ return;
+
+ //check resist chance
+ int32 resistChance = 100; //must be initialized to 100 for percent modifiers
+ ((Player*)m_caster)->ApplySpellMod(m_spellInfo->Id,SPELLMOD_NOT_LOSE_CASTING_TIME,resistChance, this);
+ resistChance += m_caster->GetTotalAuraModifier(SPELL_AURA_RESIST_PUSHBACK) - 100;
+ if (roll_chance_i(resistChance))
+ return;
+
+ int32 delaytime = GetNextDelayAtDamageMsTime();
+
+ if(int32(m_timer) < delaytime)
+ {
+ delaytime = m_timer;
+ m_timer = 0;
+ }
+ else
+ m_timer -= delaytime;
+
+ sLog.outDebug("Spell %u partially interrupted for %i ms, new duration: %u ms", m_spellInfo->Id, delaytime, m_timer);
+
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ {
+ if ((*ihit).missCondition == SPELL_MISS_NONE)
+ {
+ Unit* unit = m_caster->GetGUID()==ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
+ if (unit)
+ {
+ for (int j=0;j<3;j++)
+ if( ihit->effectMask & (1<<j) )
+ unit->DelayAura(m_spellInfo->Id, j, delaytime);
+ }
+
+ }
+ }
+
+ for(int j = 0; j < 3; j++)
+ {
+ // partially interrupt persistent area auras
+ DynamicObject* dynObj = m_caster->GetDynObject(m_spellInfo->Id, j);
+ if(dynObj)
+ dynObj->Delay(delaytime);
+ }
+
+ SendChannelUpdate(m_timer);
+}
+
+void Spell::UpdatePointers()
+{
+ if(m_originalCasterGUID==m_caster->GetGUID())
+ m_originalCaster = m_caster;
+ else
+ {
+ m_originalCaster = ObjectAccessor::GetUnit(*m_caster,m_originalCasterGUID);
+ if(m_originalCaster && !m_originalCaster->IsInWorld()) m_originalCaster = NULL;
+ }
+
+ m_targets.Update(m_caster);
+}
+
+bool Spell::IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId)
+{
+ return spellmgr.IsAffectedBySpell(m_spellInfo,spellInfo->Id,effectId,spellInfo->EffectItemType[effectId]);
+}
+
+bool Spell::CheckTargetCreatureType(Unit* target) const
+{
+ uint32 spellCreatureTargetMask = m_spellInfo->TargetCreatureType;
+
+ // Curse of Doom : not find another way to fix spell target check :/
+ if(m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->SpellFamilyFlags == 0x0200000000LL)
+ {
+ // not allow cast at player
+ if(target->GetTypeId()==TYPEID_PLAYER)
+ return false;
+
+ spellCreatureTargetMask = 0x7FF;
+ }
+
+ // Dismiss Pet and Taming Lesson skipped
+ if(m_spellInfo->Id == 2641 || m_spellInfo->Id == 23356)
+ spellCreatureTargetMask = 0;
+
+ if (spellCreatureTargetMask)
+ {
+ uint32 TargetCreatureType = target->GetCreatureTypeMask();
+
+ return !TargetCreatureType || (spellCreatureTargetMask & TargetCreatureType);
+ }
+ return true;
+}
+
+CurrentSpellTypes Spell::GetCurrentContainer()
+{
+ if (IsNextMeleeSwingSpell())
+ return(CURRENT_MELEE_SPELL);
+ else if (IsAutoRepeat())
+ return(CURRENT_AUTOREPEAT_SPELL);
+ else if (IsChanneledSpell(m_spellInfo))
+ return(CURRENT_CHANNELED_SPELL);
+ else
+ return(CURRENT_GENERIC_SPELL);
+}
+
+bool Spell::CheckTarget( Unit* target, uint32 eff, bool hitPhase )
+{
+ // Check targets for creature type mask and remove not appropriate (skip explicit self target case, maybe need other explicit targets)
+ if(m_spellInfo->EffectImplicitTargetA[eff]!=TARGET_SELF && !m_magnetPair.first)
+ {
+ if (!CheckTargetCreatureType(target))
+ return false;
+ }
+
+ // Check targets for not_selectable unit flag and remove
+ // A player can cast spells on his pet (or other controlled unit) though in any state
+ if (target != m_caster && target->GetCharmerOrOwnerGUID() != m_caster->GetGUID())
+ {
+ // any unattackable target skipped
+ if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
+ return false;
+
+ // unselectable targets skipped in all cases except TARGET_SCRIPT targeting
+ // in case TARGET_SCRIPT target selected by server always and can't be cheated
+ if( target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE) &&
+ m_spellInfo->EffectImplicitTargetA[eff] != TARGET_SCRIPT &&
+ m_spellInfo->EffectImplicitTargetB[eff] != TARGET_SCRIPT )
+ return false;
+ }
+
+ //Check player targets and remove if in GM mode or GM invisibility (for not self casting case)
+ if( target != m_caster && target->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(((Player*)target)->GetVisibility()==VISIBILITY_OFF)
+ return false;
+
+ if(((Player*)target)->isGameMaster() && !IsPositiveSpell(m_spellInfo->Id))
+ return false;
+ }
+
+ //Check targets for LOS visibility (except spells without range limitations )
+ switch(m_spellInfo->Effect[eff])
+ {
+ case SPELL_EFFECT_SUMMON_PLAYER: // from anywhere
+ break;
+ case SPELL_EFFECT_DUMMY:
+ if(m_spellInfo->Id!=20577) // Cannibalize
+ break;
+ //fall through
+ case SPELL_EFFECT_RESURRECT_NEW:
+ // player far away, maybe his corpse near?
+ if(target!=m_caster && !target->IsWithinLOSInMap(m_caster))
+ {
+ if(!m_targets.getCorpseTargetGUID())
+ return false;
+
+ Corpse *corpse = ObjectAccessor::GetCorpse(*m_caster,m_targets.getCorpseTargetGUID());
+ if(!corpse)
+ return false;
+
+ if(target->GetGUID()!=corpse->GetOwnerGUID())
+ return false;
+
+ if(!corpse->IsWithinLOSInMap(m_caster))
+ return false;
+ }
+
+ // all ok by some way or another, skip normal check
+ break;
+ default: // normal case
+ if(target!=m_caster && !target->IsWithinLOSInMap(m_caster))
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+Unit* Spell::SelectMagnetTarget()
+{
+ Unit* target = m_targets.getUnitTarget();
+
+ if(target && target->HasAuraType(SPELL_AURA_SPELL_MAGNET) && !(m_spellInfo->Attributes & 0x10))
+ {
+ Unit::AuraList const& magnetAuras = target->GetAurasByType(SPELL_AURA_SPELL_MAGNET);
+ for(Unit::AuraList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr)
+ {
+ if(Unit* magnet = (*itr)->GetCaster())
+ {
+ if((*itr)->m_procCharges>0 && magnet->IsWithinLOSInMap(m_caster))
+ {
+ (*itr)->SetAuraProcCharges((*itr)->m_procCharges-1);
+ m_magnetPair.first = true;
+ m_magnetPair.second = magnet;
+
+ target = magnet;
+ m_targets.setUnitTarget(target);
+ break;
+ }
+ }
+ }
+ }
+
+ return target;
+}
+
+bool Spell::IsNeedSendToClient() const
+{
+ return m_spellInfo->SpellVisual!=0 || IsChanneledSpell(m_spellInfo) ||
+ m_spellInfo->speed > 0.0f || !m_triggeredByAuraSpell && !m_IsTriggeredSpell;
+}
+
+bool Spell::HaveTargetsForEffect( uint8 effect ) const
+{
+ for(std::list<TargetInfo>::const_iterator itr= m_UniqueTargetInfo.begin();itr != m_UniqueTargetInfo.end();++itr)
+ if(itr->effectMask & (1<<effect))
+ return true;
+
+ for(std::list<GOTargetInfo>::const_iterator itr= m_UniqueGOTargetInfo.begin();itr != m_UniqueGOTargetInfo.end();++itr)
+ if(itr->effectMask & (1<<effect))
+ return true;
+
+ for(std::list<ItemTargetInfo>::const_iterator itr= m_UniqueItemInfo.begin();itr != m_UniqueItemInfo.end();++itr)
+ if(itr->effectMask & (1<<effect))
+ return true;
+
+ return false;
+}
+
+SpellEvent::SpellEvent(Spell* spell) : BasicEvent()
+{
+ m_Spell = spell;
+}
+
+SpellEvent::~SpellEvent()
+{
+ if (m_Spell->getState() != SPELL_STATE_FINISHED)
+ m_Spell->cancel();
+
+ if (m_Spell->IsDeletable())
+ {
+ delete m_Spell;
+ }
+ else
+ {
+ sLog.outError("~SpellEvent: %s %u tried to delete non-deletable spell %u. Was not deleted, causes memory leak.",
+ (m_Spell->GetCaster()->GetTypeId()==TYPEID_PLAYER?"Player":"Creature"), m_Spell->GetCaster()->GetGUIDLow(),m_Spell->m_spellInfo->Id);
+ }
+}
+
+bool SpellEvent::Execute(uint64 e_time, uint32 p_time)
+{
+ // update spell if it is not finished
+ if (m_Spell->getState() != SPELL_STATE_FINISHED)
+ m_Spell->update(p_time);
+
+ // check spell state to process
+ switch (m_Spell->getState())
+ {
+ case SPELL_STATE_FINISHED:
+ {
+ // spell was finished, check deletable state
+ if (m_Spell->IsDeletable())
+ {
+ // check, if we do have unfinished triggered spells
+
+ return(true); // spell is deletable, finish event
+ }
+ // event will be re-added automatically at the end of routine)
+ } break;
+
+ case SPELL_STATE_CASTING:
+ {
+ // this spell is in channeled state, process it on the next update
+ // event will be re-added automatically at the end of routine)
+ } break;
+
+ case SPELL_STATE_DELAYED:
+ {
+ // first, check, if we have just started
+ if (m_Spell->GetDelayStart() != 0)
+ {
+ // no, we aren't, do the typical update
+ // check, if we have channeled spell on our hands
+ if (IsChanneledSpell(m_Spell->m_spellInfo))
+ {
+ // evented channeled spell is processed separately, casted once after delay, and not destroyed till finish
+ // check, if we have casting anything else except this channeled spell and autorepeat
+ if (m_Spell->GetCaster()->IsNonMeleeSpellCasted(false, true, true))
+ {
+ // another non-melee non-delayed spell is casted now, abort
+ m_Spell->cancel();
+ }
+ else
+ {
+ // do the action (pass spell to channeling state)
+ m_Spell->handle_immediate();
+ }
+ // event will be re-added automatically at the end of routine)
+ }
+ else
+ {
+ // run the spell handler and think about what we can do next
+ uint64 t_offset = e_time - m_Spell->GetDelayStart();
+ uint64 n_offset = m_Spell->handle_delayed(t_offset);
+ if (n_offset)
+ {
+ // re-add us to the queue
+ m_Spell->GetCaster()->m_Events.AddEvent(this, m_Spell->GetDelayStart() + n_offset, false);
+ return(false); // event not complete
+ }
+ // event complete
+ // finish update event will be re-added automatically at the end of routine)
+ }
+ }
+ else
+ {
+ // delaying had just started, record the moment
+ m_Spell->SetDelayStart(e_time);
+ // re-plan the event for the delay moment
+ m_Spell->GetCaster()->m_Events.AddEvent(this, e_time + m_Spell->GetDelayMoment(), false);
+ return(false); // event not complete
+ }
+ } break;
+
+ default:
+ {
+ // all other states
+ // event will be re-added automatically at the end of routine)
+ } break;
+ }
+
+ // spell processing not complete, plan event on the next update interval
+ m_Spell->GetCaster()->m_Events.AddEvent(this, e_time + 1, false);
+ return(false); // event not complete
+}
+
+void SpellEvent::Abort(uint64 /*e_time*/)
+{
+ // oops, the spell we try to do is aborted
+ if (m_Spell->getState() != SPELL_STATE_FINISHED)
+ m_Spell->cancel();
+}
diff --git a/src/game/Spell.h b/src/game/Spell.h
index 62e5fdb40b2..4384ab256e0 100644
--- a/src/game/Spell.h
+++ b/src/game/Spell.h
@@ -1,696 +1,696 @@
-/*
- * 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
- */
-
-#ifndef __SPELL_H
-#define __SPELL_H
-
-#include "GridDefines.h"
-
-class WorldSession;
-class Unit;
-class DynamicObj;
-class Player;
-class GameObject;
-class Group;
-class Aura;
-
-enum SpellCastTargetFlags
-{
- /*TARGET_FLAG_NONE = 0x0000,
- TARGET_FLAG_SWIMMER = 0x0002,
- TARGET_FLAG_ITEM = 0x0010,
- TARGET_FLAG_SOURCE_AREA = 0x0020,
- TARGET_FLAG_DEST_AREA = 0x0040,
- TARGET_FLAG_UNKNOWN = 0x0080,
- TARGET_FLAG_SELF = 0x0100,
- TARGET_FLAG_PVP_CORPSE = 0x0200,
- TARGET_FLAG_MASS_SPIRIT_HEAL = 0x0400,
- TARGET_FLAG_BEAST_CORPSE = 0x0402,
- TARGET_FLAG_OBJECT = 0x4000,
- TARGET_FLAG_RESURRECTABLE = 0x8000*/
-
- TARGET_FLAG_SELF = 0x00000000,
- TARGET_FLAG_UNIT = 0x00000002, // pguid
- TARGET_FLAG_ITEM = 0x00000010, // pguid
- TARGET_FLAG_SOURCE_LOCATION = 0x00000020, // 3 float
- TARGET_FLAG_DEST_LOCATION = 0x00000040, // 3 float
- TARGET_FLAG_OBJECT_UNK = 0x00000080, // ?
- TARGET_FLAG_PVP_CORPSE = 0x00000200, // pguid
- TARGET_FLAG_OBJECT = 0x00000800, // pguid
- TARGET_FLAG_TRADE_ITEM = 0x00001000, // pguid
- TARGET_FLAG_STRING = 0x00002000, // string
- TARGET_FLAG_UNK1 = 0x00004000, // ?
- TARGET_FLAG_CORPSE = 0x00008000, // pguid
- TARGET_FLAG_UNK2 = 0x00010000 // pguid
-};
-
-enum SpellCastFlags
-{
- CAST_FLAG_UNKNOWN1 = 0x00000002,
- CAST_FLAG_UNKNOWN2 = 0x00000010,
- CAST_FLAG_AMMO = 0x00000020,
- CAST_FLAG_UNKNOWN3 = 0x00000100
-};
-
-enum SpellNotifyPushType
-{
- PUSH_IN_FRONT,
- PUSH_IN_BACK,
- PUSH_SELF_CENTER,
- PUSH_DEST_CENTER,
- PUSH_TARGET_CENTER
-};
-
-bool IsQuestTameSpell(uint32 spellId);
-
-namespace MaNGOS
-{
- struct SpellNotifierPlayer;
- struct SpellNotifierCreatureAndPlayer;
-}
-
-class SpellCastTargets
-{
- public:
- SpellCastTargets();
- ~SpellCastTargets();
-
- bool read ( WorldPacket * data, Unit *caster );
- void write ( WorldPacket * data );
-
- SpellCastTargets& operator=(const SpellCastTargets &target)
- {
- m_unitTarget = target.m_unitTarget;
- m_itemTarget = target.m_itemTarget;
- m_GOTarget = target.m_GOTarget;
-
- m_unitTargetGUID = target.m_unitTargetGUID;
- m_GOTargetGUID = target.m_GOTargetGUID;
- m_CorpseTargetGUID = target.m_CorpseTargetGUID;
- m_itemTargetGUID = target.m_itemTargetGUID;
-
- m_itemTargetEntry = target.m_itemTargetEntry;
-
- m_srcX = target.m_srcX;
- m_srcY = target.m_srcY;
- m_srcZ = target.m_srcZ;
-
- m_destX = target.m_destX;
- m_destY = target.m_destY;
- m_destZ = target.m_destZ;
-
- m_strTarget = target.m_strTarget;
-
- m_targetMask = target.m_targetMask;
-
- return *this;
- }
-
- uint64 getUnitTargetGUID() const { return m_unitTargetGUID; }
- Unit *getUnitTarget() const { return m_unitTarget; }
- void setUnitTarget(Unit *target);
- void setDestination(float x, float y, float z);
-
- uint64 getGOTargetGUID() const { return m_GOTargetGUID; }
- GameObject *getGOTarget() const { return m_GOTarget; }
- void setGOTarget(GameObject *target);
-
- uint64 getCorpseTargetGUID() const { return m_CorpseTargetGUID; }
- void setCorpseTarget(Corpse* corpse);
- uint64 getItemTargetGUID() const { return m_itemTargetGUID; }
- Item* getItemTarget() const { return m_itemTarget; }
- uint32 getItemTargetEntry() const { return m_itemTargetEntry; }
- void setItemTarget(Item* item);
- void updateTradeSlotItem()
- {
- if(m_itemTarget && (m_targetMask & TARGET_FLAG_TRADE_ITEM))
- {
- m_itemTargetGUID = m_itemTarget->GetGUID();
- m_itemTargetEntry = m_itemTarget->GetEntry();
- }
- }
-
- bool IsEmpty() const { return m_GOTargetGUID==0 && m_unitTargetGUID==0 && m_itemTarget==0 && m_CorpseTargetGUID==0; }
-
- void Update(Unit* caster);
-
- float m_srcX, m_srcY, m_srcZ;
- float m_destX, m_destY, m_destZ;
- std::string m_strTarget;
-
- uint32 m_targetMask;
- private:
- // objects (can be used at spell creating and after Update at casting
- Unit *m_unitTarget;
- GameObject *m_GOTarget;
- Item *m_itemTarget;
-
- // object GUID/etc, can be used always
- uint64 m_unitTargetGUID;
- uint64 m_GOTargetGUID;
- uint64 m_CorpseTargetGUID;
- uint64 m_itemTargetGUID;
- uint32 m_itemTargetEntry;
-};
-
-enum SpellState
-{
- SPELL_STATE_NULL = 0,
- SPELL_STATE_PREPARING = 1,
- SPELL_STATE_CASTING = 2,
- SPELL_STATE_FINISHED = 3,
- SPELL_STATE_IDLE = 4,
- SPELL_STATE_DELAYED = 5
-};
-
-#define SPELL_SPELL_CHANNEL_UPDATE_INTERVAL 1000
-
-typedef std::multimap<uint64, uint64> SpellTargetTimeMap;
-
-class Spell
-{
- friend struct MaNGOS::SpellNotifierPlayer;
- friend struct MaNGOS::SpellNotifierCreatureAndPlayer;
- public:
-
- void EffectNULL(uint32 );
- void EffectUnused(uint32 );
- void EffectDistract(uint32 i);
- void EffectPull(uint32 i);
- void EffectSchoolDMG(uint32 i);
- void EffectEnvirinmentalDMG(uint32 i);
- void EffectInstaKill(uint32 i);
- void EffectDummy(uint32 i);
- void EffectTeleportUnits(uint32 i);
- void EffectApplyAura(uint32 i);
- void EffectSendEvent(uint32 i);
- void EffectPowerBurn(uint32 i);
- void EffectPowerDrain(uint32 i);
- void EffectHeal(uint32 i);
- void EffectHealthLeech(uint32 i);
- void EffectQuestComplete(uint32 i);
- void EffectCreateItem(uint32 i);
- void EffectPersistentAA(uint32 i);
- void EffectEnergize(uint32 i);
- void EffectOpenLock(uint32 i);
- void EffectSummonChangeItem(uint32 i);
- void EffectOpenSecretSafe(uint32 i);
- void EffectProficiency(uint32 i);
- void EffectApplyAreaAura(uint32 i);
- void EffectSummonType(uint32 i);
- void EffectSummon(uint32 i);
- void EffectLearnSpell(uint32 i);
- void EffectDispel(uint32 i);
- void EffectDualWield(uint32 i);
- void EffectPickPocket(uint32 i);
- void EffectAddFarsight(uint32 i);
- void EffectSummonWild(uint32 i);
- void EffectSummonGuardian(uint32 i);
- void EffectHealMechanical(uint32 i);
- void EffectTeleUnitsFaceCaster(uint32 i);
- void EffectLearnSkill(uint32 i);
- void EffectAddHonor(uint32 i);
- void EffectTradeSkill(uint32 i);
- void EffectEnchantItemPerm(uint32 i);
- void EffectEnchantItemTmp(uint32 i);
- void EffectTameCreature(uint32 i);
- void EffectSummonPet(uint32 i);
- void EffectLearnPetSpell(uint32 i);
- void EffectWeaponDmg(uint32 i);
- void EffectForceCast(uint32 i);
- void EffectTriggerSpell(uint32 i);
- void EffectTriggerMissileSpell(uint32 i);
- void EffectThreat(uint32 i);
- void EffectHealMaxHealth(uint32 i);
- void EffectInterruptCast(uint32 i);
- void EffectSummonObjectWild(uint32 i);
- void EffectScriptEffect(uint32 i);
- void EffectSanctuary(uint32 i);
- void EffectAddComboPoints(uint32 i);
- void EffectDuel(uint32 i);
- void EffectStuck(uint32 i);
- void EffectSummonPlayer(uint32 i);
- void EffectActivateObject(uint32 i);
- void EffectSummonTotem(uint32 i);
- void EffectEnchantHeldItem(uint32 i);
- void EffectSummonObject(uint32 i);
- void EffectResurrect(uint32 i);
- void EffectParry(uint32 i);
- void EffectBlock(uint32 i);
- void EffectMomentMove(uint32 i);
- void EffectTransmitted(uint32 i);
- void EffectDisEnchant(uint32 i);
- void EffectInebriate(uint32 i);
- void EffectFeedPet(uint32 i);
- void EffectDismissPet(uint32 i);
- void EffectReputation(uint32 i);
- void EffectSelfResurrect(uint32 i);
- void EffectSkinning(uint32 i);
- void EffectCharge(uint32 i);
- void EffectProspecting(uint32 i);
- void EffectSendTaxi(uint32 i);
- void EffectSummonCritter(uint32 i);
- void EffectKnockBack(uint32 i);
- void EffectPlayerPull(uint32 i);
- void EffectDispelMechanic(uint32 i);
- void EffectSummonDeadPet(uint32 i);
- void EffectDestroyAllTotems(uint32 i);
- void EffectDurabilityDamage(uint32 i);
- void EffectSkill(uint32 i);
- void EffectTaunt(uint32 i);
- void EffectDurabilityDamagePCT(uint32 i);
- void EffectModifyThreatPercent(uint32 i);
- void EffectResurrectNew(uint32 i);
- void EffectAddExtraAttacks(uint32 i);
- void EffectSpiritHeal(uint32 i);
- void EffectSkinPlayerCorpse(uint32 i);
- void EffectSummonDemon(uint32 i);
- void EffectStealBeneficialBuff(uint32 i);
- void EffectUnlearnSpecialization(uint32 i);
- void EffectHealPct(uint32 i);
- void EffectEnergisePct(uint32 i);
- void EffectTriggerSpellWithValue(uint32 i);
- void EffectTriggerRitualOfSummoning(uint32 i);
- void EffectKillCredit(uint32 i);
- void EffectQuestFail(uint32 i);
-
- Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 originalCasterGUID = 0, Spell** triggeringContainer = NULL );
- ~Spell();
-
- void prepare(SpellCastTargets * targets, Aura* triggeredByAura = NULL);
- void cancel();
- void update(uint32 difftime);
- void cast(bool skipCheck = false);
- void finish(bool ok = true);
- void TakePower();
- void TakeReagents();
- void TakeCastItem();
- void TriggerSpell();
- uint8 CanCast(bool strict);
- int16 PetCanCast(Unit* target);
- bool CanAutoCast(Unit* target);
-
- // handlers
- void handle_immediate();
- uint64 handle_delayed(uint64 t_offset);
- // handler helpers
- void _handle_immediate_phase();
- void _handle_finish_phase();
-
- uint8 CheckItems();
- uint8 CheckRange(bool strict);
- uint8 CheckPower();
- uint8 CheckCasterAuras() const;
-
- int32 CalculateDamage(uint8 i, Unit* target) { return m_caster->CalculateSpellDamage(m_spellInfo,i,m_currentBasePoints[i],target); }
- int32 CalculatePowerCost();
-
- bool HaveTargetsForEffect(uint8 effect) const;
- void Delayed();
- void DelayedChannel();
- inline uint32 getState() const { return m_spellState; }
- void setState(uint32 state) { m_spellState = state; }
-
- void DoCreateItem(uint32 i, uint32 itemtype);
-
- void WriteSpellGoTargets( WorldPacket * data );
- void WriteAmmoToPacket( WorldPacket * data );
- void FillTargetMap();
-
- void SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap);
-
- Unit* SelectMagnetTarget();
- std::pair <bool,Unit *> m_magnetPair;
- bool CheckTarget( Unit* target, uint32 eff, bool hitPhase );
-
- void SendCastResult(uint8 result);
- void SendSpellStart();
- void SendSpellGo();
- void SendSpellCooldown();
- void SendLogExecute();
- void SendInterrupted(uint8 result);
- void SendChannelUpdate(uint32 time);
- void SendChannelStart(uint32 duration);
- void SendResurrectRequest(Player* target);
- void SendPlaySpellVisual(uint32 SpellID);
-
- void HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i, float DamageMultiplier = 1.0);
- void HandleThreatSpells(uint32 spellId);
- //void HandleAddAura(Unit* Target);
-
- SpellEntry const* m_spellInfo;
- int32 m_currentBasePoints[3]; // cache SpellEntry::EffectBasePoints and use for set custom base points
- Item* m_CastItem;
- uint8 m_cast_count;
- SpellCastTargets m_targets;
-
- int32 GetCastTime() const { return m_casttime; }
- bool IsAutoRepeat() const { return m_autoRepeat; }
- void SetAutoRepeat(bool rep) { m_autoRepeat = rep; }
- void ReSetTimer() { m_timer = m_casttime > 0 ? m_casttime : 0; }
- bool IsNextMeleeSwingSpell() const
- {
- return m_spellInfo->Attributes & (SPELL_ATTR_ON_NEXT_SWING_1|SPELL_ATTR_ON_NEXT_SWING_2);
- }
- bool IsRangedSpell() const
- {
- return m_spellInfo->Attributes & SPELL_ATTR_RANGED;
- }
- bool IsChannelActive() const { return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; }
- bool IsMeleeAttackResetSpell() const { return !m_IsTriggeredSpell && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK); }
- bool IsRangedAttackResetSpell() const { return !m_IsTriggeredSpell && IsRangedSpell() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK); }
-
- bool IsDeletable() const { return m_deletable; }
- void SetDeletable(bool deletable) { m_deletable = deletable; }
- uint64 GetDelayStart() const { return m_delayStart; }
- void SetDelayStart(uint64 m_time) { m_delayStart = m_time; }
- uint64 GetDelayMoment() const { return m_delayMoment; }
-
- bool IsNeedSendToClient() const;
-
- CurrentSpellTypes GetCurrentContainer();
-
- Unit* GetCaster() const { return m_caster; }
- Unit* GetOriginalCaster() const { return m_originalCaster; }
- int32 GetPowerCost() const { return m_powerCost; }
-
- void UpdatePointers(); // must be used at call Spell code after time delay (non triggered spell cast/update spell call/etc)
-
- bool IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId);
-
- bool CheckTargetCreatureType(Unit* target) const;
-
- void AddTriggeredSpell(SpellEntry const* spell) { m_TriggerSpells.push_back(spell); }
-
- void CleanupTargetList();
- protected:
-
- void SendLoot(uint64 guid, LootType loottype);
-
- Unit* m_caster;
-
- uint64 m_originalCasterGUID; // real source of cast (aura caster/etc), used for spell targets selection
- // e.g. damage around area spell trigered by victim aura and da,age emeies of aura caster
- Unit* m_originalCaster; // cached pointer for m_originalCaster, updated at Spell::UpdatePointers()
-
- Spell** m_selfContainer; // pointer to our spell container (if applicable)
- Spell** m_triggeringContainer; // pointer to container with spell that has triggered us
-
- //Spell data
- SpellSchoolMask m_spellSchoolMask; // Spell school (can be overwrite for some spells (wand shoot for example)
- WeaponAttackType m_attackType; // For weapon based attack
- int32 m_powerCost; // Calculated spell cost initialized only in Spell::prepare
- int32 m_casttime; // Calculated spell cast time initialized only in Spell::prepare
- bool m_canReflect; // can reflect this spell?
- bool m_autoRepeat;
-
- uint8 m_delayAtDamageCount;
- int32 GetNextDelayAtDamageMsTime() { return m_delayAtDamageCount < 5 ? 1000 - (m_delayAtDamageCount++)* 200 : 200; }
-
- // Delayed spells system
- uint64 m_delayStart; // time of spell delay start, filled by event handler, zero = just started
- uint64 m_delayMoment; // moment of next delay call, used internally
- bool m_immediateHandled; // were immediate actions handled? (used by delayed spells only)
-
- // These vars are used in both delayed spell system and modified immediate spell system
- bool m_deletable; // is the spell pending deletion or must be updated till permitted to delete?
- bool m_needSpellLog; // need to send spell log?
- uint8 m_applyMultiplierMask; // by effect: damage multiplier needed?
- float m_damageMultipliers[3]; // by effect: damage multiplier
-
- // Current targets, to be used in SpellEffects (MUST BE USED ONLY IN SPELL EFFECTS)
- Unit* unitTarget;
- Item* itemTarget;
- GameObject* gameObjTarget;
- int32 damage;
-
- // this is set in Spell Hit, but used in Apply Aura handler
- DiminishingLevels m_diminishLevel;
- DiminishingGroup m_diminishGroup;
-
- // -------------------------------------------
- GameObject* focusObject;
-
- //******************************************
- // Spell trigger system
- //******************************************
- void doTriggers(SpellMissInfo missInfo, uint32 damage=0, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NONE, uint32 block=0, uint32 absorb=0, bool crit=false);
-
- //*****************************************
- // Spell target subsystem
- //*****************************************
- // Targets store structures and data
- uint32 m_countOfHit;
- uint32 m_countOfMiss;
- struct TargetInfo
- {
- uint64 targetGUID;
- uint64 timeDelay;
- SpellMissInfo missCondition:8;
- SpellMissInfo reflectResult:8;
- uint8 effectMask:8;
- bool processed:1;
- };
- std::list<TargetInfo> m_UniqueTargetInfo;
- uint8 m_needAliveTargetMask; // Mask req. alive targets
-
- struct GOTargetInfo
- {
- uint64 targetGUID;
- uint64 timeDelay;
- uint8 effectMask:8;
- bool processed:1;
- };
- std::list<GOTargetInfo> m_UniqueGOTargetInfo;
-
- struct ItemTargetInfo
- {
- Item *item;
- uint8 effectMask;
- };
- std::list<ItemTargetInfo> m_UniqueItemInfo;
-
- void AddUnitTarget(Unit* target, uint32 effIndex);
- void AddUnitTarget(uint64 unitGUID, uint32 effIndex);
- void AddGOTarget(GameObject* target, uint32 effIndex);
- void AddGOTarget(uint64 goGUID, uint32 effIndex);
- void AddItemTarget(Item* target, uint32 effIndex);
- void DoAllEffectOnTarget(TargetInfo *target);
- void DoSpellHitOnUnit(Unit *unit, uint32 effectMask);
- void DoAllEffectOnTarget(GOTargetInfo *target);
- void DoAllEffectOnTarget(ItemTargetInfo *target);
- bool IsAliveUnitPresentInTargetList();
- // -------------------------------------------
-
- //List For Triggered Spells
- typedef std::list<SpellEntry const*> TriggerSpells;
- TriggerSpells m_TriggerSpells;
-
- uint32 m_spellState;
- uint32 m_timer;
-
- float m_castPositionX;
- float m_castPositionY;
- float m_castPositionZ;
- float m_castOrientation;
- bool m_IsTriggeredSpell;
-
- // if need this can be replaced by Aura copy
- // we can't store original aura link to prevent access to deleted auras
- // and in same time need aura data and after aura deleting.
- SpellEntry const* m_triggeredByAuraSpell;
-};
-
-enum ReplenishType
-{
- REPLENISH_UNDEFINED = 0,
- REPLENISH_HEALTH = 20,
- REPLENISH_MANA = 21,
- REPLENISH_RAGE = 22
-};
-
-enum SpellTargets
-{
- SPELL_TARGETS_HOSTILE,
- SPELL_TARGETS_NOT_FRIENDLY,
- SPELL_TARGETS_NOT_HOSTILE,
- SPELL_TARGETS_FRIENDLY,
- SPELL_TARGETS_AOE_DAMAGE
-};
-
-namespace MaNGOS
-{
- struct MANGOS_DLL_DECL SpellNotifierPlayer
- {
- std::list<Unit*> &i_data;
- Spell &i_spell;
- const uint32& i_index;
- float i_radius;
- Unit* i_originalCaster;
-
- SpellNotifierPlayer(Spell &spell, std::list<Unit*> &data, const uint32 &i, float radius)
- : i_data(data), i_spell(spell), i_index(i), i_radius(radius)
- {
- i_originalCaster = i_spell.GetOriginalCaster();
- }
-
- void Visit(PlayerMapType &m)
- {
- if(!i_originalCaster)
- return;
-
- for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
- {
- Player * pPlayer = itr->getSource();
- if( !pPlayer->isAlive() || pPlayer->isInFlight())
- continue;
-
- if( i_originalCaster->IsFriendlyTo(pPlayer) )
- continue;
-
- if( pPlayer->GetDistance(i_spell.m_targets.m_destX, i_spell.m_targets.m_destY, i_spell.m_targets.m_destZ) < i_radius )
- i_data.push_back(pPlayer);
- }
- }
- template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
- };
-
- struct MANGOS_DLL_DECL SpellNotifierCreatureAndPlayer
- {
- std::list<Unit*> *i_data;
- Spell &i_spell;
- const uint32& i_push_type;
- float i_radius;
- SpellTargets i_TargetType;
- Unit* i_originalCaster;
-
- SpellNotifierCreatureAndPlayer(Spell &spell, std::list<Unit*> &data, float radius, const uint32 &type,
- SpellTargets TargetType = SPELL_TARGETS_NOT_FRIENDLY)
- : i_data(&data), i_spell(spell), i_push_type(type), i_radius(radius), i_TargetType(TargetType)
- {
- i_originalCaster = spell.GetOriginalCaster();
- }
-
- template<class T> inline void Visit(GridRefManager<T> &m)
- {
- assert(i_data);
-
- if(!i_originalCaster)
- return;
-
- for(typename GridRefManager<T>::iterator itr = m.begin(); itr != m.end(); ++itr)
- {
- if( !itr->getSource()->isAlive() || (itr->getSource()->GetTypeId() == TYPEID_PLAYER && ((Player*)itr->getSource())->isInFlight()))
- continue;
-
- switch (i_TargetType)
- {
- case SPELL_TARGETS_HOSTILE:
- if (!itr->getSource()->isTargetableForAttack() || !i_originalCaster->IsHostileTo( itr->getSource() ))
- continue;
- break;
- case SPELL_TARGETS_NOT_FRIENDLY:
- if (!itr->getSource()->isTargetableForAttack() || i_originalCaster->IsFriendlyTo( itr->getSource() ))
- continue;
- break;
- case SPELL_TARGETS_NOT_HOSTILE:
- if (!itr->getSource()->isTargetableForAttack() || i_originalCaster->IsHostileTo( itr->getSource() ))
- continue;
- break;
- case SPELL_TARGETS_FRIENDLY:
- if (!itr->getSource()->isTargetableForAttack() || !i_originalCaster->IsFriendlyTo( itr->getSource() ))
- continue;
- break;
- case SPELL_TARGETS_AOE_DAMAGE:
- {
- if(itr->getSource()->GetTypeId()==TYPEID_UNIT && ((Creature*)itr->getSource())->isTotem())
- continue;
- if(!itr->getSource()->isTargetableForAttack())
- continue;
-
- Unit* check = i_originalCaster->GetCharmerOrOwnerOrSelf();
-
- if( check->GetTypeId()==TYPEID_PLAYER )
- {
- if (check->IsFriendlyTo( itr->getSource() ))
- continue;
- }
- else
- {
- if (!check->IsHostileTo( itr->getSource() ))
- continue;
- }
- }
- break;
- default: continue;
- }
-
- switch(i_push_type)
- {
- case PUSH_IN_FRONT:
- if(i_spell.GetCaster()->isInFront((Unit*)(itr->getSource()), i_radius, 2*M_PI/3 ))
- i_data->push_back(itr->getSource());
- break;
- case PUSH_IN_BACK:
- if(i_spell.GetCaster()->isInBack((Unit*)(itr->getSource()), i_radius, 2*M_PI/3 ))
- i_data->push_back(itr->getSource());
- break;
- case PUSH_SELF_CENTER:
- if(i_spell.GetCaster()->IsWithinDistInMap((Unit*)(itr->getSource()), i_radius))
- i_data->push_back(itr->getSource());
- break;
- case PUSH_DEST_CENTER:
- if((itr->getSource()->GetDistance(i_spell.m_targets.m_destX, i_spell.m_targets.m_destY, i_spell.m_targets.m_destZ) < i_radius ))
- i_data->push_back(itr->getSource());
- break;
- case PUSH_TARGET_CENTER:
- if(i_spell.m_targets.getUnitTarget()->IsWithinDistInMap((Unit*)(itr->getSource()), i_radius))
- i_data->push_back(itr->getSource());
- break;
- }
- }
- }
-
- #ifdef WIN32
- template<> inline void Visit(CorpseMapType & ) {}
- template<> inline void Visit(GameObjectMapType & ) {}
- template<> inline void Visit(DynamicObjectMapType & ) {}
- #endif
- };
-
- #ifndef WIN32
- template<> inline void SpellNotifierCreatureAndPlayer::Visit(CorpseMapType& ) {}
- template<> inline void SpellNotifierCreatureAndPlayer::Visit(GameObjectMapType& ) {}
- template<> inline void SpellNotifierCreatureAndPlayer::Visit(DynamicObjectMapType& ) {}
- #endif
-}
-
-typedef void(Spell::*pEffect)(uint32 i);
-
-class SpellEvent : public BasicEvent
-{
- public:
- SpellEvent(Spell* spell);
- virtual ~SpellEvent();
-
- virtual bool Execute(uint64 e_time, uint32 p_time);
- virtual void Abort(uint64 e_time);
- protected:
- Spell* m_Spell;
-};
-#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
+ */
+
+#ifndef __SPELL_H
+#define __SPELL_H
+
+#include "GridDefines.h"
+
+class WorldSession;
+class Unit;
+class DynamicObj;
+class Player;
+class GameObject;
+class Group;
+class Aura;
+
+enum SpellCastTargetFlags
+{
+ /*TARGET_FLAG_NONE = 0x0000,
+ TARGET_FLAG_SWIMMER = 0x0002,
+ TARGET_FLAG_ITEM = 0x0010,
+ TARGET_FLAG_SOURCE_AREA = 0x0020,
+ TARGET_FLAG_DEST_AREA = 0x0040,
+ TARGET_FLAG_UNKNOWN = 0x0080,
+ TARGET_FLAG_SELF = 0x0100,
+ TARGET_FLAG_PVP_CORPSE = 0x0200,
+ TARGET_FLAG_MASS_SPIRIT_HEAL = 0x0400,
+ TARGET_FLAG_BEAST_CORPSE = 0x0402,
+ TARGET_FLAG_OBJECT = 0x4000,
+ TARGET_FLAG_RESURRECTABLE = 0x8000*/
+
+ TARGET_FLAG_SELF = 0x00000000,
+ TARGET_FLAG_UNIT = 0x00000002, // pguid
+ TARGET_FLAG_ITEM = 0x00000010, // pguid
+ TARGET_FLAG_SOURCE_LOCATION = 0x00000020, // 3 float
+ TARGET_FLAG_DEST_LOCATION = 0x00000040, // 3 float
+ TARGET_FLAG_OBJECT_UNK = 0x00000080, // ?
+ TARGET_FLAG_PVP_CORPSE = 0x00000200, // pguid
+ TARGET_FLAG_OBJECT = 0x00000800, // pguid
+ TARGET_FLAG_TRADE_ITEM = 0x00001000, // pguid
+ TARGET_FLAG_STRING = 0x00002000, // string
+ TARGET_FLAG_UNK1 = 0x00004000, // ?
+ TARGET_FLAG_CORPSE = 0x00008000, // pguid
+ TARGET_FLAG_UNK2 = 0x00010000 // pguid
+};
+
+enum SpellCastFlags
+{
+ CAST_FLAG_UNKNOWN1 = 0x00000002,
+ CAST_FLAG_UNKNOWN2 = 0x00000010,
+ CAST_FLAG_AMMO = 0x00000020,
+ CAST_FLAG_UNKNOWN3 = 0x00000100
+};
+
+enum SpellNotifyPushType
+{
+ PUSH_IN_FRONT,
+ PUSH_IN_BACK,
+ PUSH_SELF_CENTER,
+ PUSH_DEST_CENTER,
+ PUSH_TARGET_CENTER
+};
+
+bool IsQuestTameSpell(uint32 spellId);
+
+namespace MaNGOS
+{
+ struct SpellNotifierPlayer;
+ struct SpellNotifierCreatureAndPlayer;
+}
+
+class SpellCastTargets
+{
+ public:
+ SpellCastTargets();
+ ~SpellCastTargets();
+
+ bool read ( WorldPacket * data, Unit *caster );
+ void write ( WorldPacket * data );
+
+ SpellCastTargets& operator=(const SpellCastTargets &target)
+ {
+ m_unitTarget = target.m_unitTarget;
+ m_itemTarget = target.m_itemTarget;
+ m_GOTarget = target.m_GOTarget;
+
+ m_unitTargetGUID = target.m_unitTargetGUID;
+ m_GOTargetGUID = target.m_GOTargetGUID;
+ m_CorpseTargetGUID = target.m_CorpseTargetGUID;
+ m_itemTargetGUID = target.m_itemTargetGUID;
+
+ m_itemTargetEntry = target.m_itemTargetEntry;
+
+ m_srcX = target.m_srcX;
+ m_srcY = target.m_srcY;
+ m_srcZ = target.m_srcZ;
+
+ m_destX = target.m_destX;
+ m_destY = target.m_destY;
+ m_destZ = target.m_destZ;
+
+ m_strTarget = target.m_strTarget;
+
+ m_targetMask = target.m_targetMask;
+
+ return *this;
+ }
+
+ uint64 getUnitTargetGUID() const { return m_unitTargetGUID; }
+ Unit *getUnitTarget() const { return m_unitTarget; }
+ void setUnitTarget(Unit *target);
+ void setDestination(float x, float y, float z);
+
+ uint64 getGOTargetGUID() const { return m_GOTargetGUID; }
+ GameObject *getGOTarget() const { return m_GOTarget; }
+ void setGOTarget(GameObject *target);
+
+ uint64 getCorpseTargetGUID() const { return m_CorpseTargetGUID; }
+ void setCorpseTarget(Corpse* corpse);
+ uint64 getItemTargetGUID() const { return m_itemTargetGUID; }
+ Item* getItemTarget() const { return m_itemTarget; }
+ uint32 getItemTargetEntry() const { return m_itemTargetEntry; }
+ void setItemTarget(Item* item);
+ void updateTradeSlotItem()
+ {
+ if(m_itemTarget && (m_targetMask & TARGET_FLAG_TRADE_ITEM))
+ {
+ m_itemTargetGUID = m_itemTarget->GetGUID();
+ m_itemTargetEntry = m_itemTarget->GetEntry();
+ }
+ }
+
+ bool IsEmpty() const { return m_GOTargetGUID==0 && m_unitTargetGUID==0 && m_itemTarget==0 && m_CorpseTargetGUID==0; }
+
+ void Update(Unit* caster);
+
+ float m_srcX, m_srcY, m_srcZ;
+ float m_destX, m_destY, m_destZ;
+ std::string m_strTarget;
+
+ uint32 m_targetMask;
+ private:
+ // objects (can be used at spell creating and after Update at casting
+ Unit *m_unitTarget;
+ GameObject *m_GOTarget;
+ Item *m_itemTarget;
+
+ // object GUID/etc, can be used always
+ uint64 m_unitTargetGUID;
+ uint64 m_GOTargetGUID;
+ uint64 m_CorpseTargetGUID;
+ uint64 m_itemTargetGUID;
+ uint32 m_itemTargetEntry;
+};
+
+enum SpellState
+{
+ SPELL_STATE_NULL = 0,
+ SPELL_STATE_PREPARING = 1,
+ SPELL_STATE_CASTING = 2,
+ SPELL_STATE_FINISHED = 3,
+ SPELL_STATE_IDLE = 4,
+ SPELL_STATE_DELAYED = 5
+};
+
+#define SPELL_SPELL_CHANNEL_UPDATE_INTERVAL 1000
+
+typedef std::multimap<uint64, uint64> SpellTargetTimeMap;
+
+class Spell
+{
+ friend struct MaNGOS::SpellNotifierPlayer;
+ friend struct MaNGOS::SpellNotifierCreatureAndPlayer;
+ public:
+
+ void EffectNULL(uint32 );
+ void EffectUnused(uint32 );
+ void EffectDistract(uint32 i);
+ void EffectPull(uint32 i);
+ void EffectSchoolDMG(uint32 i);
+ void EffectEnvirinmentalDMG(uint32 i);
+ void EffectInstaKill(uint32 i);
+ void EffectDummy(uint32 i);
+ void EffectTeleportUnits(uint32 i);
+ void EffectApplyAura(uint32 i);
+ void EffectSendEvent(uint32 i);
+ void EffectPowerBurn(uint32 i);
+ void EffectPowerDrain(uint32 i);
+ void EffectHeal(uint32 i);
+ void EffectHealthLeech(uint32 i);
+ void EffectQuestComplete(uint32 i);
+ void EffectCreateItem(uint32 i);
+ void EffectPersistentAA(uint32 i);
+ void EffectEnergize(uint32 i);
+ void EffectOpenLock(uint32 i);
+ void EffectSummonChangeItem(uint32 i);
+ void EffectOpenSecretSafe(uint32 i);
+ void EffectProficiency(uint32 i);
+ void EffectApplyAreaAura(uint32 i);
+ void EffectSummonType(uint32 i);
+ void EffectSummon(uint32 i);
+ void EffectLearnSpell(uint32 i);
+ void EffectDispel(uint32 i);
+ void EffectDualWield(uint32 i);
+ void EffectPickPocket(uint32 i);
+ void EffectAddFarsight(uint32 i);
+ void EffectSummonWild(uint32 i);
+ void EffectSummonGuardian(uint32 i);
+ void EffectHealMechanical(uint32 i);
+ void EffectTeleUnitsFaceCaster(uint32 i);
+ void EffectLearnSkill(uint32 i);
+ void EffectAddHonor(uint32 i);
+ void EffectTradeSkill(uint32 i);
+ void EffectEnchantItemPerm(uint32 i);
+ void EffectEnchantItemTmp(uint32 i);
+ void EffectTameCreature(uint32 i);
+ void EffectSummonPet(uint32 i);
+ void EffectLearnPetSpell(uint32 i);
+ void EffectWeaponDmg(uint32 i);
+ void EffectForceCast(uint32 i);
+ void EffectTriggerSpell(uint32 i);
+ void EffectTriggerMissileSpell(uint32 i);
+ void EffectThreat(uint32 i);
+ void EffectHealMaxHealth(uint32 i);
+ void EffectInterruptCast(uint32 i);
+ void EffectSummonObjectWild(uint32 i);
+ void EffectScriptEffect(uint32 i);
+ void EffectSanctuary(uint32 i);
+ void EffectAddComboPoints(uint32 i);
+ void EffectDuel(uint32 i);
+ void EffectStuck(uint32 i);
+ void EffectSummonPlayer(uint32 i);
+ void EffectActivateObject(uint32 i);
+ void EffectSummonTotem(uint32 i);
+ void EffectEnchantHeldItem(uint32 i);
+ void EffectSummonObject(uint32 i);
+ void EffectResurrect(uint32 i);
+ void EffectParry(uint32 i);
+ void EffectBlock(uint32 i);
+ void EffectMomentMove(uint32 i);
+ void EffectTransmitted(uint32 i);
+ void EffectDisEnchant(uint32 i);
+ void EffectInebriate(uint32 i);
+ void EffectFeedPet(uint32 i);
+ void EffectDismissPet(uint32 i);
+ void EffectReputation(uint32 i);
+ void EffectSelfResurrect(uint32 i);
+ void EffectSkinning(uint32 i);
+ void EffectCharge(uint32 i);
+ void EffectProspecting(uint32 i);
+ void EffectSendTaxi(uint32 i);
+ void EffectSummonCritter(uint32 i);
+ void EffectKnockBack(uint32 i);
+ void EffectPlayerPull(uint32 i);
+ void EffectDispelMechanic(uint32 i);
+ void EffectSummonDeadPet(uint32 i);
+ void EffectDestroyAllTotems(uint32 i);
+ void EffectDurabilityDamage(uint32 i);
+ void EffectSkill(uint32 i);
+ void EffectTaunt(uint32 i);
+ void EffectDurabilityDamagePCT(uint32 i);
+ void EffectModifyThreatPercent(uint32 i);
+ void EffectResurrectNew(uint32 i);
+ void EffectAddExtraAttacks(uint32 i);
+ void EffectSpiritHeal(uint32 i);
+ void EffectSkinPlayerCorpse(uint32 i);
+ void EffectSummonDemon(uint32 i);
+ void EffectStealBeneficialBuff(uint32 i);
+ void EffectUnlearnSpecialization(uint32 i);
+ void EffectHealPct(uint32 i);
+ void EffectEnergisePct(uint32 i);
+ void EffectTriggerSpellWithValue(uint32 i);
+ void EffectTriggerRitualOfSummoning(uint32 i);
+ void EffectKillCredit(uint32 i);
+ void EffectQuestFail(uint32 i);
+
+ Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 originalCasterGUID = 0, Spell** triggeringContainer = NULL );
+ ~Spell();
+
+ void prepare(SpellCastTargets * targets, Aura* triggeredByAura = NULL);
+ void cancel();
+ void update(uint32 difftime);
+ void cast(bool skipCheck = false);
+ void finish(bool ok = true);
+ void TakePower();
+ void TakeReagents();
+ void TakeCastItem();
+ void TriggerSpell();
+ uint8 CanCast(bool strict);
+ int16 PetCanCast(Unit* target);
+ bool CanAutoCast(Unit* target);
+
+ // handlers
+ void handle_immediate();
+ uint64 handle_delayed(uint64 t_offset);
+ // handler helpers
+ void _handle_immediate_phase();
+ void _handle_finish_phase();
+
+ uint8 CheckItems();
+ uint8 CheckRange(bool strict);
+ uint8 CheckPower();
+ uint8 CheckCasterAuras() const;
+
+ int32 CalculateDamage(uint8 i, Unit* target) { return m_caster->CalculateSpellDamage(m_spellInfo,i,m_currentBasePoints[i],target); }
+ int32 CalculatePowerCost();
+
+ bool HaveTargetsForEffect(uint8 effect) const;
+ void Delayed();
+ void DelayedChannel();
+ inline uint32 getState() const { return m_spellState; }
+ void setState(uint32 state) { m_spellState = state; }
+
+ void DoCreateItem(uint32 i, uint32 itemtype);
+
+ void WriteSpellGoTargets( WorldPacket * data );
+ void WriteAmmoToPacket( WorldPacket * data );
+ void FillTargetMap();
+
+ void SetTargetMap(uint32 i,uint32 cur,std::list<Unit*> &TagUnitMap);
+
+ Unit* SelectMagnetTarget();
+ std::pair <bool,Unit *> m_magnetPair;
+ bool CheckTarget( Unit* target, uint32 eff, bool hitPhase );
+
+ void SendCastResult(uint8 result);
+ void SendSpellStart();
+ void SendSpellGo();
+ void SendSpellCooldown();
+ void SendLogExecute();
+ void SendInterrupted(uint8 result);
+ void SendChannelUpdate(uint32 time);
+ void SendChannelStart(uint32 duration);
+ void SendResurrectRequest(Player* target);
+ void SendPlaySpellVisual(uint32 SpellID);
+
+ void HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i, float DamageMultiplier = 1.0);
+ void HandleThreatSpells(uint32 spellId);
+ //void HandleAddAura(Unit* Target);
+
+ SpellEntry const* m_spellInfo;
+ int32 m_currentBasePoints[3]; // cache SpellEntry::EffectBasePoints and use for set custom base points
+ Item* m_CastItem;
+ uint8 m_cast_count;
+ SpellCastTargets m_targets;
+
+ int32 GetCastTime() const { return m_casttime; }
+ bool IsAutoRepeat() const { return m_autoRepeat; }
+ void SetAutoRepeat(bool rep) { m_autoRepeat = rep; }
+ void ReSetTimer() { m_timer = m_casttime > 0 ? m_casttime : 0; }
+ bool IsNextMeleeSwingSpell() const
+ {
+ return m_spellInfo->Attributes & (SPELL_ATTR_ON_NEXT_SWING_1|SPELL_ATTR_ON_NEXT_SWING_2);
+ }
+ bool IsRangedSpell() const
+ {
+ return m_spellInfo->Attributes & SPELL_ATTR_RANGED;
+ }
+ bool IsChannelActive() const { return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; }
+ bool IsMeleeAttackResetSpell() const { return !m_IsTriggeredSpell && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK); }
+ bool IsRangedAttackResetSpell() const { return !m_IsTriggeredSpell && IsRangedSpell() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK); }
+
+ bool IsDeletable() const { return m_deletable; }
+ void SetDeletable(bool deletable) { m_deletable = deletable; }
+ uint64 GetDelayStart() const { return m_delayStart; }
+ void SetDelayStart(uint64 m_time) { m_delayStart = m_time; }
+ uint64 GetDelayMoment() const { return m_delayMoment; }
+
+ bool IsNeedSendToClient() const;
+
+ CurrentSpellTypes GetCurrentContainer();
+
+ Unit* GetCaster() const { return m_caster; }
+ Unit* GetOriginalCaster() const { return m_originalCaster; }
+ int32 GetPowerCost() const { return m_powerCost; }
+
+ void UpdatePointers(); // must be used at call Spell code after time delay (non triggered spell cast/update spell call/etc)
+
+ bool IsAffectedBy(SpellEntry const *spellInfo, uint32 effectId);
+
+ bool CheckTargetCreatureType(Unit* target) const;
+
+ void AddTriggeredSpell(SpellEntry const* spell) { m_TriggerSpells.push_back(spell); }
+
+ void CleanupTargetList();
+ protected:
+
+ void SendLoot(uint64 guid, LootType loottype);
+
+ Unit* m_caster;
+
+ uint64 m_originalCasterGUID; // real source of cast (aura caster/etc), used for spell targets selection
+ // e.g. damage around area spell trigered by victim aura and da,age emeies of aura caster
+ Unit* m_originalCaster; // cached pointer for m_originalCaster, updated at Spell::UpdatePointers()
+
+ Spell** m_selfContainer; // pointer to our spell container (if applicable)
+ Spell** m_triggeringContainer; // pointer to container with spell that has triggered us
+
+ //Spell data
+ SpellSchoolMask m_spellSchoolMask; // Spell school (can be overwrite for some spells (wand shoot for example)
+ WeaponAttackType m_attackType; // For weapon based attack
+ int32 m_powerCost; // Calculated spell cost initialized only in Spell::prepare
+ int32 m_casttime; // Calculated spell cast time initialized only in Spell::prepare
+ bool m_canReflect; // can reflect this spell?
+ bool m_autoRepeat;
+
+ uint8 m_delayAtDamageCount;
+ int32 GetNextDelayAtDamageMsTime() { return m_delayAtDamageCount < 5 ? 1000 - (m_delayAtDamageCount++)* 200 : 200; }
+
+ // Delayed spells system
+ uint64 m_delayStart; // time of spell delay start, filled by event handler, zero = just started
+ uint64 m_delayMoment; // moment of next delay call, used internally
+ bool m_immediateHandled; // were immediate actions handled? (used by delayed spells only)
+
+ // These vars are used in both delayed spell system and modified immediate spell system
+ bool m_deletable; // is the spell pending deletion or must be updated till permitted to delete?
+ bool m_needSpellLog; // need to send spell log?
+ uint8 m_applyMultiplierMask; // by effect: damage multiplier needed?
+ float m_damageMultipliers[3]; // by effect: damage multiplier
+
+ // Current targets, to be used in SpellEffects (MUST BE USED ONLY IN SPELL EFFECTS)
+ Unit* unitTarget;
+ Item* itemTarget;
+ GameObject* gameObjTarget;
+ int32 damage;
+
+ // this is set in Spell Hit, but used in Apply Aura handler
+ DiminishingLevels m_diminishLevel;
+ DiminishingGroup m_diminishGroup;
+
+ // -------------------------------------------
+ GameObject* focusObject;
+
+ //******************************************
+ // Spell trigger system
+ //******************************************
+ void doTriggers(SpellMissInfo missInfo, uint32 damage=0, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NONE, uint32 block=0, uint32 absorb=0, bool crit=false);
+
+ //*****************************************
+ // Spell target subsystem
+ //*****************************************
+ // Targets store structures and data
+ uint32 m_countOfHit;
+ uint32 m_countOfMiss;
+ struct TargetInfo
+ {
+ uint64 targetGUID;
+ uint64 timeDelay;
+ SpellMissInfo missCondition:8;
+ SpellMissInfo reflectResult:8;
+ uint8 effectMask:8;
+ bool processed:1;
+ };
+ std::list<TargetInfo> m_UniqueTargetInfo;
+ uint8 m_needAliveTargetMask; // Mask req. alive targets
+
+ struct GOTargetInfo
+ {
+ uint64 targetGUID;
+ uint64 timeDelay;
+ uint8 effectMask:8;
+ bool processed:1;
+ };
+ std::list<GOTargetInfo> m_UniqueGOTargetInfo;
+
+ struct ItemTargetInfo
+ {
+ Item *item;
+ uint8 effectMask;
+ };
+ std::list<ItemTargetInfo> m_UniqueItemInfo;
+
+ void AddUnitTarget(Unit* target, uint32 effIndex);
+ void AddUnitTarget(uint64 unitGUID, uint32 effIndex);
+ void AddGOTarget(GameObject* target, uint32 effIndex);
+ void AddGOTarget(uint64 goGUID, uint32 effIndex);
+ void AddItemTarget(Item* target, uint32 effIndex);
+ void DoAllEffectOnTarget(TargetInfo *target);
+ void DoSpellHitOnUnit(Unit *unit, uint32 effectMask);
+ void DoAllEffectOnTarget(GOTargetInfo *target);
+ void DoAllEffectOnTarget(ItemTargetInfo *target);
+ bool IsAliveUnitPresentInTargetList();
+ // -------------------------------------------
+
+ //List For Triggered Spells
+ typedef std::list<SpellEntry const*> TriggerSpells;
+ TriggerSpells m_TriggerSpells;
+
+ uint32 m_spellState;
+ uint32 m_timer;
+
+ float m_castPositionX;
+ float m_castPositionY;
+ float m_castPositionZ;
+ float m_castOrientation;
+ bool m_IsTriggeredSpell;
+
+ // if need this can be replaced by Aura copy
+ // we can't store original aura link to prevent access to deleted auras
+ // and in same time need aura data and after aura deleting.
+ SpellEntry const* m_triggeredByAuraSpell;
+};
+
+enum ReplenishType
+{
+ REPLENISH_UNDEFINED = 0,
+ REPLENISH_HEALTH = 20,
+ REPLENISH_MANA = 21,
+ REPLENISH_RAGE = 22
+};
+
+enum SpellTargets
+{
+ SPELL_TARGETS_HOSTILE,
+ SPELL_TARGETS_NOT_FRIENDLY,
+ SPELL_TARGETS_NOT_HOSTILE,
+ SPELL_TARGETS_FRIENDLY,
+ SPELL_TARGETS_AOE_DAMAGE
+};
+
+namespace MaNGOS
+{
+ struct MANGOS_DLL_DECL SpellNotifierPlayer
+ {
+ std::list<Unit*> &i_data;
+ Spell &i_spell;
+ const uint32& i_index;
+ float i_radius;
+ Unit* i_originalCaster;
+
+ SpellNotifierPlayer(Spell &spell, std::list<Unit*> &data, const uint32 &i, float radius)
+ : i_data(data), i_spell(spell), i_index(i), i_radius(radius)
+ {
+ i_originalCaster = i_spell.GetOriginalCaster();
+ }
+
+ void Visit(PlayerMapType &m)
+ {
+ if(!i_originalCaster)
+ return;
+
+ for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
+ {
+ Player * pPlayer = itr->getSource();
+ if( !pPlayer->isAlive() || pPlayer->isInFlight())
+ continue;
+
+ if( i_originalCaster->IsFriendlyTo(pPlayer) )
+ continue;
+
+ if( pPlayer->GetDistance(i_spell.m_targets.m_destX, i_spell.m_targets.m_destY, i_spell.m_targets.m_destZ) < i_radius )
+ i_data.push_back(pPlayer);
+ }
+ }
+ template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+ };
+
+ struct MANGOS_DLL_DECL SpellNotifierCreatureAndPlayer
+ {
+ std::list<Unit*> *i_data;
+ Spell &i_spell;
+ const uint32& i_push_type;
+ float i_radius;
+ SpellTargets i_TargetType;
+ Unit* i_originalCaster;
+
+ SpellNotifierCreatureAndPlayer(Spell &spell, std::list<Unit*> &data, float radius, const uint32 &type,
+ SpellTargets TargetType = SPELL_TARGETS_NOT_FRIENDLY)
+ : i_data(&data), i_spell(spell), i_push_type(type), i_radius(radius), i_TargetType(TargetType)
+ {
+ i_originalCaster = spell.GetOriginalCaster();
+ }
+
+ template<class T> inline void Visit(GridRefManager<T> &m)
+ {
+ assert(i_data);
+
+ if(!i_originalCaster)
+ return;
+
+ for(typename GridRefManager<T>::iterator itr = m.begin(); itr != m.end(); ++itr)
+ {
+ if( !itr->getSource()->isAlive() || (itr->getSource()->GetTypeId() == TYPEID_PLAYER && ((Player*)itr->getSource())->isInFlight()))
+ continue;
+
+ switch (i_TargetType)
+ {
+ case SPELL_TARGETS_HOSTILE:
+ if (!itr->getSource()->isTargetableForAttack() || !i_originalCaster->IsHostileTo( itr->getSource() ))
+ continue;
+ break;
+ case SPELL_TARGETS_NOT_FRIENDLY:
+ if (!itr->getSource()->isTargetableForAttack() || i_originalCaster->IsFriendlyTo( itr->getSource() ))
+ continue;
+ break;
+ case SPELL_TARGETS_NOT_HOSTILE:
+ if (!itr->getSource()->isTargetableForAttack() || i_originalCaster->IsHostileTo( itr->getSource() ))
+ continue;
+ break;
+ case SPELL_TARGETS_FRIENDLY:
+ if (!itr->getSource()->isTargetableForAttack() || !i_originalCaster->IsFriendlyTo( itr->getSource() ))
+ continue;
+ break;
+ case SPELL_TARGETS_AOE_DAMAGE:
+ {
+ if(itr->getSource()->GetTypeId()==TYPEID_UNIT && ((Creature*)itr->getSource())->isTotem())
+ continue;
+ if(!itr->getSource()->isTargetableForAttack())
+ continue;
+
+ Unit* check = i_originalCaster->GetCharmerOrOwnerOrSelf();
+
+ if( check->GetTypeId()==TYPEID_PLAYER )
+ {
+ if (check->IsFriendlyTo( itr->getSource() ))
+ continue;
+ }
+ else
+ {
+ if (!check->IsHostileTo( itr->getSource() ))
+ continue;
+ }
+ }
+ break;
+ default: continue;
+ }
+
+ switch(i_push_type)
+ {
+ case PUSH_IN_FRONT:
+ if(i_spell.GetCaster()->isInFront((Unit*)(itr->getSource()), i_radius, 2*M_PI/3 ))
+ i_data->push_back(itr->getSource());
+ break;
+ case PUSH_IN_BACK:
+ if(i_spell.GetCaster()->isInBack((Unit*)(itr->getSource()), i_radius, 2*M_PI/3 ))
+ i_data->push_back(itr->getSource());
+ break;
+ case PUSH_SELF_CENTER:
+ if(i_spell.GetCaster()->IsWithinDistInMap((Unit*)(itr->getSource()), i_radius))
+ i_data->push_back(itr->getSource());
+ break;
+ case PUSH_DEST_CENTER:
+ if((itr->getSource()->GetDistance(i_spell.m_targets.m_destX, i_spell.m_targets.m_destY, i_spell.m_targets.m_destZ) < i_radius ))
+ i_data->push_back(itr->getSource());
+ break;
+ case PUSH_TARGET_CENTER:
+ if(i_spell.m_targets.getUnitTarget()->IsWithinDistInMap((Unit*)(itr->getSource()), i_radius))
+ i_data->push_back(itr->getSource());
+ break;
+ }
+ }
+ }
+
+ #ifdef WIN32
+ template<> inline void Visit(CorpseMapType & ) {}
+ template<> inline void Visit(GameObjectMapType & ) {}
+ template<> inline void Visit(DynamicObjectMapType & ) {}
+ #endif
+ };
+
+ #ifndef WIN32
+ template<> inline void SpellNotifierCreatureAndPlayer::Visit(CorpseMapType& ) {}
+ template<> inline void SpellNotifierCreatureAndPlayer::Visit(GameObjectMapType& ) {}
+ template<> inline void SpellNotifierCreatureAndPlayer::Visit(DynamicObjectMapType& ) {}
+ #endif
+}
+
+typedef void(Spell::*pEffect)(uint32 i);
+
+class SpellEvent : public BasicEvent
+{
+ public:
+ SpellEvent(Spell* spell);
+ virtual ~SpellEvent();
+
+ virtual bool Execute(uint64 e_time, uint32 p_time);
+ virtual void Abort(uint64 e_time);
+ protected:
+ Spell* m_Spell;
+};
+#endif
diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp
index d8623a6ebe2..474ec7ea04d 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_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);
-}
+/*
+ * 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 06607e90fd8..9a37b07ee4f 100644
--- a/src/game/SpellEffects.cpp
+++ b/src/game/SpellEffects.cpp
@@ -1,6187 +1,6187 @@
-/*
- * 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 "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 "SkillExtraItems.h"
-#include "Unit.h"
-#include "CreatureAI.h"
-#include "Spell.h"
-#include "DynamicObject.h"
-#include "SpellAuras.h"
-#include "Group.h"
-#include "UpdateData.h"
-#include "MapManager.h"
-#include "ObjectAccessor.h"
-#include "SharedDefines.h"
-#include "Pet.h"
-#include "GameObject.h"
-#include "GossipDef.h"
-#include "Creature.h"
-#include "Totem.h"
-#include "CreatureAI.h"
-#include "BattleGround.h"
-#include "BattleGroundEY.h"
-#include "BattleGroundWS.h"
-#include "VMapFactory.h"
-#include "Language.h"
-#include "SocialMgr.h"
-#include "Util.h"
-#include "TemporarySummon.h"
-
-
-pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
-{
- &Spell::EffectNULL, // 0
- &Spell::EffectInstaKill, // 1 SPELL_EFFECT_INSTAKILL
- &Spell::EffectSchoolDMG, // 2 SPELL_EFFECT_SCHOOL_DAMAGE
- &Spell::EffectDummy, // 3 SPELL_EFFECT_DUMMY
- &Spell::EffectUnused, // 4 SPELL_EFFECT_PORTAL_TELEPORT unused
- &Spell::EffectTeleportUnits, // 5 SPELL_EFFECT_TELEPORT_UNITS
- &Spell::EffectApplyAura, // 6 SPELL_EFFECT_APPLY_AURA
- &Spell::EffectEnvirinmentalDMG, // 7 SPELL_EFFECT_ENVIRONMENTAL_DAMAGE
- &Spell::EffectPowerDrain, // 8 SPELL_EFFECT_POWER_DRAIN
- &Spell::EffectHealthLeech, // 9 SPELL_EFFECT_HEALTH_LEECH
- &Spell::EffectHeal, // 10 SPELL_EFFECT_HEAL
- &Spell::EffectUnused, // 11 SPELL_EFFECT_BIND
- &Spell::EffectNULL, // 12 SPELL_EFFECT_PORTAL
- &Spell::EffectUnused, // 13 SPELL_EFFECT_RITUAL_BASE unused
- &Spell::EffectUnused, // 14 SPELL_EFFECT_RITUAL_SPECIALIZE unused
- &Spell::EffectUnused, // 15 SPELL_EFFECT_RITUAL_ACTIVATE_PORTAL unused
- &Spell::EffectQuestComplete, // 16 SPELL_EFFECT_QUEST_COMPLETE
- &Spell::EffectWeaponDmg, // 17 SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL
- &Spell::EffectResurrect, // 18 SPELL_EFFECT_RESURRECT
- &Spell::EffectAddExtraAttacks, // 19 SPELL_EFFECT_ADD_EXTRA_ATTACKS
- &Spell::EffectUnused, // 20 SPELL_EFFECT_DODGE one spell: Dodge
- &Spell::EffectUnused, // 21 SPELL_EFFECT_EVADE one spell: Evade (DND)
- &Spell::EffectParry, // 22 SPELL_EFFECT_PARRY
- &Spell::EffectBlock, // 23 SPELL_EFFECT_BLOCK one spell: Block
- &Spell::EffectCreateItem, // 24 SPELL_EFFECT_CREATE_ITEM
- &Spell::EffectUnused, // 25 SPELL_EFFECT_WEAPON
- &Spell::EffectUnused, // 26 SPELL_EFFECT_DEFENSE one spell: Defense
- &Spell::EffectPersistentAA, // 27 SPELL_EFFECT_PERSISTENT_AREA_AURA
- &Spell::EffectSummonType, // 28 SPELL_EFFECT_SUMMON
- &Spell::EffectMomentMove, // 29 SPELL_EFFECT_LEAP
- &Spell::EffectEnergize, // 30 SPELL_EFFECT_ENERGIZE
- &Spell::EffectWeaponDmg, // 31 SPELL_EFFECT_WEAPON_PERCENT_DAMAGE
- &Spell::EffectTriggerMissileSpell, // 32 SPELL_EFFECT_TRIGGER_MISSILE
- &Spell::EffectOpenLock, // 33 SPELL_EFFECT_OPEN_LOCK
- &Spell::EffectSummonChangeItem, // 34 SPELL_EFFECT_SUMMON_CHANGE_ITEM
- &Spell::EffectApplyAreaAura, // 35 SPELL_EFFECT_APPLY_AREA_AURA_PARTY
- &Spell::EffectLearnSpell, // 36 SPELL_EFFECT_LEARN_SPELL
- &Spell::EffectUnused, // 37 SPELL_EFFECT_SPELL_DEFENSE one spell: SPELLDEFENSE (DND)
- &Spell::EffectDispel, // 38 SPELL_EFFECT_DISPEL
- &Spell::EffectUnused, // 39 SPELL_EFFECT_LANGUAGE
- &Spell::EffectDualWield, // 40 SPELL_EFFECT_DUAL_WIELD
- &Spell::EffectSummonWild, // 41 SPELL_EFFECT_SUMMON_WILD
- &Spell::EffectSummonGuardian, // 42 SPELL_EFFECT_SUMMON_GUARDIAN
- &Spell::EffectTeleUnitsFaceCaster, // 43 SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER
- &Spell::EffectLearnSkill, // 44 SPELL_EFFECT_SKILL_STEP
- &Spell::EffectAddHonor, // 45 SPELL_EFFECT_ADD_HONOR honor/pvp related
- &Spell::EffectNULL, // 46 SPELL_EFFECT_SPAWN we must spawn pet there
- &Spell::EffectTradeSkill, // 47 SPELL_EFFECT_TRADE_SKILL
- &Spell::EffectUnused, // 48 SPELL_EFFECT_STEALTH one spell: Base Stealth
- &Spell::EffectUnused, // 49 SPELL_EFFECT_DETECT one spell: Detect
- &Spell::EffectTransmitted, // 50 SPELL_EFFECT_TRANS_DOOR
- &Spell::EffectUnused, // 51 SPELL_EFFECT_FORCE_CRITICAL_HIT unused
- &Spell::EffectUnused, // 52 SPELL_EFFECT_GUARANTEE_HIT one spell: zzOLDCritical Shot
- &Spell::EffectEnchantItemPerm, // 53 SPELL_EFFECT_ENCHANT_ITEM
- &Spell::EffectEnchantItemTmp, // 54 SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY
- &Spell::EffectTameCreature, // 55 SPELL_EFFECT_TAMECREATURE
- &Spell::EffectSummonPet, // 56 SPELL_EFFECT_SUMMON_PET
- &Spell::EffectLearnPetSpell, // 57 SPELL_EFFECT_LEARN_PET_SPELL
- &Spell::EffectWeaponDmg, // 58 SPELL_EFFECT_WEAPON_DAMAGE
- &Spell::EffectOpenSecretSafe, // 59 SPELL_EFFECT_OPEN_LOCK_ITEM
- &Spell::EffectProficiency, // 60 SPELL_EFFECT_PROFICIENCY
- &Spell::EffectSendEvent, // 61 SPELL_EFFECT_SEND_EVENT
- &Spell::EffectPowerBurn, // 62 SPELL_EFFECT_POWER_BURN
- &Spell::EffectThreat, // 63 SPELL_EFFECT_THREAT
- &Spell::EffectTriggerSpell, // 64 SPELL_EFFECT_TRIGGER_SPELL
- &Spell::EffectUnused, // 65 SPELL_EFFECT_HEALTH_FUNNEL unused
- &Spell::EffectUnused, // 66 SPELL_EFFECT_POWER_FUNNEL unused
- &Spell::EffectHealMaxHealth, // 67 SPELL_EFFECT_HEAL_MAX_HEALTH
- &Spell::EffectInterruptCast, // 68 SPELL_EFFECT_INTERRUPT_CAST
- &Spell::EffectDistract, // 69 SPELL_EFFECT_DISTRACT
- &Spell::EffectPull, // 70 SPELL_EFFECT_PULL one spell: Distract Move
- &Spell::EffectPickPocket, // 71 SPELL_EFFECT_PICKPOCKET
- &Spell::EffectAddFarsight, // 72 SPELL_EFFECT_ADD_FARSIGHT
- &Spell::EffectSummonGuardian, // 73 SPELL_EFFECT_SUMMON_POSSESSED
- &Spell::EffectSummonTotem, // 74 SPELL_EFFECT_SUMMON_TOTEM
- &Spell::EffectHealMechanical, // 75 SPELL_EFFECT_HEAL_MECHANICAL one spell: Mechanical Patch Kit
- &Spell::EffectSummonObjectWild, // 76 SPELL_EFFECT_SUMMON_OBJECT_WILD
- &Spell::EffectScriptEffect, // 77 SPELL_EFFECT_SCRIPT_EFFECT
- &Spell::EffectUnused, // 78 SPELL_EFFECT_ATTACK
- &Spell::EffectSanctuary, // 79 SPELL_EFFECT_SANCTUARY
- &Spell::EffectAddComboPoints, // 80 SPELL_EFFECT_ADD_COMBO_POINTS
- &Spell::EffectUnused, // 81 SPELL_EFFECT_CREATE_HOUSE one spell: Create House (TEST)
- &Spell::EffectNULL, // 82 SPELL_EFFECT_BIND_SIGHT
- &Spell::EffectDuel, // 83 SPELL_EFFECT_DUEL
- &Spell::EffectStuck, // 84 SPELL_EFFECT_STUCK
- &Spell::EffectSummonPlayer, // 85 SPELL_EFFECT_SUMMON_PLAYER
- &Spell::EffectActivateObject, // 86 SPELL_EFFECT_ACTIVATE_OBJECT
- &Spell::EffectSummonTotem, // 87 SPELL_EFFECT_SUMMON_TOTEM_SLOT1
- &Spell::EffectSummonTotem, // 88 SPELL_EFFECT_SUMMON_TOTEM_SLOT2
- &Spell::EffectSummonTotem, // 89 SPELL_EFFECT_SUMMON_TOTEM_SLOT3
- &Spell::EffectSummonTotem, // 90 SPELL_EFFECT_SUMMON_TOTEM_SLOT4
- &Spell::EffectUnused, // 91 SPELL_EFFECT_THREAT_ALL one spell: zzOLDBrainwash
- &Spell::EffectEnchantHeldItem, // 92 SPELL_EFFECT_ENCHANT_HELD_ITEM
- &Spell::EffectUnused, // 93 SPELL_EFFECT_SUMMON_PHANTASM
- &Spell::EffectSelfResurrect, // 94 SPELL_EFFECT_SELF_RESURRECT
- &Spell::EffectSkinning, // 95 SPELL_EFFECT_SKINNING
- &Spell::EffectCharge, // 96 SPELL_EFFECT_CHARGE
- &Spell::EffectSummonCritter, // 97 SPELL_EFFECT_SUMMON_CRITTER
- &Spell::EffectKnockBack, // 98 SPELL_EFFECT_KNOCK_BACK
- &Spell::EffectDisEnchant, // 99 SPELL_EFFECT_DISENCHANT
- &Spell::EffectInebriate, //100 SPELL_EFFECT_INEBRIATE
- &Spell::EffectFeedPet, //101 SPELL_EFFECT_FEED_PET
- &Spell::EffectDismissPet, //102 SPELL_EFFECT_DISMISS_PET
- &Spell::EffectReputation, //103 SPELL_EFFECT_REPUTATION
- &Spell::EffectSummonObject, //104 SPELL_EFFECT_SUMMON_OBJECT_SLOT1
- &Spell::EffectSummonObject, //105 SPELL_EFFECT_SUMMON_OBJECT_SLOT2
- &Spell::EffectSummonObject, //106 SPELL_EFFECT_SUMMON_OBJECT_SLOT3
- &Spell::EffectSummonObject, //107 SPELL_EFFECT_SUMMON_OBJECT_SLOT4
- &Spell::EffectDispelMechanic, //108 SPELL_EFFECT_DISPEL_MECHANIC
- &Spell::EffectSummonDeadPet, //109 SPELL_EFFECT_SUMMON_DEAD_PET
- &Spell::EffectDestroyAllTotems, //110 SPELL_EFFECT_DESTROY_ALL_TOTEMS
- &Spell::EffectDurabilityDamage, //111 SPELL_EFFECT_DURABILITY_DAMAGE
- &Spell::EffectSummonDemon, //112 SPELL_EFFECT_SUMMON_DEMON
- &Spell::EffectResurrectNew, //113 SPELL_EFFECT_RESURRECT_NEW
- &Spell::EffectTaunt, //114 SPELL_EFFECT_ATTACK_ME
- &Spell::EffectDurabilityDamagePCT, //115 SPELL_EFFECT_DURABILITY_DAMAGE_PCT
- &Spell::EffectSkinPlayerCorpse, //116 SPELL_EFFECT_SKIN_PLAYER_CORPSE one spell: Remove Insignia, bg usage, required special corpse flags...
- &Spell::EffectSpiritHeal, //117 SPELL_EFFECT_SPIRIT_HEAL one spell: Spirit Heal
- &Spell::EffectSkill, //118 SPELL_EFFECT_SKILL professions and more
- &Spell::EffectApplyAreaAura, //119 SPELL_EFFECT_APPLY_AREA_AURA_PET
- &Spell::EffectUnused, //120 SPELL_EFFECT_TELEPORT_GRAVEYARD one spell: Graveyard Teleport Test
- &Spell::EffectWeaponDmg, //121 SPELL_EFFECT_NORMALIZED_WEAPON_DMG
- &Spell::EffectUnused, //122 SPELL_EFFECT_122 unused
- &Spell::EffectSendTaxi, //123 SPELL_EFFECT_SEND_TAXI taxi/flight related (misc value is taxi path id)
- &Spell::EffectPlayerPull, //124 SPELL_EFFECT_PLAYER_PULL opposite of knockback effect (pulls player twoard caster)
- &Spell::EffectModifyThreatPercent, //125 SPELL_EFFECT_MODIFY_THREAT_PERCENT
- &Spell::EffectStealBeneficialBuff, //126 SPELL_EFFECT_STEAL_BENEFICIAL_BUFF spell steal effect?
- &Spell::EffectProspecting, //127 SPELL_EFFECT_PROSPECTING Prospecting spell
- &Spell::EffectApplyAreaAura, //128 SPELL_EFFECT_APPLY_AREA_AURA_FRIEND
- &Spell::EffectApplyAreaAura, //129 SPELL_EFFECT_APPLY_AREA_AURA_ENEMY
- &Spell::EffectNULL, //130 SPELL_EFFECT_REDIRECT_THREAT
- &Spell::EffectUnused, //131 SPELL_EFFECT_131 used in some test spells
- &Spell::EffectNULL, //132 SPELL_EFFECT_PLAY_MUSIC sound id in misc value
- &Spell::EffectUnlearnSpecialization, //133 SPELL_EFFECT_UNLEARN_SPECIALIZATION unlearn profession specialization
- &Spell::EffectKillCredit, //134 SPELL_EFFECT_KILL_CREDIT misc value is creature entry
- &Spell::EffectNULL, //135 SPELL_EFFECT_CALL_PET
- &Spell::EffectHealPct, //136 SPELL_EFFECT_HEAL_PCT
- &Spell::EffectEnergisePct, //137 SPELL_EFFECT_ENERGIZE_PCT
- &Spell::EffectNULL, //138 SPELL_EFFECT_138 Leap
- &Spell::EffectUnused, //139 SPELL_EFFECT_139 unused
- &Spell::EffectForceCast, //140 SPELL_EFFECT_FORCE_CAST
- &Spell::EffectNULL, //141 SPELL_EFFECT_141 damage and reduce speed?
- &Spell::EffectTriggerSpellWithValue, //142 SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE
- &Spell::EffectApplyAreaAura, //143 SPELL_EFFECT_APPLY_AREA_AURA_OWNER
- &Spell::EffectNULL, //144 SPELL_EFFECT_144 Spectral Blast
- &Spell::EffectNULL, //145 SPELL_EFFECT_145 Black Hole Effect
- &Spell::EffectUnused, //146 SPELL_EFFECT_146 unused
- &Spell::EffectQuestFail, //147 SPELL_EFFECT_QUEST_FAIL quest fail
- &Spell::EffectUnused, //148 SPELL_EFFECT_148 unused
- &Spell::EffectNULL, //149 SPELL_EFFECT_149 swoop
- &Spell::EffectUnused, //150 SPELL_EFFECT_150 unused
- &Spell::EffectTriggerRitualOfSummoning, //151 SPELL_EFFECT_TRIGGER_SPELL_2
- &Spell::EffectNULL, //152 SPELL_EFFECT_152 summon Refer-a-Friend
- &Spell::EffectNULL, //153 SPELL_EFFECT_CREATE_PET misc value is creature entry
-};
-
-void Spell::EffectNULL(uint32 /*i*/)
-{
- sLog.outDebug("WORLD: Spell Effect DUMMY");
-}
-
-void Spell::EffectUnused(uint32 /*i*/)
-{
- // NOT USED BY ANY SPELL OR USELESS OR IMPLEMENTED IN DIFFERENT WAY IN MANGOS
-}
-
-void Spell::EffectResurrectNew(uint32 i)
-{
- if(!unitTarget || unitTarget->isAlive())
- return;
-
- if(unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if(!unitTarget->IsInWorld())
- return;
-
- Player* pTarget = ((Player*)unitTarget);
-
- if(pTarget->isRessurectRequested()) // already have one active request
- return;
-
- uint32 health = damage;
- uint32 mana = m_spellInfo->EffectMiscValue[i];
- pTarget->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana);
- SendResurrectRequest(pTarget);
-}
-
-void Spell::EffectInstaKill(uint32 /*i*/)
-{
- if( !unitTarget || !unitTarget->isAlive() )
- return;
-
- // Demonic Sacrifice
- if(m_spellInfo->Id==18788 && unitTarget->GetTypeId()==TYPEID_UNIT)
- {
- uint32 entry = unitTarget->GetEntry();
- uint32 spellID;
- switch(entry)
- {
- case 416: spellID=18789; break; //imp
- case 417: spellID=18792; break; //fellhunter
- case 1860: spellID=18790; break; //void
- case 1863: spellID=18791; break; //succubus
- case 17252: spellID=35701; break; //fellguard
- default:
- sLog.outError("EffectInstaKill: Unhandled creature entry (%u) case.",entry);
- return;
- }
-
- m_caster->CastSpell(m_caster,spellID,true);
- }
-
- if(m_caster==unitTarget) // prevent interrupt message
- finish();
-
- uint32 health = unitTarget->GetHealth();
- m_caster->DealDamage(unitTarget, health, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
-}
-
-void Spell::EffectEnvirinmentalDMG(uint32 i)
-{
- uint32 absorb = 0;
- uint32 resist = 0;
-
- // Note: this hack with damage replace required until GO casting not implemented
- // environment damage spells already have around enemies targeting but this not help in case not existed GO casting support
- // currently each enemy selected explicitly and self cast damage, we prevent apply self casted spell bonuses/etc
- damage = m_spellInfo->EffectBasePoints[i]+m_spellInfo->EffectBaseDice[i];
-
- m_caster->CalcAbsorbResist(m_caster,GetSpellSchoolMask(m_spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
-
- m_caster->SendSpellNonMeleeDamageLog(m_caster, m_spellInfo->Id, damage, GetSpellSchoolMask(m_spellInfo), absorb, resist, false, 0, false);
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- ((Player*)m_caster)->EnvironmentalDamage(m_caster->GetGUID(),DAMAGE_FIRE,damage);
-}
-
-void Spell::EffectSchoolDMG(uint32 effect_idx)
-{
- if( unitTarget && unitTarget->isAlive())
- {
- switch(m_spellInfo->SpellFamilyName)
- {
- case SPELLFAMILY_GENERIC:
- {
- //Gore
- if(m_spellInfo->SpellIconID == 2269 )
- {
- damage+= rand()%2 ? damage : 0;
- }
-
- switch(m_spellInfo->Id) // better way to check unknown
- {
- // Meteor like spells (divided damage to targets)
- case 24340: case 26558: case 28884: // Meteor
- case 36837: case 38903: case 41276: // Meteor
- case 26789: // Shard of the Fallen Star
- case 31436: // Malevolent Cleave
- case 35181: // Dive Bomb
- case 40810: case 43267: case 43268: // Saber Lash
- case 42384: // Brutal Swipe
- case 45150: // Meteor Slash
- {
- uint32 count = 0;
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- if(ihit->effectMask & (1<<effect_idx))
- ++count;
-
- damage /= count; // divide to all targets
- break;
- }
- // percent from health with min
- case 25599: // Thundercrash
- {
- damage = unitTarget->GetHealth() / 2;
- if(damage < 200)
- damage = 200;
- break;
- }
- }
- break;
- }
-
- case SPELLFAMILY_MAGE:
- {
- // Arcane Blast
- if(m_spellInfo->SpellFamilyFlags & 0x20000000LL)
- {
- m_caster->CastSpell(m_caster,36032,true);
- }
- break;
- }
- case SPELLFAMILY_WARRIOR:
- {
- // Bloodthirst
- if(m_spellInfo->SpellFamilyFlags & 0x40000000000LL)
- {
- damage = uint32(damage * (m_caster->GetTotalAttackPowerValue(BASE_ATTACK)) / 100);
- }
- // Shield Slam
- else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL)
- damage += int32(m_caster->GetShieldBlockValue());
- // Victory Rush
- else if(m_spellInfo->SpellFamilyFlags & 0x10000000000LL)
- {
- damage = uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
- m_caster->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, false);
- }
- break;
- }
- case SPELLFAMILY_WARLOCK:
- {
- // Incinerate Rank 1 & 2
- if((m_spellInfo->SpellFamilyFlags & 0x00004000000000LL) && m_spellInfo->SpellIconID==2128)
- {
- // Incinerate does more dmg (dmg*0.25) if the target is Immolated.
- if(unitTarget->HasAuraState(AURA_STATE_IMMOLATE))
- damage += int32(damage*0.25);
- }
- break;
- }
- case SPELLFAMILY_DRUID:
- {
- // Ferocious Bite
- if((m_spellInfo->SpellFamilyFlags & 0x000800000) && m_spellInfo->SpellVisual==6587)
- {
- // converts each extra point of energy into ($f1+$AP/630) additional damage
- float multiple = m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 630 + m_spellInfo->DmgMultiplier[effect_idx];
- damage += int32(m_caster->GetPower(POWER_ENERGY) * multiple);
- m_caster->SetPower(POWER_ENERGY,0);
- }
- // Rake
- else if(m_spellInfo->SpellFamilyFlags & 0x0000000000001000LL)
- {
- damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
- }
- // Swipe
- else if(m_spellInfo->SpellFamilyFlags & 0x0010000000000000LL)
- {
- damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.08f);
- }
- // Starfire
- else if ( m_spellInfo->SpellFamilyFlags & 0x0004LL )
- {
- Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
- for(Unit::AuraList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i)
- {
- // Starfire Bonus (caster)
- switch((*i)->GetModifier()->m_miscvalue)
- {
- case 5481: // Nordrassil Regalia - bonus
- {
- Unit::AuraList const& m_periodicDamageAuras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
- for(Unit::AuraList::const_iterator itr = m_periodicDamageAuras.begin(); itr != m_periodicDamageAuras.end(); ++itr)
- {
- // Moonfire or Insect Swarm (target debuff from any casters)
- if ( (*itr)->GetSpellProto()->SpellFamilyFlags & 0x00200002LL )
- {
- int32 mod = (*i)->GetModifier()->m_amount;
- damage += damage*mod/100;
- break;
- }
- }
- break;
- }
- case 5148: //Improved Starfire - Ivory Idol of the Moongoddes Aura
- {
- damage += (*i)->GetModifier()->m_amount;
- break;
- }
- }
- }
- }
- //Mangle Bonus for the initial damage of Lacerate and Rake
- if((m_spellInfo->SpellFamilyFlags==0x0000000000001000LL && m_spellInfo->SpellIconID==494) ||
- (m_spellInfo->SpellFamilyFlags==0x0000010000000000LL && m_spellInfo->SpellIconID==2246))
- {
- Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY);
- for(Unit::AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
- if((*i)->GetSpellProto()->SpellFamilyFlags & 0x0000044000000000LL && (*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID)
- {
- damage = int32(damage*(100.0f+(*i)->GetModifier()->m_amount)/100.0f);
- break;
- }
- }
- break;
- }
- case SPELLFAMILY_ROGUE:
- {
- // Envenom
- if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & 0x800000000LL))
- {
- // consume from stack dozes not more that have combo-points
- if(uint32 combo = ((Player*)m_caster)->GetComboPoints())
- {
- // count consumed deadly poison doses at target
- uint32 doses = 0;
-
- // remove consumed poison doses
- Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
- for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end() && combo;)
- {
- // Deadly poison (only attacker applied)
- if( (*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && ((*itr)->GetSpellProto()->SpellFamilyFlags & 0x10000) &&
- (*itr)->GetSpellProto()->SpellVisual==5100 && (*itr)->GetCasterGUID()==m_caster->GetGUID() )
- {
- --combo;
- ++doses;
-
- unitTarget->RemoveSingleAuraFromStack((*itr)->GetId(), (*itr)->GetEffIndex());
-
- itr = auras.begin();
- }
- else
- ++itr;
- }
-
- damage *= doses;
- damage += int32(((Player*)m_caster)->GetTotalAttackPowerValue(BASE_ATTACK) * 0.03f * doses);
-
- // Eviscerate and Envenom Bonus Damage (item set effect)
- if(m_caster->GetDummyAura(37169))
- damage += ((Player*)m_caster)->GetComboPoints()*40;
- }
- }
- // Eviscerate
- else if((m_spellInfo->SpellFamilyFlags & 0x00020000LL) && m_caster->GetTypeId()==TYPEID_PLAYER)
- {
- if(uint32 combo = ((Player*)m_caster)->GetComboPoints())
- {
- damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * combo * 0.03f);
-
- // Eviscerate and Envenom Bonus Damage (item set effect)
- if(m_caster->GetDummyAura(37169))
- damage += combo*40;
- }
- }
- break;
- }
- case SPELLFAMILY_HUNTER:
- {
- // Mongoose Bite
- if((m_spellInfo->SpellFamilyFlags & 0x000000002) && m_spellInfo->SpellVisual==342)
- {
- damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2);
- }
- // Arcane Shot
- else if((m_spellInfo->SpellFamilyFlags & 0x00000800) && m_spellInfo->maxLevel > 0)
- {
- damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.15);
- }
- // Steady Shot
- else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL)
- {
- int32 base = irand((int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE),(int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE));
- damage += int32(float(base)/m_caster->GetAttackTime(RANGED_ATTACK)*2800 + m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.2f);
- }
- //Explosive Trap Effect
- else if(m_spellInfo->SpellFamilyFlags & 0x00000004)
- {
- damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.1);
- }
- break;
- }
- case SPELLFAMILY_PALADIN:
- {
- //Judgement of Vengeance
- if((m_spellInfo->SpellFamilyFlags & 0x800000000LL) && m_spellInfo->SpellIconID==2292)
- {
- uint32 stacks = 0;
- Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
- for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
- if((*itr)->GetId() == 31803 && (*itr)->GetCasterGUID()==m_caster->GetGUID())
- ++stacks;
- if(!stacks)
- //No damage if the target isn't affected by this
- damage = -1;
- else
- damage *= stacks;
- }
- break;
- }
- }
-
- if(damage >= 0)
- {
- uint32 finalDamage;
- if(m_originalCaster) // m_caster only passive source of cast
- finalDamage = m_originalCaster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
- else
- finalDamage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
-
- // post effects
- switch(m_spellInfo->SpellFamilyName)
- {
- case SPELLFAMILY_WARRIOR:
- {
- // Bloodthirst
- if(m_spellInfo->SpellFamilyFlags & 0x40000000000LL)
- {
- uint32 BTAura = 0;
- switch(m_spellInfo->Id)
- {
- case 23881: BTAura = 23885; break;
- case 23892: BTAura = 23886; break;
- case 23893: BTAura = 23887; break;
- case 23894: BTAura = 23888; break;
- case 25251: BTAura = 25252; break;
- case 30335: BTAura = 30339; break;
- default:
- sLog.outError("Spell::EffectSchoolDMG: Spell %u not handled in BTAura",m_spellInfo->Id);
- break;
- }
-
- if (BTAura)
- m_caster->CastSpell(m_caster,BTAura,true);
- }
- break;
- }
- case SPELLFAMILY_PRIEST:
- {
- // Shadow Word: Death
- if(finalDamage > 0 && (m_spellInfo->SpellFamilyFlags & 0x0000000200000000LL) && unitTarget->isAlive())
- // deals damage equal to damage done to caster if victim is not killed
- m_caster->SpellNonMeleeDamageLog( m_caster, m_spellInfo->Id, finalDamage, m_IsTriggeredSpell, false);
-
- break;
- }
- case SPELLFAMILY_PALADIN:
- {
- // Judgement of Blood
- if(finalDamage > 0 && (m_spellInfo->SpellFamilyFlags & 0x0000000800000000LL) && m_spellInfo->SpellIconID==153)
- {
- int32 damagePoint = finalDamage * 33 / 100;
- m_caster->CastCustomSpell(m_caster, 32220, &damagePoint, NULL, NULL, true);
- }
- break;
- }
- }
- }
- }
-}
-
-void Spell::EffectDummy(uint32 i)
-{
- if(!unitTarget && !gameObjTarget && !itemTarget)
- return;
-
- // selection by spell family
- switch(m_spellInfo->SpellFamilyName)
- {
- case SPELLFAMILY_GENERIC:
- // Gnomish Poultryizer trinket
- switch(m_spellInfo->Id )
- {
- case 8063: // Deviate Fish
- {
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- uint32 spell_id = 0;
- switch(urand(1,5))
- {
- case 1: spell_id = 8064; break; // Sleepy
- case 2: spell_id = 8065; break; // Invigorate
- case 3: spell_id = 8066; break; // Shrink
- case 4: spell_id = 8067; break; // Party Time!
- case 5: spell_id = 8068; break; // Healthy Spirit
- }
- m_caster->CastSpell(m_caster,spell_id,true,NULL);
- return;
- }
- case 8213: // Savory Deviate Delight
- {
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- uint32 spell_id = 0;
- switch(urand(1,2))
- {
- // Flip Out - ninja
- case 1: spell_id = (m_caster->getGender() == GENDER_MALE ? 8219 : 8220); break;
- // Yaaarrrr - pirate
- case 2: spell_id = (m_caster->getGender() == GENDER_MALE ? 8221 : 8222); break;
- }
- m_caster->CastSpell(m_caster,spell_id,true,NULL);
- return;
- }
- case 8593: // Symbol of life (restore creature to life)
- case 31225: // Shimmering Vessel (restore creature to life)
- {
- if(!unitTarget || unitTarget->GetTypeId()!=TYPEID_UNIT)
- return;
- ((Creature*)unitTarget)->setDeathState(JUST_ALIVED);
- return;
- }
- case 12162: // Deep wounds
- case 12850: // (now good common check for this spells)
- case 12868:
- {
- if(!unitTarget)
- return;
-
- float damage;
- // DW should benefit of attack power, damage percent mods etc.
- // TODO: check if using offhand damage is correct and if it should be divided by 2
- if (m_caster->haveOffhandWeapon() && m_caster->getAttackTimer(BASE_ATTACK) > m_caster->getAttackTimer(OFF_ATTACK))
- damage = (m_caster->GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE))/2;
- else
- damage = (m_caster->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXDAMAGE))/2;
-
- switch (m_spellInfo->Id)
- {
- case 12850: damage *= 0.2f; break;
- case 12162: damage *= 0.4f; break;
- case 12868: damage *= 0.6f; break;
- default:
- sLog.outError("Spell::EffectDummy: Spell %u not handled in DW",m_spellInfo->Id);
- return;
- };
-
- int32 deepWoundsDotBasePoints0 = int32(damage / 4);
- m_caster->CastCustomSpell(unitTarget, 12721, &deepWoundsDotBasePoints0, NULL, NULL, true, NULL);
- return;
- }
- case 12975: //Last Stand
- {
- int32 healthModSpellBasePoints0 = int32(m_caster->GetMaxHealth()*0.3);
- m_caster->CastCustomSpell(m_caster, 12976, &healthModSpellBasePoints0, NULL, NULL, true, NULL);
- return;
- }
- case 13120: // net-o-matic
- {
- if(!unitTarget)
- return;
-
- uint32 spell_id = 0;
-
- uint32 roll = urand(0, 99);
-
- if(roll < 2) // 2% for 30 sec self root (off-like chance unknown)
- spell_id = 16566;
- else if(roll < 4) // 2% for 20 sec root, charge to target (off-like chance unknown)
- spell_id = 13119;
- else // normal root
- spell_id = 13099;
-
- m_caster->CastSpell(unitTarget,spell_id,true,NULL);
- return;
- }
- case 13567: // Dummy Trigger
- {
- // can be used for different aura triggreing, so select by aura
- if(!m_triggeredByAuraSpell || !unitTarget)
- return;
-
- switch(m_triggeredByAuraSpell->Id)
- {
- case 26467: // Persistent Shield
- m_caster->CastCustomSpell(unitTarget, 26470, &damage, NULL, NULL, true);
- break;
- default:
- sLog.outError("EffectDummy: Non-handled case for spell 13567 for triggered aura %u",m_triggeredByAuraSpell->Id);
- break;
- }
- return;
- }
- case 14185: // Preparation Rogue
- {
- if(m_caster->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- //immediately finishes the cooldown on certain Rogue abilities
- const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap();
- for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
- {
- uint32 classspell = itr->first;
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell);
-
- if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & 0x26000000860LL))
- {
- ((Player*)m_caster)->RemoveSpellCooldown(classspell);
-
- WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
- data << uint32(classspell);
- data << uint64(m_caster->GetGUID());
- ((Player*)m_caster)->GetSession()->SendPacket(&data);
- }
- }
- return;
- }
- case 15998: // Capture Worg Pup
- case 29435: // Capture Female Kaliri Hatchling
- {
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
- return;
-
- Creature* creatureTarget = (Creature*)unitTarget;
- creatureTarget->setDeathState(JUST_DIED);
- creatureTarget->RemoveCorpse();
- creatureTarget->SetHealth(0); // just for nice GM-mode view
- return;
- }
- case 16589: // Noggenfogger Elixir
- {
- if(m_caster->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- uint32 spell_id = 0;
- switch(urand(1,3))
- {
- case 1: spell_id = 16595; break;
- case 2: spell_id = 16593; break;
- default:spell_id = 16591; break;
- }
-
- m_caster->CastSpell(m_caster,spell_id,true,NULL);
- return;
- }
- case 17251: // Spirit Healer Res
- {
- if(!unitTarget || !m_originalCaster)
- return;
-
- if(m_originalCaster->GetTypeId() == TYPEID_PLAYER)
- {
- WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8);
- data << unitTarget->GetGUID();
- ((Player*)m_originalCaster)->GetSession()->SendPacket( &data );
- }
- return;
- }
- case 17271: // Test Fetid Skull
- {
- if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- uint32 spell_id = roll_chance_i(50) ? 17269 : 17270;
-
- m_caster->CastSpell(m_caster,spell_id,true,NULL);
- return;
- }
- case 20577: // Cannibalize
- if (unitTarget)
- m_caster->CastSpell(m_caster,20578,false,NULL);
- return;
- case 23019: // Crystal Prison Dummy DND
- {
- if(!unitTarget || !unitTarget->isAlive() || unitTarget->GetTypeId() != TYPEID_UNIT || ((Creature*)unitTarget)->isPet())
- return;
-
- Creature* creatureTarget = (Creature*)unitTarget;
- if(creatureTarget->isPet())
- return;
-
- creatureTarget->setDeathState(JUST_DIED);
- creatureTarget->RemoveCorpse();
- creatureTarget->SetHealth(0); // just for nice GM-mode view
-
- GameObject* pGameObj = new GameObject;
-
- Map *map = creatureTarget->GetMap();
-
- if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), 179644, map,
- creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(),
- creatureTarget->GetOrientation(), 0, 0, 0, 0, 100, 1) )
- {
- delete pGameObj;
- return;
- }
-
- pGameObj->SetRespawnTime(creatureTarget->GetRespawnTime()-time(NULL));
- pGameObj->SetOwnerGUID(m_caster->GetGUID() );
- pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() );
- pGameObj->SetSpellId(m_spellInfo->Id);
-
- DEBUG_LOG("AddObject at SpellEfects.cpp EffectDummy\n");
- map->Add(pGameObj);
-
- WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
- data << uint64(pGameObj->GetGUID());
- m_caster->SendMessageToSet(&data,true);
-
- return;
- }
- case 23074: // Arc. Dragonling
- if (!m_CastItem) return;
- m_caster->CastSpell(m_caster,19804,true,m_CastItem);
- return;
- case 23075: // Mithril Mechanical Dragonling
- if (!m_CastItem) return;
- m_caster->CastSpell(m_caster,12749,true,m_CastItem);
- return;
- case 23076: // Mechanical Dragonling
- if (!m_CastItem) return;
- m_caster->CastSpell(m_caster,4073,true,m_CastItem);
- return;
- case 23133: // Gnomish Battle Chicken
- if (!m_CastItem) return;
- m_caster->CastSpell(m_caster,13166,true,m_CastItem);
- return;
- case 23448: // Ultrasafe Transporter: Gadgetzan - backfires
- {
- int32 r = irand(0, 119);
- if ( r < 20 ) // 1/6 polymorph
- m_caster->CastSpell(m_caster,23444,true);
- else if ( r < 100 ) // 4/6 evil twin
- m_caster->CastSpell(m_caster,23445,true);
- else // 1/6 miss the target
- m_caster->CastSpell(m_caster,36902,true);
- return;
- }
- case 23453: // Ultrasafe Transporter: Gadgetzan
- if ( roll_chance_i(50) ) // success
- m_caster->CastSpell(m_caster,23441,true);
- else // failure
- m_caster->CastSpell(m_caster,23446,true);
- return;
- case 23645: // Hourglass Sand
- m_caster->RemoveAurasDueToSpell(23170);
- return;
- case 23725: // Gift of Life (warrior bwl trinket)
- m_caster->CastSpell(m_caster,23782,true);
- m_caster->CastSpell(m_caster,23783,true);
- return;
- case 25860: // Reindeer Transformation
- {
- if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED))
- return;
-
- float flyspeed = m_caster->GetSpeedRate(MOVE_FLY);
- float speed = m_caster->GetSpeedRate(MOVE_RUN);
-
- m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
-
- //5 different spells used depending on mounted speed and if mount can fly or not
- if (flyspeed >= 4.1f)
- m_caster->CastSpell(m_caster, 44827, true); //310% flying Reindeer
- else if (flyspeed >= 3.8f)
- m_caster->CastSpell(m_caster, 44825, true); //280% flying Reindeer
- else if (flyspeed >= 1.6f)
- m_caster->CastSpell(m_caster, 44824, true); //60% flying Reindeer
- else if (speed >= 2.0f)
- m_caster->CastSpell(m_caster, 25859, true); //100% ground Reindeer
- else
- m_caster->CastSpell(m_caster, 25858, true); //60% ground Reindeer
-
- return;
- }
- //case 26074: // Holiday Cheer
- // return; -- implemented at client side
- case 28006: // Arcane Cloaking
- {
- if( unitTarget->GetTypeId() == TYPEID_PLAYER )
- m_caster->CastSpell(unitTarget,29294,true);
- return;
- }
- case 28730: // Arcane Torrent (Mana)
- {
- int32 count = 0;
- Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
- for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i)
- if ((*i)->GetId() == 28734)
- ++count;
- if (count)
- {
- m_caster->RemoveAurasDueToSpell(28734);
- int32 bp = damage * count;
- m_caster->CastCustomSpell(m_caster, 28733, &bp, NULL, NULL, true);
- }
- return;
- }
- case 29200: // Purify Helboar Meat
- {
- if( m_caster->GetTypeId() != TYPEID_PLAYER )
- return;
-
- uint32 spell_id = roll_chance_i(50) ? 29277 : 29278;
-
- m_caster->CastSpell(m_caster,spell_id,true,NULL);
- return;
- }
- case 29858: // Soulshatter
- if (unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT && unitTarget->IsHostileTo(m_caster))
- m_caster->CastSpell(unitTarget,32835,true);
- return;
- case 30458: // Nigh Invulnerability
- if (!m_CastItem) return;
- if(roll_chance_i(86)) // success
- m_caster->CastSpell(m_caster, 30456, true, m_CastItem);
- else // backfire in 14% casts
- m_caster->CastSpell(m_caster, 30457, true, m_CastItem);
- return;
- case 30507: // Poultryizer
- if (!m_CastItem) return;
- if(roll_chance_i(80)) // success
- m_caster->CastSpell(unitTarget, 30501, true, m_CastItem);
- else // backfire 20%
- m_caster->CastSpell(unitTarget, 30504, true, m_CastItem);
- return;
- case 33060: // Make a Wish
- {
- if(m_caster->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- uint32 spell_id = 0;
-
- switch(urand(1,5))
- {
- case 1: spell_id = 33053; break;
- case 2: spell_id = 33057; break;
- case 3: spell_id = 33059; break;
- case 4: spell_id = 33062; break;
- case 5: spell_id = 33064; break;
- }
-
- m_caster->CastSpell(m_caster,spell_id,true,NULL);
- return;
- }
- case 35745:
- {
- uint32 spell_id;
- switch(m_caster->GetAreaId())
- {
- case 3900: spell_id = 35743; break;
- case 3742: spell_id = 35744; break;
- default: return;
- }
-
- m_caster->CastSpell(m_caster,spell_id,true);
- return;
- }
- case 37674: // Chaos Blast
- if(unitTarget)
- m_caster->CastSpell(unitTarget,37675,true);
- return;
- case 44875: // Complete Raptor Capture
- {
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
- return;
-
- Creature* creatureTarget = (Creature*)unitTarget;
-
- creatureTarget->setDeathState(JUST_DIED);
- creatureTarget->RemoveCorpse();
- creatureTarget->SetHealth(0); // just for nice GM-mode view
-
- //cast spell Raptor Capture Credit
- m_caster->CastSpell(m_caster,42337,true,NULL);
- return;
- }
- case 37573: //Temporal Phase Modulator
- {
- if(!unitTarget)
- return;
-
- TemporarySummon* tempSummon = dynamic_cast<TemporarySummon*>(unitTarget);
- if(!tempSummon)
- return;
-
- uint32 health = tempSummon->GetHealth();
- const uint32 entry_list[6] = {21821, 21820, 21817};
-
- float x = tempSummon->GetPositionX();
- float y = tempSummon->GetPositionY();
- float z = tempSummon->GetPositionZ();
- float o = tempSummon->GetOrientation();
-
- tempSummon->UnSummon();
-
- Creature* pCreature = m_caster->SummonCreature(entry_list[urand(0, 2)], x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,180000);
- if (!pCreature)
- return;
-
- pCreature->SetHealth(health);
-
- if(pCreature->AI())
- pCreature->AI()->AttackStart(m_caster);
-
- return;
- }
- case 34665: //Administer Antidote
- {
- if(!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER )
- return;
-
- if(!unitTarget)
- return;
-
- TemporarySummon* tempSummon = dynamic_cast<TemporarySummon*>(unitTarget);
- if(!tempSummon)
- return;
-
- uint32 health = tempSummon->GetHealth();
-
- float x = tempSummon->GetPositionX();
- float y = tempSummon->GetPositionY();
- float z = tempSummon->GetPositionZ();
- float o = tempSummon->GetOrientation();
- tempSummon->UnSummon();
-
- Creature* pCreature = m_caster->SummonCreature(16992, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,180000);
- if (!pCreature)
- return;
-
- pCreature->SetHealth(health);
- ((Player*)m_caster)->KilledMonster(16992,pCreature->GetGUID());
-
- if (pCreature->AI())
- pCreature->AI()->AttackStart(m_caster);
-
- return;
- }
- case 44997: // Converting Sentry
- {
- //Converted Sentry Credit
- m_caster->CastSpell(m_caster, 45009, true);
- return;
- }
- case 45030: // Impale Emissary
- {
- // Emissary of Hate Credit
- m_caster->CastSpell(m_caster, 45088, true);
- return;
- }
- case 50243: // Teach Language
- {
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- // spell has a 1/3 chance to trigger one of the below
- if(roll_chance_i(66))
- return;
- if(((Player*)m_caster)->GetTeam() == ALLIANCE)
- {
- // 1000001 - gnomish binary
- m_caster->CastSpell(m_caster, 50242, true);
- }
- else
- {
- // 01001000 - goblin binary
- m_caster->CastSpell(m_caster, 50246, true);
- }
-
- return;
- }
- case 51582: //Rocket Boots Engaged (Rocket Boots Xtreme and Rocket Boots Xtreme Lite)
- {
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if(BattleGround* bg = ((Player*)m_caster)->GetBattleGround())
- bg->EventPlayerDroppedFlag((Player*)m_caster);
-
- m_caster->CastSpell(m_caster, 30452, true, NULL);
- return;
- }
- }
-
- //All IconID Check in there
- switch(m_spellInfo->SpellIconID)
- {
- // Berserking (troll racial traits)
- case 1661:
- {
- uint32 healthPerc = uint32((float(m_caster->GetHealth())/m_caster->GetMaxHealth())*100);
- int32 melee_mod = 10;
- if (healthPerc <= 40)
- melee_mod = 30;
- if (healthPerc < 100 && healthPerc > 40)
- melee_mod = 10+(100-healthPerc)/3;
-
- int32 hasteModBasePoints0 = melee_mod; // (EffectBasePoints[0]+1)-1+(5-melee_mod) = (melee_mod-1+1)-1+5-melee_mod = 5-1
- int32 hasteModBasePoints1 = (5-melee_mod);
- int32 hasteModBasePoints2 = 5;
-
- // FIXME: custom spell required this aura state by some unknown reason, we not need remove it anyway
- m_caster->ModifyAuraState(AURA_STATE_BERSERKING,true);
- m_caster->CastCustomSpell(m_caster,26635,&hasteModBasePoints0,&hasteModBasePoints1,&hasteModBasePoints2,true,NULL);
- return;
- }
- }
- break;
- case SPELLFAMILY_MAGE:
- switch(m_spellInfo->Id )
- {
- case 11958: // Cold Snap
- {
- if(m_caster->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- // immediately finishes the cooldown on Frost spells
- const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap();
- for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
- {
- if (itr->second->state == PLAYERSPELL_REMOVED)
- continue;
-
- uint32 classspell = itr->first;
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell);
-
- if( spellInfo->SpellFamilyName == SPELLFAMILY_MAGE &&
- (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) &&
- spellInfo->Id != 11958 && GetSpellRecoveryTime(spellInfo) > 0 )
- {
- ((Player*)m_caster)->RemoveSpellCooldown(classspell);
-
- WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
- data << uint32(classspell);
- data << uint64(m_caster->GetGUID());
- ((Player*)m_caster)->GetSession()->SendPacket(&data);
- }
- }
- return;
- }
- case 32826:
- {
- if ( unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT )
- {
- //Polymorph Cast Visual Rank 1
- const uint32 spell_list[6] = {32813, 32816, 32817, 32818, 32819, 32820};
- unitTarget->CastSpell( unitTarget, spell_list[urand(0, 5)], true);
- }
- return;
- }
- }
- break;
- case SPELLFAMILY_WARRIOR:
- // Charge
- if(m_spellInfo->SpellFamilyFlags & 0x1 && m_spellInfo->SpellVisual == 867)
- {
- int32 chargeBasePoints0 = damage;
- m_caster->CastCustomSpell(m_caster,34846,&chargeBasePoints0,NULL,NULL,true);
- return;
- }
- // Execute
- if(m_spellInfo->SpellFamilyFlags & 0x20000000)
- {
- if(!unitTarget)
- return;
-
- int32 basePoints0 = damage+int32(m_caster->GetPower(POWER_RAGE) * m_spellInfo->DmgMultiplier[i]);
- m_caster->CastCustomSpell(unitTarget, 20647, &basePoints0, NULL, NULL, true, 0);
- m_caster->SetPower(POWER_RAGE,0);
- return;
- }
- if(m_spellInfo->Id==21977) //Warrior's Wrath
- {
- if(!unitTarget)
- return;
-
- m_caster->CastSpell(unitTarget,21887,true); // spell mod
- return;
- }
- break;
- case SPELLFAMILY_WARLOCK:
- //Life Tap (only it have this with dummy effect)
- if (m_spellInfo->SpellFamilyFlags == 0x40000)
- {
- float cost = m_currentBasePoints[0]+1;
-
- if(Player* modOwner = m_caster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, cost,this);
-
- int32 dmg = m_caster->SpellDamageBonus(m_caster, m_spellInfo,uint32(cost > 0 ? cost : 0), SPELL_DIRECT_DAMAGE);
-
- if(int32(m_caster->GetHealth()) > dmg)
- {
- // Shouldn't Appear in Combat Log
- m_caster->ModifyHealth(-dmg);
-
- int32 mana = dmg;
-
- Unit::AuraList const& auraDummy = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
- for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr != auraDummy.end(); ++itr)
- {
- // only Imp. Life Tap have this in combination with dummy aura
- if((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 208)
- mana = ((*itr)->GetModifier()->m_amount + 100)* mana / 100;
- }
-
- m_caster->CastCustomSpell(m_caster,31818,&mana,NULL,NULL,true,NULL);
-
- // Mana Feed
- int32 manaFeedVal = m_caster->CalculateSpellDamage(m_spellInfo,1, m_spellInfo->EffectBasePoints[1],m_caster);
- manaFeedVal = manaFeedVal * mana / 100;
- if(manaFeedVal > 0)
- m_caster->CastCustomSpell(m_caster,32553,&manaFeedVal,NULL,NULL,true,NULL);
- }
- else
- SendCastResult(SPELL_FAILED_FIZZLE);
- return;
- }
- break;
- case SPELLFAMILY_PRIEST:
- switch(m_spellInfo->Id )
- {
- case 28598: // Touch of Weakness triggered spell
- {
- if(!unitTarget || !m_triggeredByAuraSpell)
- return;
-
- uint32 spellid = 0;
- switch(m_triggeredByAuraSpell->Id)
- {
- case 2652: spellid = 2943; break; // Rank 1
- case 19261: spellid = 19249; break; // Rank 2
- case 19262: spellid = 19251; break; // Rank 3
- case 19264: spellid = 19252; break; // Rank 4
- case 19265: spellid = 19253; break; // Rank 5
- case 19266: spellid = 19254; break; // Rank 6
- case 25461: spellid = 25460; break; // Rank 7
- default:
- sLog.outError("Spell::EffectDummy: Spell 28598 triggered by unhandeled spell %u",m_triggeredByAuraSpell->Id);
- return;
- }
- m_caster->CastSpell(unitTarget, spellid, true, NULL);
- return;
- }
- }
- break;
- case SPELLFAMILY_DRUID:
- switch(m_spellInfo->Id )
- {
- case 5420: // Tree of Life passive
- {
- // Tree of Life area effect
- int32 health_mod = int32(m_caster->GetStat(STAT_SPIRIT)/4);
- m_caster->CastCustomSpell(m_caster,34123,&health_mod,NULL,NULL,true,NULL);
- return;
- }
- }
- break;
- case SPELLFAMILY_ROGUE:
- switch(m_spellInfo->Id )
- {
- case 31231: // Cheat Death
- {
- m_caster->CastSpell(m_caster,45182,true);
- return;
- }
- case 5938: // Shiv
- {
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player *pCaster = ((Player*)m_caster);
-
- Item *item = pCaster->GetWeaponForAttack(OFF_ATTACK);
- if(!item)
- return;
-
- // all poison enchantments is temporary
- uint32 enchant_id = item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT);
- if(!enchant_id)
- return;
-
- SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!pEnchant)
- return;
-
- for (int s=0;s<3;s++)
- {
- if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
- continue;
-
- SpellEntry const* combatEntry = sSpellStore.LookupEntry(pEnchant->spellid[s]);
- if(!combatEntry || combatEntry->Dispel != DISPEL_POISON)
- continue;
-
- m_caster->CastSpell(unitTarget, combatEntry, true, item);
- }
-
- m_caster->CastSpell(unitTarget, 5940, true);
- return;
- }
- }
- break;
- case SPELLFAMILY_HUNTER:
- // Steady Shot
- if(m_spellInfo->SpellFamilyFlags & 0x100000000LL)
- {
- if( !unitTarget || !unitTarget->isAlive())
- return;
-
- bool found = false;
-
- // check dazed affect
- Unit::AuraList const& decSpeedList = unitTarget->GetAurasByType(SPELL_AURA_MOD_DECREASE_SPEED);
- for(Unit::AuraList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter)
- {
- if((*iter)->GetSpellProto()->SpellIconID==15 && (*iter)->GetSpellProto()->Dispel==0)
- {
- found = true;
- break;
- }
- }
-
- if(found)
- m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
- return;
- }
- // Kill command
- if(m_spellInfo->SpellFamilyFlags & 0x00080000000000LL)
- {
- if(m_caster->getClass()!=CLASS_HUNTER)
- return;
-
- // clear hunter crit aura state
- m_caster->ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE,false);
-
- // additional damage from pet to pet target
- Pet* pet = m_caster->GetPet();
- if(!pet || !pet->getVictim())
- return;
-
- uint32 spell_id = 0;
- switch (m_spellInfo->Id)
- {
- case 34026: spell_id = 34027; break; // rank 1
- default:
- sLog.outError("Spell::EffectDummy: Spell %u not handled in KC",m_spellInfo->Id);
- return;
- }
-
- pet->CastSpell(pet->getVictim(), spell_id, true);
- return;
- }
-
- switch(m_spellInfo->Id)
- {
- case 23989: //Readiness talent
- {
- if(m_caster->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- //immediately finishes the cooldown for hunter abilities
- const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap();
- for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
- {
- uint32 classspell = itr->first;
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell);
-
- if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && spellInfo->Id != 23989 && GetSpellRecoveryTime(spellInfo) > 0 )
- {
- ((Player*)m_caster)->RemoveSpellCooldown(classspell);
-
- WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
- data << uint32(classspell);
- data << uint64(m_caster->GetGUID());
- ((Player*)m_caster)->GetSession()->SendPacket(&data);
- }
- }
- return;
- }
- case 37506: // Scatter Shot
- {
- if (m_caster->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- // break Auto Shot and autohit
- m_caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
- m_caster->AttackStop();
- ((Player*)m_caster)->SendAttackSwingCancelAttack();
- return;
- }
- }
- break;
- case SPELLFAMILY_PALADIN:
- switch(m_spellInfo->SpellIconID)
- {
- case 156: // Holy Shock
- {
- if(!unitTarget)
- return;
-
- int hurt = 0;
- int heal = 0;
-
- switch(m_spellInfo->Id)
- {
- case 20473: hurt = 25912; heal = 25914; break;
- case 20929: hurt = 25911; heal = 25913; break;
- case 20930: hurt = 25902; heal = 25903; break;
- case 27174: hurt = 27176; heal = 27175; break;
- case 33072: hurt = 33073; heal = 33074; break;
- default:
- sLog.outError("Spell::EffectDummy: Spell %u not handled in HS",m_spellInfo->Id);
- return;
- }
-
- if(m_caster->IsFriendlyTo(unitTarget))
- m_caster->CastSpell(unitTarget, heal, true, 0);
- else
- m_caster->CastSpell(unitTarget, hurt, true, 0);
-
- return;
- }
- case 561: // Judgement of command
- {
- if(!unitTarget)
- return;
-
- uint32 spell_id = m_currentBasePoints[i]+1;
- SpellEntry const* spell_proto = sSpellStore.LookupEntry(spell_id);
- if(!spell_proto)
- return;
-
- if( !unitTarget->hasUnitState(UNIT_STAT_STUNNED) && m_caster->GetTypeId()==TYPEID_PLAYER)
- {
- // decreased damage (/2) for non-stunned target.
- SpellModifier *mod = new SpellModifier;
- mod->op = SPELLMOD_DAMAGE;
- mod->value = -50;
- mod->type = SPELLMOD_PCT;
- mod->spellId = m_spellInfo->Id;
- mod->effectId = i;
- mod->lastAffected = NULL;
- mod->mask = 0x0000020000000000LL;
- mod->charges = 0;
-
- ((Player*)m_caster)->AddSpellMod(mod, true);
- m_caster->CastSpell(unitTarget,spell_proto,true,NULL);
- // mod deleted
- ((Player*)m_caster)->AddSpellMod(mod, false);
- }
- else
- m_caster->CastSpell(unitTarget,spell_proto,true,NULL);
-
- return;
- }
- }
-
- switch(m_spellInfo->Id)
- {
- case 31789: // Righteous Defense (step 1)
- {
- // 31989 -> dummy effect (step 1) + dummy effect (step 2) -> 31709 (taunt like spell for each target)
-
- // non-standard cast requirement check
- if (!unitTarget || unitTarget->getAttackers().empty())
- {
- // clear cooldown at fail
- if(m_caster->GetTypeId()==TYPEID_PLAYER)
- {
- ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id);
-
- WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
- data << uint32(m_spellInfo->Id);
- data << uint64(m_caster->GetGUID());
- ((Player*)m_caster)->GetSession()->SendPacket(&data);
- }
-
- SendCastResult(SPELL_FAILED_TARGET_AFFECTING_COMBAT);
- return;
- }
-
- // Righteous Defense (step 2) (in old version 31980 dummy effect)
- // Clear targets for eff 1
- for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
- ihit->effectMask &= ~(1<<1);
-
- // not empty (checked)
- Unit::AttackerSet const& attackers = unitTarget->getAttackers();
-
- // chance to be selected from list
- float chance = 100.0f/attackers.size();
- uint32 count=0;
- for(Unit::AttackerSet::const_iterator aItr = attackers.begin(); aItr != attackers.end() && count < 3; ++aItr)
- {
- if(!roll_chance_f(chance))
- continue;
- ++count;
- AddUnitTarget((*aItr), 1);
- }
-
- // now let next effect cast spell at each target.
- return;
- }
- case 37877: // Blessing of Faith
- {
- if(!unitTarget)
- return;
-
- uint32 spell_id = 0;
- switch(unitTarget->getClass())
- {
- case CLASS_DRUID: spell_id = 37878; break;
- case CLASS_PALADIN: spell_id = 37879; break;
- case CLASS_PRIEST: spell_id = 37880; break;
- case CLASS_SHAMAN: spell_id = 37881; break;
- default: return; // ignore for not healing classes
- }
-
- m_caster->CastSpell(m_caster,spell_id,true);
- return;
- }
- }
- break;
- case SPELLFAMILY_SHAMAN:
- //Shaman Rockbiter Weapon
- if (m_spellInfo->SpellFamilyFlags == 0x400000)
- {
- uint32 spell_id = 0;
- switch(m_spellInfo->Id)
- {
- case 8017: spell_id = 36494; break; // Rank 1
- case 8018: spell_id = 36750; break; // Rank 2
- case 8019: spell_id = 36755; break; // Rank 3
- case 10399: spell_id = 36759; break; // Rank 4
- case 16314: spell_id = 36763; break; // Rank 5
- case 16315: spell_id = 36766; break; // Rank 6
- case 16316: spell_id = 36771; break; // Rank 7
- case 25479: spell_id = 36775; break; // Rank 8
- case 25485: spell_id = 36499; break; // Rank 9
- default:
- sLog.outError("Spell::EffectDummy: Spell %u not handled in RW",m_spellInfo->Id);
- return;
- }
-
- SpellEntry const *spellInfo = sSpellStore.LookupEntry( spell_id );
-
- if(!spellInfo)
- {
- sLog.outError("WORLD: unknown spell id %i\n", spell_id);
- return;
- }
-
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- for(int i = BASE_ATTACK; i <= OFF_ATTACK; ++i)
- {
- if(Item* item = ((Player*)m_caster)->GetWeaponForAttack(WeaponAttackType(i)))
- {
- if(item->IsFitToSpellRequirements(m_spellInfo))
- {
- Spell *spell = new Spell(m_caster, spellInfo, true);
-
- // enchanting spell selected by calculated damage-per-sec in enchanting effect
- // at calculation applied affect from Elemental Weapons talent
- // real enchantment damage-1
- spell->m_currentBasePoints[1] = damage-1;
-
- SpellCastTargets targets;
- targets.setItemTarget( item );
- spell->prepare(&targets);
- }
- }
- }
- return;
- }
-
- if(m_spellInfo->Id == 39610) // Mana-Tide Totem effect
- {
- if(!unitTarget || unitTarget->getPowerType() != POWER_MANA)
- return;
-
- // Regenerate 6% of Total Mana Every 3 secs
- int32 EffectBasePoints0 = unitTarget->GetMaxPower(POWER_MANA) * damage / 100;
- m_caster->CastCustomSpell(unitTarget,39609,&EffectBasePoints0,NULL,NULL,true,NULL,NULL,m_originalCasterGUID);
- return;
- }
-
- break;
- }
-
- // pet auras
- if(PetAura const* petSpell = spellmgr.GetPetAura(m_spellInfo->Id))
- {
- m_caster->AddPetAura(petSpell);
- return;
- }
-}
-
-void Spell::EffectTriggerSpellWithValue(uint32 i)
-{
- uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
-
- // normal case
- SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
-
- if(!spellInfo)
- {
- sLog.outError("EffectTriggerSpellWithValue of spell %u: triggering unknown spell id %i\n", m_spellInfo->Id,triggered_spell_id);
- return;
- }
-
- int32 bp = damage;
- m_caster->CastCustomSpell(unitTarget,triggered_spell_id,&bp,&bp,&bp,true,NULL,NULL,m_originalCasterGUID);
-}
-
-void Spell::EffectTriggerRitualOfSummoning(uint32 i)
-{
- uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
- SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
-
- if(!spellInfo)
- {
- sLog.outError("EffectTriggerRitualOfSummoning of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id);
- return;
- }
-
- finish();
- Spell *spell = new Spell(m_caster, spellInfo, true);
-
- SpellCastTargets targets;
- targets.setUnitTarget( unitTarget);
- spell->prepare(&targets);
-
- m_caster->SetCurrentCastedSpell(spell);
- spell->m_selfContainer = &(m_caster->m_currentSpells[spell->GetCurrentContainer()]);
-
-}
-
-void Spell::EffectForceCast(uint32 i)
-{
- if( !unitTarget )
- return;
-
- uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
-
- // normal case
- SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
-
- if(!spellInfo)
- {
- sLog.outError("EffectForceCast of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id);
- return;
- }
-
- unitTarget->CastSpell(unitTarget,spellInfo,true,NULL,NULL,m_originalCasterGUID);
-}
-
-void Spell::EffectTriggerSpell(uint32 i)
-{
- uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
-
- // special cases
- switch(triggered_spell_id)
- {
- // Vanish
- case 18461:
- {
- m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
- m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED);
- m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED);
-
- // if this spell is given to NPC it must handle rest by it's own AI
- if ( m_caster->GetTypeId() != TYPEID_PLAYER )
- return;
-
- // get highest rank of the Stealth spell
- uint32 spellId = 0;
- const PlayerSpellMap& sp_list = ((Player*)m_caster)->GetSpellMap();
- for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
- {
- // only highest rank is shown in spell book, so simply check if shown in spell book
- if(!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED)
- continue;
-
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
- if (!spellInfo)
- continue;
-
- if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE_STEALTH)
- {
- spellId = spellInfo->Id;
- break;
- }
- }
-
- // no Stealth spell found
- if (!spellId)
- return;
-
- // reset cooldown on it if needed
- if(((Player*)m_caster)->HasSpellCooldown(spellId))
- ((Player*)m_caster)->RemoveSpellCooldown(spellId);
-
- m_caster->CastSpell(m_caster, spellId, true);
- return;
- }
- // just skip
- case 23770: // Sayge's Dark Fortune of *
- // not exist, common cooldown can be implemented in scripts if need.
- return;
- // Brittle Armor - (need add max stack of 24575 Brittle Armor)
- case 29284:
- {
- const SpellEntry *spell = sSpellStore.LookupEntry(24575);
- if (!spell)
- return;
-
- for (int i=0; i < spell->StackAmount; ++i)
- m_caster->CastSpell(unitTarget,spell->Id, true, m_CastItem, NULL, m_originalCasterGUID);
- return;
- }
- // Mercurial Shield - (need add max stack of 26464 Mercurial Shield)
- case 29286:
- {
- const SpellEntry *spell = sSpellStore.LookupEntry(26464);
- if (!spell)
- return;
-
- for (int i=0; i < spell->StackAmount; ++i)
- m_caster->CastSpell(unitTarget,spell->Id, true, m_CastItem, NULL, m_originalCasterGUID);
- return;
- }
- // Righteous Defense
- case 31980:
- {
- m_caster->CastSpell(unitTarget, 31790, true,m_CastItem,NULL,m_originalCasterGUID);
- return;
- }
- // Cloak of Shadows
- case 35729 :
- {
- Unit::AuraMap& Auras = m_caster->GetAuras();
- for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end(); ++iter)
- {
- // remove all harmful spells on you...
- if( // ignore positive and passive auras
- !iter->second->IsPositive() && !iter->second->IsPassive() &&
- // ignore physical auras
- (GetSpellSchoolMask(iter->second->GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL)==0 &&
- // ignore immunity persistent spells
- !( iter->second->GetSpellProto()->AttributesEx & 0x10000 ) )
- {
- m_caster->RemoveAurasDueToSpell(iter->second->GetSpellProto()->Id);
- iter = Auras.begin();
- }
- }
- return;
- }
- // Priest Shadowfiend (34433) need apply mana gain trigger aura on pet
- case 41967:
- {
- if (Unit *pet = m_caster->GetPet())
- pet->CastSpell(pet, 28305, true);
- return;
- }
- }
-
- // normal case
- SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
-
- if(!spellInfo)
- {
- sLog.outError("EffectTriggerSpell of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id);
- return;
- }
-
- // some triggered spells require specific equipment
- if(spellInfo->EquippedItemClass >=0 && m_caster->GetTypeId()==TYPEID_PLAYER)
- {
- // main hand weapon required
- if(spellInfo->AttributesEx3 & SPELL_ATTR_EX3_MAIN_HAND)
- {
- Item* item = ((Player*)m_caster)->GetWeaponForAttack(BASE_ATTACK);
-
- // skip spell if no weapon in slot or broken
- if(!item || item->IsBroken() )
- return;
-
- // skip spell if weapon not fit to triggered spell
- if(!item->IsFitToSpellRequirements(spellInfo))
- return;
- }
-
- // offhand hand weapon required
- if(spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND)
- {
- Item* item = ((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK);
-
- // skip spell if no weapon in slot or broken
- if(!item || item->IsBroken() )
- return;
-
- // skip spell if weapon not fit to triggered spell
- if(!item->IsFitToSpellRequirements(spellInfo))
- return;
- }
- }
-
- // some triggered spells must be casted instantly (for example, if next effect case instant kill caster)
- bool instant = false;
- for(uint32 j = i+1; j < 3; ++j)
- {
- if(m_spellInfo->Effect[j]==SPELL_EFFECT_INSTAKILL && m_spellInfo->EffectImplicitTargetA[j]==TARGET_SELF)
- {
- instant = true;
- break;
- }
- }
-
- if(instant)
- {
- if (unitTarget)
- m_caster->CastSpell(unitTarget,spellInfo,true,m_CastItem,NULL,m_originalCasterGUID);
- }
- else
- m_TriggerSpells.push_back(spellInfo);
-}
-
-void Spell::EffectTriggerMissileSpell(uint32 effect_idx)
-{
- uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[effect_idx];
-
- // normal case
- SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
-
- if(!spellInfo)
- {
- sLog.outError("EffectTriggerMissileSpell of spell %u: triggering unknown spell id %effect_idx", m_spellInfo->Id,triggered_spell_id);
- return;
- }
-
- if (m_CastItem)
- DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
-
- Spell *spell = new Spell(m_caster, spellInfo, true, m_originalCasterGUID );
-
- SpellCastTargets targets;
- targets.setDestination(m_targets.m_destX,m_targets.m_destY,m_targets.m_destZ);
- spell->m_CastItem = m_CastItem;
- spell->prepare(&targets, NULL);
-}
-
-void Spell::EffectTeleportUnits(uint32 i)
-{
- if(!unitTarget || unitTarget->isInFlight())
- return;
-
- switch (m_spellInfo->EffectImplicitTargetB[i])
- {
- case TARGET_INNKEEPER_COORDINATES:
- {
- // Only players can teleport to innkeeper
- if (unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- ((Player*)unitTarget)->TeleportTo(((Player*)unitTarget)->m_homebindMapId,((Player*)unitTarget)->m_homebindX,((Player*)unitTarget)->m_homebindY,((Player*)unitTarget)->m_homebindZ,unitTarget->GetOrientation(),unitTarget==m_caster ? TELE_TO_SPELL : 0);
- return;
- }
- case TARGET_TABLE_X_Y_Z_COORDINATES:
- {
- // TODO: Only players can teleport?
- if (unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
- SpellTargetPosition const* st = spellmgr.GetSpellTargetPosition(m_spellInfo->Id);
- if(!st)
- {
- sLog.outError( "Spell::EffectTeleportUnits - unknown Teleport coordinates for spell ID %u\n", m_spellInfo->Id );
- return;
- }
- ((Player*)unitTarget)->TeleportTo(st->target_mapId,st->target_X,st->target_Y,st->target_Z,st->target_Orientation,unitTarget==m_caster ? TELE_TO_SPELL : 0);
- break;
- }
- case TARGET_BEHIND_VICTIM:
- {
- // Get selected target for player (or victim for units)
- Unit *pTarget = NULL;
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection());
- else
- pTarget = m_caster->getVictim();
- // No target present - return
- if (!pTarget)
- return;
- // Init dest coordinates
- uint32 mapid = m_caster->GetMapId();
- float x = m_targets.m_destX;
- float y = m_targets.m_destY;
- float z = m_targets.m_destZ;
- float orientation = pTarget->GetOrientation();
- // Teleport
- if(unitTarget->GetTypeId() == TYPEID_PLAYER)
- ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
- else
- {
- MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation);
- WorldPacket data;
- unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation);
- unitTarget->SendMessageToSet(&data, false);
- }
- return;
- }
- default:
- {
- // If not exist data for dest location - return
- if(!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION))
- {
- sLog.outError( "Spell::EffectTeleportUnits - unknown EffectImplicitTargetB[%u] = %u for spell ID %u\n", i, m_spellInfo->EffectImplicitTargetB[i], m_spellInfo->Id );
- return;
- }
- // Init dest coordinates
- uint32 mapid = m_caster->GetMapId();
- float x = m_targets.m_destX;
- float y = m_targets.m_destY;
- float z = m_targets.m_destZ;
- float orientation = unitTarget->GetOrientation();
- // Teleport
- if(unitTarget->GetTypeId() == TYPEID_PLAYER)
- ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
- else
- {
- MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation);
- WorldPacket data;
- unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation);
- unitTarget->SendMessageToSet(&data, false);
- }
- return;
- }
- }
-
- // post effects for TARGET_TABLE_X_Y_Z_COORDINATES
- switch ( m_spellInfo->Id )
- {
- // Dimensional Ripper - Everlook
- case 23442:
- {
- int32 r = irand(0, 119);
- if ( r >= 70 ) // 7/12 success
- {
- if ( r < 100 ) // 4/12 evil twin
- m_caster->CastSpell(m_caster,23445,true);
- else // 1/12 fire
- m_caster->CastSpell(m_caster,23449,true);
- }
- return;
- }
- // Ultrasafe Transporter: Toshley's Station
- case 36941:
- {
- if ( roll_chance_i(50) ) // 50% success
- {
- int32 rand_eff = urand(1,7);
- switch ( rand_eff )
- {
- case 1:
- // soul split - evil
- m_caster->CastSpell(m_caster,36900,true);
- break;
- case 2:
- // soul split - good
- m_caster->CastSpell(m_caster,36901,true);
- break;
- case 3:
- // Increase the size
- m_caster->CastSpell(m_caster,36895,true);
- break;
- case 4:
- // Decrease the size
- m_caster->CastSpell(m_caster,36893,true);
- break;
- case 5:
- // Transform
- {
- if (((Player*)m_caster)->GetTeam() == ALLIANCE )
- m_caster->CastSpell(m_caster,36897,true);
- else
- m_caster->CastSpell(m_caster,36899,true);
- break;
- }
- case 6:
- // chicken
- m_caster->CastSpell(m_caster,36940,true);
- break;
- case 7:
- // evil twin
- m_caster->CastSpell(m_caster,23445,true);
- break;
- }
- }
- return;
- }
- // Dimensional Ripper - Area 52
- case 36890:
- {
- if ( roll_chance_i(50) ) // 50% success
- {
- int32 rand_eff = urand(1,4);
- switch ( rand_eff )
- {
- case 1:
- // soul split - evil
- m_caster->CastSpell(m_caster,36900,true);
- break;
- case 2:
- // soul split - good
- m_caster->CastSpell(m_caster,36901,true);
- break;
- case 3:
- // Increase the size
- m_caster->CastSpell(m_caster,36895,true);
- break;
- case 4:
- // Transform
- {
- if (((Player*)m_caster)->GetTeam() == ALLIANCE )
- m_caster->CastSpell(m_caster,36897,true);
- else
- m_caster->CastSpell(m_caster,36899,true);
- break;
- }
- }
- }
- return;
- }
- }
-}
-
-void Spell::EffectApplyAura(uint32 i)
-{
- if(!unitTarget)
- return;
-
- SpellImmuneList const& list = unitTarget->m_spellImmune[IMMUNITY_STATE];
- for(SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr)
- if(itr->type == m_spellInfo->EffectApplyAuraName[i])
- return;
-
- // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load)
- if( !unitTarget->isAlive() && m_spellInfo->Id != 20584 && m_spellInfo->Id != 8326 &&
- (unitTarget->GetTypeId()!=TYPEID_PLAYER || !((Player*)unitTarget)->GetSession()->PlayerLoading()) )
- return;
-
- Unit* caster = m_originalCasterGUID ? m_originalCaster : m_caster;
- if(!caster)
- return;
-
- sLog.outDebug("Spell: Aura is: %u", m_spellInfo->EffectApplyAuraName[i]);
-
- Aura* Aur = CreateAura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget, caster, m_CastItem);
-
- // Now Reduce spell duration using data received at spell hit
- int32 duration = Aur->GetAuraMaxDuration();
- unitTarget->ApplyDiminishingToDuration(m_diminishGroup,duration,m_caster,m_diminishLevel);
- Aur->setDiminishGroup(m_diminishGroup);
-
- // if Aura removed and deleted, do not continue.
- if(duration== 0 && !(Aur->IsPermanent()))
- {
- delete Aur;
- return;
- }
-
- if(duration != Aur->GetAuraMaxDuration())
- {
- Aur->SetAuraMaxDuration(duration);
- Aur->SetAuraDuration(duration);
- }
-
- bool added = unitTarget->AddAura(Aur);
-
- // Aura not added and deleted in AddAura call;
- if (!added)
- return;
-
- // found crash at character loading, broken pointer to Aur...
- // Aur was deleted in AddAura()...
- if(!Aur)
- return;
-
- // TODO Make a way so it works for every related spell!
- if(unitTarget->GetTypeId()==TYPEID_PLAYER) // Negative buff should only be applied on players
- {
- uint32 spellId = 0;
- if(m_spellInfo->CasterAuraStateNot==AURA_STATE_WEAKENED_SOUL || m_spellInfo->TargetAuraStateNot==AURA_STATE_WEAKENED_SOUL)
- spellId = 6788; // Weakened Soul
- else if(m_spellInfo->CasterAuraStateNot==AURA_STATE_FORBEARANCE || m_spellInfo->TargetAuraStateNot==AURA_STATE_FORBEARANCE)
- spellId = 25771; // Forbearance
- else if(m_spellInfo->CasterAuraStateNot==AURA_STATE_HYPOTHERMIA)
- spellId = 41425; // Hypothermia
- else if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages
- spellId = 11196; // Recently Bandaged
- else if( (m_spellInfo->AttributesEx & 0x20) && (m_spellInfo->AttributesEx2 & 0x20000) )
- spellId = 23230; // Blood Fury - Healing Reduction
-
- SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(spellId);
- if (AdditionalSpellInfo)
- {
- // applied at target by target
- Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, 0, &m_currentBasePoints[0], unitTarget,unitTarget, 0);
- unitTarget->AddAura(AdditionalAura);
- sLog.outDebug("Spell: Additional Aura is: %u", AdditionalSpellInfo->EffectApplyAuraName[0]);
- }
- }
-
- // Prayer of Mending (jump animation), we need formal caster instead original for correct animation
- if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && (m_spellInfo->SpellFamilyFlags & 0x00002000000000LL))
- m_caster->CastSpell(unitTarget,41637,true,NULL,Aur);
-}
-
-void Spell::EffectUnlearnSpecialization( uint32 i )
-{
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player *_player = (Player*)unitTarget;
- uint32 spellToUnlearn = m_spellInfo->EffectTriggerSpell[i];
-
- _player->removeSpell(spellToUnlearn);
-
- sLog.outDebug( "Spell: Player %u have unlearned spell %u from NpcGUID: %u", _player->GetGUIDLow(), spellToUnlearn, m_caster->GetGUIDLow() );
-}
-
-void Spell::EffectPowerDrain(uint32 i)
-{
- if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
- return;
-
- Powers drain_power = Powers(m_spellInfo->EffectMiscValue[i]);
-
- if(!unitTarget)
- return;
- if(!unitTarget->isAlive())
- return;
- if(unitTarget->getPowerType() != drain_power)
- return;
- if(damage < 0)
- return;
-
- uint32 curPower = unitTarget->GetPower(drain_power);
-
- //add spell damage bonus
- damage=m_caster->SpellDamageBonus(unitTarget,m_spellInfo,uint32(damage),SPELL_DIRECT_DAMAGE);
-
- // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
- uint32 power = damage;
- if ( drain_power == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER )
- power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power);
-
- int32 new_damage;
- if(curPower < power)
- new_damage = curPower;
- else
- new_damage = power;
-
- unitTarget->ModifyPower(drain_power,-new_damage);
-
- if(drain_power == POWER_MANA)
- {
- float manaMultiplier = m_spellInfo->EffectMultipleValue[i];
- if(manaMultiplier==0)
- manaMultiplier = 1;
-
- if(Player *modOwner = m_caster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, manaMultiplier);
-
- int32 gain = int32(new_damage*manaMultiplier);
-
- m_caster->ModifyPower(POWER_MANA,gain);
- //send log
- m_caster->SendEnergizeSpellLog(m_caster, m_spellInfo->Id,gain,POWER_MANA,false);
- }
-}
-
-void Spell::EffectSendEvent(uint32 EffectIndex)
-{
- if (m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->InBattleGround())
- {
- BattleGround* bg = ((Player *)m_caster)->GetBattleGround();
- if(bg && bg->GetStatus() == STATUS_IN_PROGRESS)
- {
- switch(m_spellInfo->Id)
- {
- case 23333: // Pickup Horde Flag
- /*do not uncomment .
- if(bg->GetTypeID()==BATTLEGROUND_WS)
- bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
- sLog.outDebug("Send Event Horde Flag Picked Up");
- break;
- /* not used :
- case 23334: // Drop Horde Flag
- if(bg->GetTypeID()==BATTLEGROUND_WS)
- bg->EventPlayerDroppedFlag((Player*)m_caster);
- sLog.outDebug("Drop Horde Flag");
- break;
- */
- case 23335: // Pickup Alliance Flag
- /*do not uncomment ... (it will cause crash, because of null targetobject!) anyway this is a bad way to call that event, because it would cause recursion
- if(bg->GetTypeID()==BATTLEGROUND_WS)
- bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
- sLog.outDebug("Send Event Alliance Flag Picked Up");
- break;
- /* not used :
- case 23336: // Drop Alliance Flag
- if(bg->GetTypeID()==BATTLEGROUND_WS)
- bg->EventPlayerDroppedFlag((Player*)m_caster);
- sLog.outDebug("Drop Alliance Flag");
- break;
- case 23385: // Alliance Flag Returns
- if(bg->GetTypeID()==BATTLEGROUND_WS)
- bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
- sLog.outDebug("Alliance Flag Returned");
- break;
- case 23386: // Horde Flag Returns
- if(bg->GetTypeID()==BATTLEGROUND_WS)
- bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
- sLog.outDebug("Horde Flag Returned");
- break;*/
- case 34976:
- /*
- if(bg->GetTypeID()==BATTLEGROUND_EY)
- bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
- */
- break;
- default:
- sLog.outDebug("Unknown spellid %u in BG event", m_spellInfo->Id);
- break;
- }
- }
- }
- sLog.outDebug("Spell ScriptStart %u for spellid %u in EffectSendEvent ", m_spellInfo->EffectMiscValue[EffectIndex], m_spellInfo->Id);
- sWorld.ScriptsStart(sEventScripts, m_spellInfo->EffectMiscValue[EffectIndex], m_caster, focusObject);
-}
-
-void Spell::EffectPowerBurn(uint32 i)
-{
- if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
- return;
-
- Powers powertype = Powers(m_spellInfo->EffectMiscValue[i]);
-
- if(!unitTarget)
- return;
- if(!unitTarget->isAlive())
- return;
- if(unitTarget->getPowerType()!=powertype)
- return;
- if(damage < 0)
- return;
-
- int32 curPower = int32(unitTarget->GetPower(powertype));
-
- // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
- uint32 power = damage;
- if ( powertype == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER )
- power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power);
-
- int32 new_damage = (curPower < power) ? curPower : power;
-
- unitTarget->ModifyPower(powertype,-new_damage);
- float multiplier = m_spellInfo->EffectMultipleValue[i];
-
- if(Player *modOwner = m_caster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
-
- new_damage = int32(new_damage*multiplier);
- m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage, m_IsTriggeredSpell, true);
-}
-
-void Spell::EffectHeal( uint32 /*i*/ )
-{
- if( unitTarget && unitTarget->isAlive() && damage >= 0)
- {
- // Try to get original caster
- Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
-
- // Skip if m_originalCaster not available
- if (!caster)
- return;
-
- int32 addhealth = damage;
-
- // Vessel of the Naaru (Vial of the Sunwell trinket)
- if (m_spellInfo->Id == 45064)
- {
- // Amount of heal - depends from stacked Holy Energy
- int damageAmount = 0;
- Unit::AuraList const& mDummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
- for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
- if((*i)->GetId() == 45062)
- damageAmount+=(*i)->GetModifier()->m_amount;
- if (damageAmount)
- m_caster->RemoveAurasDueToSpell(45062);
-
- addhealth += damageAmount;
- }
- // Swiftmend - consumes Regrowth or Rejuvenation
- else if (m_spellInfo->TargetAuraState == AURA_STATE_SWIFTMEND && unitTarget->HasAuraState(AURA_STATE_SWIFTMEND))
- {
- Unit::AuraList const& RejorRegr = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_HEAL);
- // find most short by duration
- Aura *targetAura = NULL;
- 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) )
- {
- if(!targetAura || (*i)->GetAuraDuration() < targetAura->GetAuraDuration())
- targetAura = *i;
- }
- }
-
- if(!targetAura)
- {
- sLog.outError("Target(GUID:" I64FMTD ") has aurastate AURA_STATE_SWIFTMEND but no matching aura.", unitTarget->GetGUID());
- return;
- }
- int idx = 0;
- while(idx < 3)
- {
- if(targetAura->GetSpellProto()->EffectApplyAuraName[idx] == SPELL_AURA_PERIODIC_HEAL)
- break;
- idx++;
- }
-
- int32 tickheal = caster->SpellHealingBonus(targetAura->GetSpellProto(), targetAura->GetModifier()->m_amount, DOT, unitTarget);
- int32 tickcount = GetSpellDuration(targetAura->GetSpellProto()) / targetAura->GetSpellProto()->EffectAmplitude[idx];
- unitTarget->RemoveAurasDueToSpell(targetAura->GetId());
-
- addhealth += tickheal * tickcount;
- }
- else
- addhealth = caster->SpellHealingBonus(m_spellInfo, addhealth,HEAL, unitTarget);
-
- bool crit = caster->isSpellCrit(unitTarget, m_spellInfo, m_spellSchoolMask, m_attackType);
- if (crit)
- addhealth = caster->SpellCriticalBonus(m_spellInfo, addhealth, unitTarget);
- caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, crit);
-
- int32 gain = unitTarget->ModifyHealth( int32(addhealth) );
- unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
-
- if(caster->GetTypeId()==TYPEID_PLAYER)
- if(BattleGround *bg = ((Player*)caster)->GetBattleGround())
- bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain);
-
- // ignore item heals
- if(m_CastItem)
- return;
-
- uint32 procHealer = PROC_FLAG_HEAL;
- if (crit)
- procHealer |= PROC_FLAG_CRIT_HEAL;
-
- m_caster->ProcDamageAndSpell(unitTarget,procHealer,PROC_FLAG_HEALED,addhealth,SPELL_SCHOOL_MASK_NONE,m_spellInfo,m_IsTriggeredSpell);
- }
-}
-
-void Spell::EffectHealPct( uint32 /*i*/ )
-{
- if( unitTarget && unitTarget->isAlive() && damage >= 0)
- {
- // Try to get original caster
- Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
-
- // Skip if m_originalCaster not available
- if (!caster)
- return;
-
- uint32 addhealth = unitTarget->GetMaxHealth() * damage / 100;
- caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, false);
-
- int32 gain = unitTarget->ModifyHealth( int32(addhealth) );
- unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
-
- if(caster->GetTypeId()==TYPEID_PLAYER)
- if(BattleGround *bg = ((Player*)caster)->GetBattleGround())
- bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain);
- }
-}
-
-void Spell::EffectHealMechanical( uint32 /*i*/ )
-{
- // Mechanic creature type should be correctly checked by targetCreatureType field
- if( unitTarget && unitTarget->isAlive() && damage >= 0)
- {
- // Try to get original caster
- Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
-
- // Skip if m_originalCaster not available
- if (!caster)
- return;
-
- uint32 addhealth = caster->SpellHealingBonus(m_spellInfo, uint32(damage), HEAL, unitTarget);
- caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, false);
- unitTarget->ModifyHealth( int32(damage) );
- }
-}
-
-void Spell::EffectHealthLeech(uint32 i)
-{
- if(!unitTarget)
- return;
- if(!unitTarget->isAlive())
- return;
-
- if(damage < 0)
- return;
-
- sLog.outDebug("HealthLeech :%i", damage);
-
- float multiplier = m_spellInfo->EffectMultipleValue[i];
-
- if(Player *modOwner = m_caster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
-
- int32 new_damage = int32(damage*multiplier);
- uint32 curHealth = unitTarget->GetHealth();
- new_damage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage, m_IsTriggeredSpell, true);
- if(curHealth < new_damage)
- new_damage = curHealth;
-
- if(m_caster->isAlive())
- {
- new_damage = m_caster->SpellHealingBonus(m_spellInfo, new_damage, HEAL, m_caster);
-
- m_caster->ModifyHealth(new_damage);
-
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- m_caster->SendHealSpellLog(m_caster, m_spellInfo->Id, uint32(new_damage));
- }
-}
-
-void Spell::DoCreateItem(uint32 i, uint32 itemtype)
-{
- if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player* player = (Player*)unitTarget;
-
- uint32 newitemid = itemtype;
- ItemPrototype const *pProto = objmgr.GetItemPrototype( newitemid );
- if(!pProto)
- {
- player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
- return;
- }
-
- uint32 num_to_add;
-
- // TODO: maybe all this can be replaced by using correct calculated `damage` value
- if(pProto->Class != ITEM_CLASS_CONSUMABLE || m_spellInfo->SpellFamilyName != SPELLFAMILY_MAGE)
- {
- int32 basePoints = m_currentBasePoints[i];
- int32 randomPoints = m_spellInfo->EffectDieSides[i];
- if (randomPoints)
- num_to_add = basePoints + irand(1, randomPoints);
- else
- num_to_add = basePoints + 1;
- }
- else if (pProto->MaxCount == 1)
- num_to_add = 1;
- else if(player->getLevel() >= m_spellInfo->spellLevel)
- {
- int32 basePoints = m_currentBasePoints[i];
- float pointPerLevel = m_spellInfo->EffectRealPointsPerLevel[i];
- num_to_add = basePoints + 1 + uint32((player->getLevel() - m_spellInfo->spellLevel)*pointPerLevel);
- }
- else
- num_to_add = 2;
-
- if (num_to_add < 1)
- num_to_add = 1;
- if (num_to_add > pProto->Stackable)
- num_to_add = pProto->Stackable;
-
- // init items_count to 1, since 1 item will be created regardless of specialization
- int items_count=1;
- // the chance to create additional items
- float additionalCreateChance=0.0f;
- // the maximum number of created additional items
- uint8 additionalMaxNum=0;
- // get the chance and maximum number for creating extra items
- if ( canCreateExtraItems(player, m_spellInfo->Id, additionalCreateChance, additionalMaxNum) )
- {
- // roll with this chance till we roll not to create or we create the max num
- while ( roll_chance_f(additionalCreateChance) && items_count<=additionalMaxNum )
- ++items_count;
- }
-
- // really will be created more items
- num_to_add *= items_count;
-
- // can the player store the new item?
- ItemPosCountVec dest;
- uint32 no_space = 0;
- uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, newitemid, num_to_add, &no_space );
- if( msg != EQUIP_ERR_OK )
- {
- // convert to possible store amount
- if( msg == EQUIP_ERR_INVENTORY_FULL || msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
- num_to_add -= no_space;
- else
- {
- // if not created by another reason from full inventory or unique items amount limitation
- player->SendEquipError( msg, NULL, NULL );
- return;
- }
- }
-
- if(num_to_add)
- {
- // create the new item and store it
- Item* pItem = player->StoreNewItem( dest, newitemid, true, Item::GenerateItemRandomPropertyId(newitemid));
-
- // was it successful? return error if not
- if(!pItem)
- {
- player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
- return;
- }
-
- // set the "Crafted by ..." property of the item
- if( pItem->GetProto()->Class != ITEM_CLASS_CONSUMABLE && pItem->GetProto()->Class != ITEM_CLASS_QUEST)
- pItem->SetUInt32Value(ITEM_FIELD_CREATOR,player->GetGUIDLow());
-
- // send info to the client
- if(pItem)
- player->SendNewItem(pItem, num_to_add, true, true);
-
- // we succeeded in creating at least one item, so a levelup is possible
- player->UpdateCraftSkill(m_spellInfo->Id);
- }
-
- // for battleground marks send by mail if not add all expected
- if(no_space > 0 )
- {
- BattleGroundTypeId bgType;
- switch(m_spellInfo->Id)
- {
- case SPELL_AV_MARK_WINNER:
- case SPELL_AV_MARK_LOSER:
- bgType = BATTLEGROUND_AV;
- break;
- case SPELL_WS_MARK_WINNER:
- case SPELL_WS_MARK_LOSER:
- bgType = BATTLEGROUND_WS;
- break;
- case SPELL_AB_MARK_WINNER:
- case SPELL_AB_MARK_LOSER:
- bgType = BATTLEGROUND_AB;
- break;
- default:
- return;
- }
-
- if(BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(bgType))
- bg->SendRewardMarkByMail(player,newitemid,no_space);
- }
-}
-
-void Spell::EffectCreateItem(uint32 i)
-{
- DoCreateItem(i,m_spellInfo->EffectItemType[i]);
-}
-
-void Spell::EffectPersistentAA(uint32 i)
-{
- float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
-
- if(Player* modOwner = m_caster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius);
-
- int32 duration = GetSpellDuration(m_spellInfo);
- DynamicObject* dynObj = new DynamicObject;
- if(!dynObj->Create(objmgr.GenerateLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, i, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, radius))
- {
- delete dynObj;
- return;
- }
- dynObj->SetUInt32Value(OBJECT_FIELD_TYPE, 65);
- dynObj->SetUInt32Value(GAMEOBJECT_DISPLAYID, 368003);
- dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x01eeeeee);
- m_caster->AddDynObject(dynObj);
- MapManager::Instance().GetMap(dynObj->GetMapId(), dynObj)->Add(dynObj);
-}
-
-void Spell::EffectEnergize(uint32 i)
-{
- if(!unitTarget)
- return;
- if(!unitTarget->isAlive())
- return;
-
- if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
- return;
-
- // Some level depends spells
- int multipler = 0;
- int level_diff = 0;
- switch (m_spellInfo->Id)
- {
- // Restore Energy
- case 9512:
- level_diff = m_caster->getLevel() - 40;
- multipler = 2;
- break;
- // Blood Fury
- case 24571:
- level_diff = m_caster->getLevel() - 60;
- multipler = 10;
- break;
- // Burst of Energy
- case 24532:
- level_diff = m_caster->getLevel() - 60;
- multipler = 4;
- break;
- default:
- break;
- }
-
- if (level_diff > 0)
- damage -= multipler * level_diff;
-
- if(damage < 0)
- return;
-
- Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
-
- if(unitTarget->GetMaxPower(power) == 0)
- return;
-
- unitTarget->ModifyPower(power,damage);
- m_caster->SendEnergizeSpellLog(unitTarget, m_spellInfo->Id, damage, power);
-
- // Mad Alchemist's Potion
- if (m_spellInfo->Id == 45051)
- {
- // find elixirs on target
- uint32 elixir_mask = 0;
- Unit::AuraMap& Auras = unitTarget->GetAuras();
- for(Unit::AuraMap::iterator itr = Auras.begin(); itr != Auras.end(); ++itr)
- {
- uint32 spell_id = itr->second->GetId();
- if(uint32 mask = spellmgr.GetSpellElixirMask(spell_id))
- elixir_mask |= mask;
- }
-
- // get available elixir mask any not active type from battle/guardian (and flask if no any)
- elixir_mask = (elixir_mask & ELIXIR_FLASK_MASK) ^ ELIXIR_FLASK_MASK;
-
- // get all available elixirs by mask and spell level
- std::vector<uint32> elixirs;
- SpellElixirMap const& m_spellElixirs = spellmgr.GetSpellElixirMap();
- for(SpellElixirMap::const_iterator itr = m_spellElixirs.begin(); itr != m_spellElixirs.end(); ++itr)
- {
- if (itr->second & elixir_mask)
- {
- if (itr->second & (ELIXIR_UNSTABLE_MASK | ELIXIR_SHATTRATH_MASK))
- continue;
-
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
- if (spellInfo && (spellInfo->spellLevel < m_spellInfo->spellLevel || spellInfo->spellLevel > unitTarget->getLevel()))
- continue;
-
- elixirs.push_back(itr->first);
- }
- }
-
- if (!elixirs.empty())
- {
- // cast random elixir on target
- uint32 rand_spell = urand(0,elixirs.size()-1);
- m_caster->CastSpell(unitTarget,elixirs[rand_spell],true,m_CastItem);
- }
- }
-}
-
-void Spell::EffectEnergisePct(uint32 i)
-{
- if(!unitTarget)
- return;
- if(!unitTarget->isAlive())
- return;
-
- if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
- return;
-
- Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
-
- uint32 maxPower = unitTarget->GetMaxPower(power);
- if(maxPower == 0)
- return;
-
- uint32 gain = damage * maxPower / 100;
- unitTarget->ModifyPower(power, gain);
- m_caster->SendEnergizeSpellLog(unitTarget, m_spellInfo->Id, damage, power);
-}
-
-void Spell::SendLoot(uint64 guid, LootType loottype)
-{
- Player* player = (Player*)m_caster;
- if (!player)
- return;
-
- if (gameObjTarget)
- {
- switch (gameObjTarget->GetGoType())
- {
- case GAMEOBJECT_TYPE_DOOR:
- case GAMEOBJECT_TYPE_BUTTON:
- gameObjTarget->UseDoorOrButton();
- sWorld.ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget);
- return;
-
- case GAMEOBJECT_TYPE_QUESTGIVER:
- // start or end quest
- player->PrepareQuestMenu(guid);
- player->SendPreparedQuest(guid);
- return;
-
- case GAMEOBJECT_TYPE_SPELL_FOCUS:
- // triggering linked GO
- if(uint32 trapEntry = gameObjTarget->GetGOInfo()->spellFocus.linkedTrapId)
- gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster);
- return;
-
- case GAMEOBJECT_TYPE_GOOBER:
- // goober_scripts can be triggered if the player don't have the quest
- if (gameObjTarget->GetGOInfo()->goober.eventId)
- {
- sLog.outDebug("Goober ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->goober.eventId,gameObjTarget->GetDBTableGUIDLow());
- sWorld.ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->goober.eventId, player, gameObjTarget);
- }
-
- // cast goober spell
- if (gameObjTarget->GetGOInfo()->goober.questId)
- ///Quest require to be active for GO using
- if(player->GetQuestStatus(gameObjTarget->GetGOInfo()->goober.questId) != QUEST_STATUS_INCOMPLETE)
- return;
-
- gameObjTarget->AddUniqueUse(player);
- gameObjTarget->SetLootState(GO_JUST_DEACTIVATED);
-
- //TODO? Objective counting called without spell check but with quest objective check
- // if send spell id then this line will duplicate to spell casting call (double counting)
- // So we or have this line and not required in quest_template have reqSpellIdN
- // or must remove this line and required in DB have data in quest_template have reqSpellIdN for all quest using cases.
- player->CastedCreatureOrGO(gameObjTarget->GetEntry(), gameObjTarget->GetGUID(), 0);
-
- // triggering linked GO
- if(uint32 trapEntry = gameObjTarget->GetGOInfo()->goober.linkedTrapId)
- gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster);
-
- return;
-
- case GAMEOBJECT_TYPE_CHEST:
- // TODO: possible must be moved to loot release (in different from linked triggering)
- if (gameObjTarget->GetGOInfo()->chest.eventId)
- {
- sLog.outDebug("Chest ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->chest.eventId,gameObjTarget->GetDBTableGUIDLow());
- sWorld.ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->chest.eventId, player, gameObjTarget);
- }
-
- // triggering linked GO
- if(uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrapId)
- gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster);
-
- // Don't return, let loots been taken
- }
- }
-
- // Send loot
- player->SendLoot(guid, loottype);
-}
-
-void Spell::EffectOpenLock(uint32 /*i*/)
-{
- if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
- {
- sLog.outDebug( "WORLD: Open Lock - No Player Caster!");
- return;
- }
-
- Player* player = (Player*)m_caster;
-
- LootType loottype = LOOT_CORPSE;
- uint32 lockId = 0;
- uint64 guid = 0;
-
- // Get lockId
- if(gameObjTarget)
- {
- GameObjectInfo const* goInfo = gameObjTarget->GetGOInfo();
- // Arathi Basin banner opening !
- if( goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune ||
- goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK )
- {
- //isAllowUseBattleGroundObject() already called in CanCast()
- // in battleground check
- if(BattleGround *bg = player->GetBattleGround())
- {
- // check if it's correct bg
- if(bg && bg->GetTypeID() == BATTLEGROUND_AB)
- bg->EventPlayerClickedOnFlag(player, gameObjTarget);
- return;
- }
- }
- else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND)
- {
- //isAllowUseBattleGroundObject() already called in CanCast()
- // in battleground check
- if(BattleGround *bg = player->GetBattleGround())
- {
- if(bg->GetTypeID() == BATTLEGROUND_EY)
- bg->EventPlayerClickedOnFlag(player, gameObjTarget);
- return;
- }
- }
- lockId = gameObjTarget->GetLockId();
- guid = gameObjTarget->GetGUID();
- }
- else if(itemTarget)
- {
- lockId = itemTarget->GetProto()->LockID;
- guid = itemTarget->GetGUID();
- }
- else
- {
- sLog.outDebug( "WORLD: Open Lock - No GameObject/Item Target!");
- return;
- }
-
- if(!lockId) // possible case for GO and maybe for items.
- {
- SendLoot(guid, loottype);
- return;
- }
-
- // Get LockInfo
- LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
-
- if (!lockInfo)
- {
- sLog.outError( "Spell::EffectOpenLock: %s [guid = %u] has an unknown lockId: %u!",
- (gameObjTarget ? "gameobject" : "item"), GUID_LOPART(guid), lockId);
- SendCastResult(SPELL_FAILED_BAD_TARGETS);
- return;
- }
-
- // check key
- for(int i = 0; i < 5; ++i)
- {
- // type==1 This means lockInfo->key[i] is an item
- if(lockInfo->keytype[i]==LOCK_KEY_ITEM && lockInfo->key[i] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[i])
- {
- SendLoot(guid, loottype);
- return;
- }
- }
-
- uint32 SkillId = 0;
- // Check and skill-up skill
- if( m_spellInfo->Effect[1] == SPELL_EFFECT_SKILL )
- SkillId = m_spellInfo->EffectMiscValue[1];
- // pickpocketing spells
- else if( m_spellInfo->EffectMiscValue[0] == LOCKTYPE_PICKLOCK )
- SkillId = SKILL_LOCKPICKING;
-
- // skill bonus provided by casting spell (mostly item spells)
- uint32 spellSkillBonus = uint32(m_currentBasePoints[0]+1);
-
- uint32 reqSkillValue = lockInfo->requiredminingskill;
-
- if(lockInfo->requiredlockskill) // required pick lock skill applying
- {
- if(SkillId != SKILL_LOCKPICKING) // wrong skill (cheating?)
- {
- SendCastResult(SPELL_FAILED_FIZZLE);
- return;
- }
-
- reqSkillValue = lockInfo->requiredlockskill;
- }
- else if(SkillId == SKILL_LOCKPICKING) // apply picklock skill to wrong target
- {
- SendCastResult(SPELL_FAILED_BAD_TARGETS);
- return;
- }
-
- if ( SkillId )
- {
- loottype = LOOT_SKINNING;
- if ( player->GetSkillValue(SkillId) + spellSkillBonus < reqSkillValue )
- {
- SendCastResult(SPELL_FAILED_LOW_CASTLEVEL);
- return;
- }
-
- // update skill if really known
- uint32 SkillValue = player->GetPureSkillValue(SkillId);
- if(SkillValue) // non only item base skill
- {
- if(gameObjTarget)
- {
- // Allow one skill-up until respawned
- if ( !gameObjTarget->IsInSkillupList( player->GetGUIDLow() ) &&
- player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue) )
- gameObjTarget->AddToSkillupList( player->GetGUIDLow() );
- }
- else if(itemTarget)
- {
- // Do one skill-up
- uint32 SkillValue = player->GetPureSkillValue(SkillId);
- player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue);
- }
- }
- }
-
- SendLoot(guid, loottype);
-}
-
-void Spell::EffectSummonChangeItem(uint32 i)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player *player = (Player*)m_caster;
-
- // applied only to using item
- if(!m_CastItem)
- return;
-
- // ... only to item in own inventory/bank/equip_slot
- if(m_CastItem->GetOwnerGUID()!=player->GetGUID())
- return;
-
- uint32 newitemid = m_spellInfo->EffectItemType[i];
- if(!newitemid)
- return;
-
- uint16 pos = m_CastItem->GetPos();
-
- Item *pNewItem = Item::CreateItem( newitemid, 1, player);
- if( !pNewItem )
- return;
-
- for(uint8 i= PERM_ENCHANTMENT_SLOT; i<=TEMP_ENCHANTMENT_SLOT; ++i)
- {
- if(m_CastItem->GetEnchantmentId(EnchantmentSlot(i)))
- pNewItem->SetEnchantment(EnchantmentSlot(i), m_CastItem->GetEnchantmentId(EnchantmentSlot(i)), m_CastItem->GetEnchantmentDuration(EnchantmentSlot(i)), m_CastItem->GetEnchantmentCharges(EnchantmentSlot(i)));
- }
-
- if(m_CastItem->GetUInt32Value(ITEM_FIELD_DURABILITY) < m_CastItem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY))
- {
- double loosePercent = 1 - m_CastItem->GetUInt32Value(ITEM_FIELD_DURABILITY) / double(m_CastItem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY));
- player->DurabilityLoss(pNewItem, loosePercent);
- }
-
- if( player->IsInventoryPos( pos ) )
- {
- ItemPosCountVec dest;
- uint8 msg = player->CanStoreItem( m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true );
- if( msg == EQUIP_ERR_OK )
- {
- player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true);
-
- // prevent crash at access and unexpected charges counting with item update queue corrupt
- if(m_CastItem==m_targets.getItemTarget())
- m_targets.setItemTarget(NULL);
-
- m_CastItem = NULL;
-
- player->StoreItem( dest, pNewItem, true);
- return;
- }
- }
- else if( player->IsBankPos ( pos ) )
- {
- ItemPosCountVec dest;
- uint8 msg = player->CanBankItem( m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true );
- if( msg == EQUIP_ERR_OK )
- {
- player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true);
-
- // prevent crash at access and unexpected charges counting with item update queue corrupt
- if(m_CastItem==m_targets.getItemTarget())
- m_targets.setItemTarget(NULL);
-
- m_CastItem = NULL;
-
- player->BankItem( dest, pNewItem, true);
- return;
- }
- }
- else if( player->IsEquipmentPos ( pos ) )
- {
- uint16 dest;
- uint8 msg = player->CanEquipItem( m_CastItem->GetSlot(), dest, pNewItem, true );
- if( msg == EQUIP_ERR_OK )
- {
- player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true);
-
- // prevent crash at access and unexpected charges counting with item update queue corrupt
- if(m_CastItem==m_targets.getItemTarget())
- m_targets.setItemTarget(NULL);
-
- m_CastItem = NULL;
-
- player->EquipItem( dest, pNewItem, true);
- player->AutoUnequipOffhandIfNeed();
- return;
- }
- }
-
- // fail
- delete pNewItem;
-}
-
-void Spell::EffectOpenSecretSafe(uint32 i)
-{
- EffectOpenLock(i); //no difference for now
-}
-
-void Spell::EffectProficiency(uint32 /*i*/)
-{
- if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
- Player *p_target = (Player*)unitTarget;
-
- uint32 subClassMask = m_spellInfo->EquippedItemSubClassMask;
- if(m_spellInfo->EquippedItemClass == 2 && !(p_target->GetWeaponProficiency() & subClassMask))
- {
- p_target->AddWeaponProficiency(subClassMask);
- p_target->SendProficiency(uint8(0x02),p_target->GetWeaponProficiency());
- }
- if(m_spellInfo->EquippedItemClass == 4 && !(p_target->GetArmorProficiency() & subClassMask))
- {
- p_target->AddArmorProficiency(subClassMask);
- p_target->SendProficiency(uint8(0x04),p_target->GetArmorProficiency());
- }
-}
-
-void Spell::EffectApplyAreaAura(uint32 i)
-{
- if(!unitTarget)
- return;
- if(!unitTarget->isAlive())
- return;
-
- AreaAura* Aur = new AreaAura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget, m_caster, m_CastItem);
- unitTarget->AddAura(Aur);
-}
-
-void Spell::EffectSummonType(uint32 i)
-{
- switch(m_spellInfo->EffectMiscValueB[i])
- {
- case SUMMON_TYPE_GUARDIAN:
- case SUMMON_TYPE_POSESSED:
- case SUMMON_TYPE_POSESSED2:
- EffectSummonGuardian(i);
- break;
- case SUMMON_TYPE_WILD:
- EffectSummonWild(i);
- break;
- case SUMMON_TYPE_DEMON:
- EffectSummonDemon(i);
- break;
- case SUMMON_TYPE_SUMMON:
- EffectSummon(i);
- break;
- case SUMMON_TYPE_CRITTER:
- case SUMMON_TYPE_CRITTER2:
- EffectSummonCritter(i);
- break;
- case SUMMON_TYPE_TOTEM_SLOT1:
- case SUMMON_TYPE_TOTEM_SLOT2:
- case SUMMON_TYPE_TOTEM_SLOT3:
- case SUMMON_TYPE_TOTEM_SLOT4:
- case SUMMON_TYPE_TOTEM:
- EffectSummonTotem(i);
- break;
- case SUMMON_TYPE_UNKNOWN1:
- case SUMMON_TYPE_UNKNOWN2:
- case SUMMON_TYPE_UNKNOWN3:
- case SUMMON_TYPE_UNKNOWN4:
- case SUMMON_TYPE_UNKNOWN5:
- case SUMMON_TYPE_UNKNOWN6:
- break;
- default:
- sLog.outError("EffectSummonType: Unhandled summon type %u", m_spellInfo->EffectMiscValueB[i]);
- break;
- }
-}
-
-void Spell::EffectSummon(uint32 i)
-{
- if(m_caster->GetPetGUID())
- return;
-
- if(!unitTarget)
- return;
- uint32 pet_entry = m_spellInfo->EffectMiscValue[i];
- if(!pet_entry)
- return;
- uint32 level = m_caster->getLevel();
- Pet* spawnCreature = new Pet(SUMMON_PET);
-
- if(spawnCreature->LoadPetFromDB(m_caster,pet_entry))
- {
- // set timer for unsummon
- int32 duration = GetSpellDuration(m_spellInfo);
- if(duration > 0)
- spawnCreature->SetDuration(duration);
-
- return;
- }
-
- Map *map = m_caster->GetMap();
- uint32 pet_number = objmgr.GeneratePetNumber();
- if(!spawnCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_PET),map,m_spellInfo->EffectMiscValue[i], pet_number))
- {
- sLog.outErrorDb("Spell::EffectSummon: no such creature entry %u",m_spellInfo->EffectMiscValue[i]);
- delete spawnCreature;
- return;
- }
-
- // Summon in dest location
- float x,y,z;
- if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
- {
- x = m_targets.m_destX;
- y = m_targets.m_destY;
- z = m_targets.m_destZ;
- }
- else
- m_caster->GetClosePoint(x,y,z,spawnCreature->GetObjectSize());
-
- spawnCreature->Relocate(x,y,z,-m_caster->GetOrientation());
-
- if(!spawnCreature->IsPositionValid())
- {
- sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", spawnCreature->GetGUIDLow(), spawnCreature->GetEntry(), spawnCreature->GetPositionX(), spawnCreature->GetPositionY());
- delete spawnCreature;
- return;
- }
-
- // set timer for unsummon
- int32 duration = GetSpellDuration(m_spellInfo);
- if(duration > 0)
- spawnCreature->SetDuration(duration);
-
- spawnCreature->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID());
- spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS , 0);
- spawnCreature->setPowerType(POWER_MANA);
- spawnCreature->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
- spawnCreature->SetUInt32Value(UNIT_FIELD_FLAGS,0);
- spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
- spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
- spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
- spawnCreature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
- spawnCreature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
- spawnCreature->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
- spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
-
- spawnCreature->InitStatsForLevel(level);
-
- spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false);
-
- spawnCreature->AIM_Initialize();
- spawnCreature->InitPetCreateSpells();
- spawnCreature->SetHealth(spawnCreature->GetMaxHealth());
- spawnCreature->SetPower(POWER_MANA, spawnCreature->GetMaxPower(POWER_MANA));
-
- std::string name = m_caster->GetName();
- name.append(petTypeSuffix[spawnCreature->getPetType()]);
- spawnCreature->SetName( name );
-
- map->Add((Creature*)spawnCreature);
-
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- m_caster->SetPet(spawnCreature);
- spawnCreature->GetCharmInfo()->SetReactState( REACT_DEFENSIVE );
- spawnCreature->SavePetToDB(PET_SAVE_AS_CURRENT);
- ((Player*)m_caster)->PetSpellInitialize();
- }
-}
-
-void Spell::EffectLearnSpell(uint32 i)
-{
- if(!unitTarget)
- return;
-
- if(unitTarget->GetTypeId() != TYPEID_PLAYER)
- {
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- EffectLearnPetSpell(i);
-
- return;
- }
-
- Player *player = (Player*)unitTarget;
-
- uint32 spellToLearn = (m_spellInfo->Id==SPELL_ID_GENERIC_LEARN) ? damage : m_spellInfo->EffectTriggerSpell[i];
- player->learnSpell(spellToLearn);
-
- sLog.outDebug( "Spell: Player %u have learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() );
-}
-
-void Spell::EffectDispel(uint32 i)
-{
- if(!unitTarget)
- return;
-
- // Fill possible dispell list
- std::vector <Aura *> dispel_list;
-
- // Create dispel mask by dispel type
- uint32 dispel_type = m_spellInfo->EffectMiscValue[i];
- uint32 dispelMask = GetDispellMask( DispelType(dispel_type) );
- Unit::AuraMap const& auras = unitTarget->GetAuras();
- for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
- {
- Aura *aur = (*itr).second;
- if (aur && (1<<aur->GetSpellProto()->Dispel) & dispelMask)
- {
- if(aur->GetSpellProto()->Dispel == DISPEL_MAGIC)
- {
- bool positive = true;
- if (!aur->IsPositive())
- positive = false;
- else
- positive = (aur->GetSpellProto()->AttributesEx & SPELL_ATTR_EX_NEGATIVE)==0;
-
- // do not remove positive auras if friendly target
- // negative auras if non-friendly target
- if(positive == unitTarget->IsFriendlyTo(m_caster))
- continue;
- }
- // Add aura to dispel list
- dispel_list.push_back(aur);
- }
- }
- // Ok if exist some buffs for dispel try dispel it
- if (!dispel_list.empty())
- {
- std::list < std::pair<uint32,uint64> > success_list;// (spell_id,casterGuid)
- std::list < uint32 > fail_list; // spell_id
- int32 list_size = dispel_list.size();
- // Dispell N = damage buffs (or while exist buffs for dispel)
- for (int32 count=0; count < damage && list_size > 0; ++count)
- {
- // Random select buff for dispel
- Aura *aur = dispel_list[urand(0, list_size-1)];
-
- SpellEntry const* spellInfo = aur->GetSpellProto();
- // Base dispel chance
- // TODO: possible chance depend from spell level??
- int32 miss_chance = 0;
- // Apply dispel mod from aura caster
- if (Unit *caster = aur->GetCaster())
- {
- if ( Player* modOwner = caster->GetSpellModOwner() )
- modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_DISPEL_CHANCE, miss_chance, this);
- }
- // Try dispel
- if (roll_chance_i(miss_chance))
- fail_list.push_back(aur->GetId());
- else
- success_list.push_back(std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID()));
- // Remove buff from list for prevent doubles
- for (std::vector<Aura *>::iterator j = dispel_list.begin(); j != dispel_list.end(); )
- {
- Aura *dispeled = *j;
- if (dispeled->GetId() == aur->GetId() && dispeled->GetCasterGUID() == aur->GetCasterGUID())
- {
- j = dispel_list.erase(j);
- --list_size;
- }
- else
- ++j;
- }
- }
- // Send success log and really remove auras
- if (!success_list.empty())
- {
- int32 count = success_list.size();
- WorldPacket data(SMSG_SPELLDISPELLOG, 8+8+4+1+4+count*5);
- data.append(unitTarget->GetPackGUID()); // Victim GUID
- data.append(m_caster->GetPackGUID()); // Caster GUID
- data << uint32(m_spellInfo->Id); // Dispell spell id
- data << uint8(0); // not used
- data << uint32(count); // count
- for (std::list<std::pair<uint32,uint64> >::iterator j = success_list.begin(); j != success_list.end(); ++j)
- {
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first);
- data << uint32(spellInfo->Id); // Spell Id
- data << uint8(0); // 0 - dispeled !=0 cleansed
- unitTarget->RemoveAurasDueToSpellByDispel(spellInfo->Id, j->second, m_caster);
- }
- m_caster->SendMessageToSet(&data, true);
-
- // On succes dispel
- // Devour Magic
- if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == 12)
- {
- uint32 heal_spell = 0;
- switch (m_spellInfo->Id)
- {
- case 19505: heal_spell = 19658; break;
- case 19731: heal_spell = 19732; break;
- case 19734: heal_spell = 19733; break;
- case 19736: heal_spell = 19735; break;
- case 27276: heal_spell = 27278; break;
- case 27277: heal_spell = 27279; break;
- default:
- sLog.outDebug("Spell for Devour Magic %d not handled in Spell::EffectDispel", m_spellInfo->Id);
- break;
- }
- if (heal_spell)
- m_caster->CastSpell(m_caster, heal_spell, true);
- }
- }
- // Send fail log to client
- if (!fail_list.empty())
- {
- // Failed to dispell
- WorldPacket data(SMSG_DISPEL_FAILED, 8+8+4+4*fail_list.size());
- data << uint64(m_caster->GetGUID()); // Caster GUID
- data << uint64(unitTarget->GetGUID()); // Victim GUID
- data << uint32(m_spellInfo->Id); // Dispell spell id
- for (std::list< uint32 >::iterator j = fail_list.begin(); j != fail_list.end(); ++j)
- data << uint32(*j); // Spell Id
- m_caster->SendMessageToSet(&data, true);
- }
- }
-}
-
-void Spell::EffectDualWield(uint32 /*i*/)
-{
- if (unitTarget->GetTypeId() == TYPEID_PLAYER)
- ((Player*)unitTarget)->SetCanDualWield(true);
-}
-
-void Spell::EffectPull(uint32 /*i*/)
-{
- // TODO: create a proper pull towards distract spell center for distract
- sLog.outDebug("WORLD: Spell Effect DUMMY");
-}
-
-void Spell::EffectDistract(uint32 /*i*/)
-{
- // Check for possible target
- if (!unitTarget || unitTarget->isInCombat())
- return;
-
- // target must be OK to do this
- 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);
-
- if ( unitTarget->GetTypeId() == TYPEID_PLAYER )
- {
- // For players just turn them
- WorldPacket data;
- ((Player*)unitTarget)->BuildTeleportAckMsg(&data, unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), angle);
- ((Player*)unitTarget)->GetSession()->SendPacket( &data );
- ((Player*)unitTarget)->SetPosition(unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), angle, false);
- }
- else
- {
- // Set creature Distracted, Stop it, And turn it
- unitTarget->SetOrientation(angle);
- unitTarget->StopMoving();
- unitTarget->GetMotionMaster()->MoveDistract(damage*1000);
- }
-}
-
-void Spell::EffectPickPocket(uint32 /*i*/)
-{
- if( m_caster->GetTypeId() != TYPEID_PLAYER )
- return;
-
- // victim must be creature and attackable
- if( !unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->IsFriendlyTo(unitTarget) )
- return;
-
- // victim have to be alive and humanoid or undead
- if( unitTarget->isAlive() && (unitTarget->GetCreatureTypeMask() &CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) != 0)
- {
- int32 chance = 10 + int32(m_caster->getLevel()) - int32(unitTarget->getLevel());
-
- if (chance > irand(0, 19))
- {
- // Stealing successful
- //sLog.outDebug("Sending loot from pickpocket");
- ((Player*)m_caster)->SendLoot(unitTarget->GetGUID(),LOOT_PICKPOCKETING);
- }
- else
- {
- // Reveal action + get attack
- m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
- if (((Creature*)unitTarget)->AI())
- ((Creature*)unitTarget)->AI()->AttackStart(m_caster);
- }
- }
-}
-
-void Spell::EffectAddFarsight(uint32 i)
-{
- float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
- int32 duration = GetSpellDuration(m_spellInfo);
- DynamicObject* dynObj = new DynamicObject;
- if(!dynObj->Create(objmgr.GenerateLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, i, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, radius))
- {
- delete dynObj;
- return;
- }
- dynObj->SetUInt32Value(OBJECT_FIELD_TYPE, 65);
- dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x80000002);
- m_caster->AddDynObject(dynObj);
- MapManager::Instance().GetMap(dynObj->GetMapId(), dynObj)->Add(dynObj);
- m_caster->SetUInt64Value(PLAYER_FARSIGHT, dynObj->GetGUID());
-}
-
-void Spell::EffectSummonWild(uint32 i)
-{
- uint32 creature_entry = m_spellInfo->EffectMiscValue[i];
- if(!creature_entry)
- return;
-
- uint32 level = m_caster->getLevel();
-
- // level of creature summoned using engineering item based at engineering skill level
- if(m_caster->GetTypeId()==TYPEID_PLAYER && m_CastItem)
- {
- ItemPrototype const *proto = m_CastItem->GetProto();
- if(proto && proto->RequiredSkill == SKILL_ENGINERING)
- {
- uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINERING);
- if(skill202)
- {
- level = skill202/5;
- }
- }
- }
-
- // select center of summon position
- float center_x = m_targets.m_destX;
- float center_y = m_targets.m_destY;
- float center_z = m_targets.m_destZ;
-
- float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
-
- int32 amount = damage > 0 ? damage : 1;
-
- for(int32 count = 0; count < amount; ++count)
- {
- float px, py, pz;
- // If dest location if present
- if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
- {
- // Summon 1 unit in dest location
- if (count == 0)
- {
- px = m_targets.m_destX;
- py = m_targets.m_destY;
- pz = m_targets.m_destZ;
- }
- // Summon in random point all other units if location present
- else
- m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz);
- }
- // Summon if dest location not present near caster
- else
- m_caster->GetClosePoint(px,py,pz,3.0f);
-
- int32 duration = GetSpellDuration(m_spellInfo);
-
- TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OR_DEAD_DESPAWN;
-
- m_caster->SummonCreature(creature_entry,px,py,pz,m_caster->GetOrientation(),summonType,duration);
- }
-}
-
-void Spell::EffectSummonGuardian(uint32 i)
-{
- uint32 pet_entry = m_spellInfo->EffectMiscValue[i];
- if(!pet_entry)
- return;
-
- // Jewelery statue case (totem like)
- if(m_spellInfo->SpellIconID==2056)
- {
- EffectSummonTotem(i);
- return;
- }
-
- // set timer for unsummon
- int32 duration = GetSpellDuration(m_spellInfo);
-
- // Search old Guardian only for players (if casted spell not have duration or cooldown)
- // FIXME: some guardians have control spell applied and controlled by player and anyway player can't summon in this time
- // so this code hack in fact
- if( m_caster->GetTypeId() == TYPEID_PLAYER && (duration <= 0 || GetSpellRecoveryTime(m_spellInfo)==0) )
- if(((Player*)m_caster)->HasGuardianWithEntry(pet_entry))
- return; // find old guardian, ignore summon
-
- // in another case summon new
- uint32 level = m_caster->getLevel();
-
- // level of pet summoned using engineering item based at engineering skill level
- if(m_caster->GetTypeId()==TYPEID_PLAYER && m_CastItem)
- {
- ItemPrototype const *proto = m_CastItem->GetProto();
- if(proto && proto->RequiredSkill == SKILL_ENGINERING)
- {
- uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINERING);
- if(skill202)
- {
- level = skill202/5;
- }
- }
- }
-
- // select center of summon position
- float center_x = m_targets.m_destX;
- float center_y = m_targets.m_destY;
- float center_z = m_targets.m_destZ;
-
- float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
-
- int32 amount = damage > 0 ? damage : 1;
-
- for(int32 count = 0; count < amount; ++count)
- {
- Pet* spawnCreature = new Pet(GUARDIAN_PET);
-
- Map *map = m_caster->GetMap();
- uint32 pet_number = objmgr.GeneratePetNumber();
- if(!spawnCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map,m_spellInfo->EffectMiscValue[i], pet_number))
- {
- sLog.outError("no such creature entry %u",m_spellInfo->EffectMiscValue[i]);
- delete spawnCreature;
- return;
- }
-
- float px, py, pz;
- // If dest location if present
- if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
- {
- // Summon 1 unit in dest location
- if (count == 0)
- {
- px = m_targets.m_destX;
- py = m_targets.m_destY;
- pz = m_targets.m_destZ;
- }
- // Summon in random point all other units if location present
- else
- m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz);
- }
- // Summon if dest location not present near caster
- else
- m_caster->GetClosePoint(px,py,pz,spawnCreature->GetObjectSize());
-
- spawnCreature->Relocate(px,py,pz,m_caster->GetOrientation());
-
- if(!spawnCreature->IsPositionValid())
- {
- sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %d Y: ^%d)", spawnCreature->GetGUIDLow(), spawnCreature->GetEntry(), spawnCreature->GetPositionX(), spawnCreature->GetPositionY());
- delete spawnCreature;
- return;
- }
-
- if(duration > 0)
- spawnCreature->SetDuration(duration);
-
- spawnCreature->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID());
- spawnCreature->setPowerType(POWER_MANA);
- spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS , 0);
- spawnCreature->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
- spawnCreature->SetUInt32Value(UNIT_FIELD_FLAGS,0);
- spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
- spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
- spawnCreature->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
- spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
-
- spawnCreature->InitStatsForLevel(level);
- spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false);
-
- spawnCreature->AIM_Initialize();
-
- if(m_caster->GetTypeId()==TYPEID_PLAYER)
- ((Player*)m_caster)->AddGuardian(spawnCreature);
-
- map->Add((Creature*)spawnCreature);
- }
-}
-
-void Spell::EffectTeleUnitsFaceCaster(uint32 i)
-{
- if(!unitTarget)
- return;
-
- if(unitTarget->isInFlight())
- return;
-
- uint32 mapid = m_caster->GetMapId();
- float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
-
- float fx,fy,fz;
- m_caster->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis);
-
- if(unitTarget->GetTypeId() == TYPEID_PLAYER)
- ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, -m_caster->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
- else
- MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)m_caster, fx, fy, fz, -m_caster->GetOrientation());
-}
-
-void Spell::EffectLearnSkill(uint32 i)
-{
- if(unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if(damage < 0)
- return;
-
- uint32 skillid = m_spellInfo->EffectMiscValue[i];
- uint16 skillval = ((Player*)unitTarget)->GetPureSkillValue(skillid);
- ((Player*)unitTarget)->SetSkill(skillid, skillval?skillval:1, damage*75);
-}
-
-void Spell::EffectAddHonor(uint32 /*i*/)
-{
- if(unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- sLog.outDebug("SpellEffect::AddHonor called for spell_id %u , that rewards %d honor points to player: %u", m_spellInfo->Id, this->damage, ((Player*)unitTarget)->GetGUIDLow());
-
- // TODO: find formula for honor reward based on player's level!
-
- // now fixed only for level 70 players:
- if (((Player*)unitTarget)->getLevel() == 70)
- ((Player*)unitTarget)->RewardHonor(NULL, 1, this->damage);
-}
-
-void Spell::EffectTradeSkill(uint32 /*i*/)
-{
- if(unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
- // uint32 skillid = m_spellInfo->EffectMiscValue[i];
- // uint16 skillmax = ((Player*)unitTarget)->(skillid);
- // ((Player*)unitTarget)->SetSkill(skillid,skillval?skillval:1,skillmax+75);
-}
-
-void Spell::EffectEnchantItemPerm(uint32 i)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
- if (!itemTarget)
- return;
-
- Player* p_caster = (Player*)m_caster;
-
- p_caster->UpdateCraftSkill(m_spellInfo->Id);
-
- if (m_spellInfo->EffectMiscValue[i])
- {
- uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
-
- SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!pEnchant)
- return;
-
- // item can be in trade slot and have owner diff. from caster
- Player* item_owner = itemTarget->GetOwner();
- if(!item_owner)
- return;
-
- if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
- sLog.outCommand("GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)",
- p_caster->GetName(),p_caster->GetSession()->GetAccountId(),
- itemTarget->GetProto()->Name1,itemTarget->GetEntry(),
- item_owner->GetName(),item_owner->GetSession()->GetAccountId());
-
- // remove old enchanting before applying new if equipped
- item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,false);
-
- itemTarget->SetEnchantment(PERM_ENCHANTMENT_SLOT, enchant_id, 0, 0);
-
- // add new enchanting if equipped
- item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,true);
- }
-}
-
-void Spell::EffectEnchantItemTmp(uint32 i)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player* p_caster = (Player*)m_caster;
-
- if(!itemTarget)
- return;
-
- uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
-
- // Shaman Rockbiter Weapon
- if(i==0 && m_spellInfo->Effect[1]==SPELL_EFFECT_DUMMY)
- {
- int32 enchnting_damage = m_currentBasePoints[1]+1;
-
- // enchanting id selected by calculated damage-per-sec stored in Effect[1] base value
- // with already applied percent bonus from Elemental Weapons talent
- // Note: damage calculated (correctly) with rounding int32(float(v)) but
- // RW enchantments applied damage int32(float(v)+0.5), this create 0..1 difference sometime
- switch(enchnting_damage)
- {
- // Rank 1
- case 2: enchant_id = 29; break; // 0% [ 7% == 2, 14% == 2, 20% == 2]
- // Rank 2
- case 4: enchant_id = 6; break; // 0% [ 7% == 4, 14% == 4]
- case 5: enchant_id = 3025; break; // 20%
- // Rank 3
- case 6: enchant_id = 1; break; // 0% [ 7% == 6, 14% == 6]
- case 7: enchant_id = 3027; break; // 20%
- // Rank 4
- case 9: enchant_id = 3032; break; // 0% [ 7% == 6]
- case 10: enchant_id = 503; break; // 14%
- case 11: enchant_id = 3031; break; // 20%
- // Rank 5
- case 15: enchant_id = 3035; break; // 0%
- case 16: enchant_id = 1663; break; // 7%
- case 17: enchant_id = 3033; break; // 14%
- case 18: enchant_id = 3034; break; // 20%
- // Rank 6
- case 28: enchant_id = 3038; break; // 0%
- case 29: enchant_id = 683; break; // 7%
- case 31: enchant_id = 3036; break; // 14%
- case 33: enchant_id = 3037; break; // 20%
- // Rank 7
- case 40: enchant_id = 3041; break; // 0%
- case 42: enchant_id = 1664; break; // 7%
- case 45: enchant_id = 3039; break; // 14%
- case 48: enchant_id = 3040; break; // 20%
- // Rank 8
- case 49: enchant_id = 3044; break; // 0%
- case 52: enchant_id = 2632; break; // 7%
- case 55: enchant_id = 3042; break; // 14%
- case 58: enchant_id = 3043; break; // 20%
- // Rank 9
- case 62: enchant_id = 2633; break; // 0%
- case 66: enchant_id = 3018; break; // 7%
- case 70: enchant_id = 3019; break; // 14%
- case 74: enchant_id = 3020; break; // 20%
- default:
- sLog.outError("Spell::EffectEnchantItemTmp: Damage %u not handled in S'RW",enchnting_damage);
- return;
- }
- }
-
- if (!enchant_id)
- {
- sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have 0 as enchanting id",m_spellInfo->Id,i);
- return;
- }
-
- SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!pEnchant)
- {
- sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have not existed enchanting id %u ",m_spellInfo->Id,i,enchant_id);
- return;
- }
-
- // select enchantment duration
- uint32 duration;
-
- // rogue family enchantments exception by duration
- if(m_spellInfo->Id==38615)
- duration = 1800; // 30 mins
- // other rogue family enchantments always 1 hour (some have spell damage=0, but some have wrong data in EffBasePoints)
- else if(m_spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE)
- duration = 3600; // 1 hour
- // shaman family enchantments
- else if(m_spellInfo->SpellFamilyName==SPELLFAMILY_SHAMAN)
- duration = 1800; // 30 mins
- // other cases with this SpellVisual already selected
- else if(m_spellInfo->SpellVisual==215)
- duration = 1800; // 30 mins
- // some fishing pole bonuses
- else if(m_spellInfo->SpellVisual==563)
- duration = 600; // 10 mins
- // shaman rockbiter enchantments
- else if(m_spellInfo->SpellVisual==0)
- duration = 1800; // 30 mins
- else if(m_spellInfo->Id==29702)
- duration = 300; // 5 mins
- else if(m_spellInfo->Id==37360)
- duration = 300; // 5 mins
- // default case
- else
- duration = 3600; // 1 hour
-
- // item can be in trade slot and have owner diff. from caster
- Player* item_owner = itemTarget->GetOwner();
- if(!item_owner)
- return;
-
- if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
- sLog.outCommand("GM %s (Account: %u) enchanting(temp): %s (Entry: %d) for player: %s (Account: %u)",
- p_caster->GetName(),p_caster->GetSession()->GetAccountId(),
- itemTarget->GetProto()->Name1,itemTarget->GetEntry(),
- item_owner->GetName(),item_owner->GetSession()->GetAccountId());
-
- // remove old enchanting before applying new if equipped
- item_owner->ApplyEnchantment(itemTarget,TEMP_ENCHANTMENT_SLOT,false);
-
- itemTarget->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, duration*1000, 0);
-
- // add new enchanting if equipped
- item_owner->ApplyEnchantment(itemTarget,TEMP_ENCHANTMENT_SLOT,true);
-}
-
-void Spell::EffectTameCreature(uint32 /*i*/)
-{
- if(m_caster->GetPetGUID())
- return;
-
- if(!unitTarget)
- return;
-
- if(unitTarget->GetTypeId() == TYPEID_PLAYER)
- return;
-
- Creature* creatureTarget = (Creature*)unitTarget;
-
- if(creatureTarget->isPet())
- return;
-
- if(m_caster->getClass() == CLASS_HUNTER)
- {
- // cast finish successfully
- //SendChannelUpdate(0);
- finish();
-
- Pet* pet = new Pet(HUNTER_PET);
-
- if(!pet->CreateBaseAtCreature(creatureTarget))
- {
- delete pet;
- return;
- }
-
- creatureTarget->setDeathState(JUST_DIED);
- creatureTarget->RemoveCorpse();
- creatureTarget->SetHealth(0); // just for nice GM-mode view
-
- pet->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, m_caster->GetGUID());
- pet->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
- pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
- pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
-
- if(!pet->InitStatsForLevel(creatureTarget->getLevel()))
- {
- sLog.outError("ERROR: InitStatsForLevel() in EffectTameCreature failed! Pet deleted.");
- delete pet;
- return;
- }
-
- // prepare visual effect for levelup
- pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel()-1);
-
- pet->GetCharmInfo()->SetPetNumber(objmgr.GeneratePetNumber(), true);
- // this enables pet details window (Shift+P)
- pet->AIM_Initialize();
- pet->InitPetCreateSpells();
- pet->SetHealth(pet->GetMaxHealth());
-
- MapManager::Instance().GetMap(pet->GetMapId(), pet)->Add((Creature*)pet);
-
- // visual effect for levelup
- pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel());
-
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- m_caster->SetPet(pet);
- pet->SavePetToDB(PET_SAVE_AS_CURRENT);
- ((Player*)m_caster)->PetSpellInitialize();
- }
- }
-}
-
-void Spell::EffectSummonPet(uint32 i)
-{
- uint32 petentry = m_spellInfo->EffectMiscValue[i];
-
- Pet *OldSummon = m_caster->GetPet();
-
- // if pet requested type already exist
- if( OldSummon )
- {
- if(petentry == 0 || OldSummon->GetEntry() == petentry)
- {
- // pet in corpse state can't be summoned
- if( OldSummon->isDead() )
- return;
-
- MapManager::Instance().GetMap(OldSummon->GetMapId(), OldSummon)->Remove((Creature*)OldSummon,false);
- OldSummon->SetMapId(m_caster->GetMapId());
-
- float px, py, pz;
- m_caster->GetClosePoint(px, py, pz, OldSummon->GetObjectSize());
-
- OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation());
- MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)->Add((Creature*)OldSummon);
-
- if(m_caster->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled() )
- {
- ((Player*)m_caster)->PetSpellInitialize();
- }
- return;
- }
-
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- ((Player*)m_caster)->RemovePet(OldSummon,(OldSummon->getPetType()==HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT),false);
- else
- return;
- }
-
- Pet* NewSummon = new Pet;
-
- // petentry==0 for hunter "call pet" (current pet summoned if any)
- if(NewSummon->LoadPetFromDB(m_caster,petentry))
- {
- if(NewSummon->getPetType()==SUMMON_PET)
- {
- // Remove Demonic Sacrifice auras (known pet)
- Unit::AuraList const& auraClassScripts = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
- for(Unit::AuraList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();)
- {
- if((*itr)->GetModifier()->m_miscvalue==2228)
- {
- m_caster->RemoveAurasDueToSpell((*itr)->GetId());
- itr = auraClassScripts.begin();
- }
- else
- ++itr;
- }
- }
-
- return;
- }
-
- // not error in case fail hunter call pet
- if(!petentry)
- {
- delete NewSummon;
- return;
- }
-
- CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(petentry);
-
- if(!cInfo)
- {
- sLog.outError("EffectSummonPet: creature entry %u not found.",petentry);
- delete NewSummon;
- return;
- }
-
- Map *map = m_caster->GetMap();
- uint32 pet_number = objmgr.GeneratePetNumber();
- if(!NewSummon->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map, petentry, pet_number))
- {
- delete NewSummon;
- return;
- }
-
- float px, py, pz;
- m_caster->GetClosePoint(px, py, pz, NewSummon->GetObjectSize());
-
- NewSummon->Relocate(px, py, pz, m_caster->GetOrientation());
-
- if(!NewSummon->IsPositionValid())
- {
- sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", NewSummon->GetGUIDLow(), NewSummon->GetEntry(), NewSummon->GetPositionX(), NewSummon->GetPositionY());
- delete NewSummon;
- return;
- }
-
- uint32 petlevel = m_caster->getLevel();
- NewSummon->setPetType(SUMMON_PET);
-
- uint32 faction = m_caster->getFaction();
- if(m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isTotem())
- {
- Unit* owner = ((Totem*)m_caster)->GetOwner();
- if(owner)
- faction = owner->getFaction();
- NewSummon->GetCharmInfo()->SetReactState(REACT_AGGRESSIVE);
- }
-
- NewSummon->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, m_caster->GetGUID());
- NewSummon->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
- NewSummon->SetUInt32Value(UNIT_NPC_FLAGS , 0);
- NewSummon->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, faction);
- NewSummon->SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
- NewSummon->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
- NewSummon->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,time(NULL));
- NewSummon->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
- NewSummon->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
- NewSummon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
-
- NewSummon->GetCharmInfo()->SetPetNumber(pet_number, true);
- // this enables pet details window (Shift+P)
-
- // this enables popup window (pet dismiss, cancel), hunter pet additional flags set later
- NewSummon->SetUInt32Value(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE);
-
- NewSummon->InitStatsForLevel( petlevel);
- NewSummon->InitPetCreateSpells();
-
- if(NewSummon->getPetType()==SUMMON_PET)
- {
- // Remove Demonic Sacrifice auras (new pet)
- Unit::AuraList const& auraClassScripts = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
- for(Unit::AuraList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();)
- {
- if((*itr)->GetModifier()->m_miscvalue==2228)
- {
- m_caster->RemoveAurasDueToSpell((*itr)->GetId());
- itr = auraClassScripts.begin();
- }
- else
- ++itr;
- }
-
- // generate new name for summon pet
- std::string new_name=objmgr.GeneratePetName(petentry);
- if(!new_name.empty())
- NewSummon->SetName(new_name);
- }
- else if(NewSummon->getPetType()==HUNTER_PET)
- NewSummon->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED);
-
- NewSummon->AIM_Initialize();
- NewSummon->SetHealth(NewSummon->GetMaxHealth());
- NewSummon->SetPower(POWER_MANA, NewSummon->GetMaxPower(POWER_MANA));
-
- map->Add((Creature*)NewSummon);
-
- m_caster->SetPet(NewSummon);
- sLog.outDebug("New Pet has guid %u", NewSummon->GetGUIDLow());
-
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- NewSummon->SavePetToDB(PET_SAVE_AS_CURRENT);
- ((Player*)m_caster)->PetSpellInitialize();
- }
-}
-
-void Spell::EffectLearnPetSpell(uint32 i)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player *_player = (Player*)m_caster;
-
- Pet *pet = _player->GetPet();
- if(!pet)
- return;
- if(!pet->isAlive())
- return;
-
- SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(m_spellInfo->EffectTriggerSpell[i]);
- if(!learn_spellproto)
- return;
-
- pet->SetTP(pet->m_TrainingPoints - pet->GetTPForSpell(learn_spellproto->Id));
- pet->learnSpell(learn_spellproto->Id);
-
- pet->SavePetToDB(PET_SAVE_AS_CURRENT);
- _player->PetSpellInitialize();
-}
-
-void Spell::EffectTaunt(uint32 /*i*/)
-{
- // this effect use before aura Taunt apply for prevent taunt already attacking target
- // for spell as marked "non effective at already attacking target"
- if(unitTarget && unitTarget->GetTypeId() != TYPEID_PLAYER)
- {
- if(unitTarget->getVictim()==m_caster)
- {
- SendCastResult(SPELL_FAILED_DONT_REPORT);
- return;
- }
- }
-
- // Also use this effect to set the taunter's threat to the taunted creature's highest value
- if(unitTarget->CanHaveThreatList() && unitTarget->getThreatManager().getCurrentVictim())
- unitTarget->getThreatManager().addThreat(m_caster,unitTarget->getThreatManager().getCurrentVictim()->getThreat());
-}
-
-void Spell::EffectWeaponDmg(uint32 i)
-{
- if(!unitTarget)
- return;
- if(!unitTarget->isAlive())
- return;
-
- // multiple weapon dmg effect workaround
- // execute only the last weapon damage
- // and handle all effects at once
- for (int j = 0; j < 3; j++)
- {
- switch(m_spellInfo->Effect[j])
- {
- case SPELL_EFFECT_WEAPON_DAMAGE:
- case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
- case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
- case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
- if (j < i) // we must calculate only at last weapon effect
- return;
- break;
- }
- }
-
- // some spell specific modifiers
- bool customBonusDamagePercentMod = false;
- float bonusDamagePercentMod = 1.0f; // applied to fixed effect damage bonus if set customBonusDamagePercentMod
- float weaponDamagePercentMod = 1.0f; // applied to weapon damage (and to fixed effect damage bonus if customBonusDamagePercentMod not set
- float totalDamagePercentMod = 1.0f; // applied to final bonus+weapon damage
- bool normalized = false;
-
- int32 spell_bonus = 0; // bonus specific for spell
- switch(m_spellInfo->SpellFamilyName)
- {
- case SPELLFAMILY_WARRIOR:
- {
- // Whirlwind, single only spell with 2 weapon white damage apply if have
- if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & 0x00000400000000LL))
- {
- if(((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK,true))
- spell_bonus += m_caster->CalculateDamage (OFF_ATTACK, normalized);
- }
- // Devastate bonus and sunder armor refresh
- else if(m_spellInfo->SpellVisual == 671 && m_spellInfo->SpellIconID == 1508)
- {
- customBonusDamagePercentMod = true;
- bonusDamagePercentMod = 0.0f; // only applied if auras found
-
- Unit::AuraList const& list = unitTarget->GetAurasByType(SPELL_AURA_MOD_RESISTANCE);
- for(Unit::AuraList::const_iterator itr=list.begin();itr!=list.end();++itr)
- {
- SpellEntry const *proto = (*itr)->GetSpellProto();
- if(proto->SpellVisual == 406 && proto->SpellIconID == 565)
- {
- int32 duration = GetSpellDuration(proto);
- (*itr)->SetAuraDuration(duration);
- (*itr)->UpdateAuraDuration();
- bonusDamagePercentMod += 1.0f; // +100%
- }
- }
- }
- break;
- }
- case SPELLFAMILY_ROGUE:
- {
- // Ambush
- if(m_spellInfo->SpellFamilyFlags & 0x00000200LL)
- {
- customBonusDamagePercentMod = true;
- bonusDamagePercentMod = 2.5f; // 250%
- }
- // Mutilate (for each hand)
- else if(m_spellInfo->SpellFamilyFlags & 0x600000000LL)
- {
- bool found = false;
- // fast check
- if(unitTarget->HasAuraState(AURA_STATE_DEADLY_POISON))
- found = true;
- // full aura scan
- else
- {
- Unit::AuraMap const& auras = unitTarget->GetAuras();
- for(Unit::AuraMap::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
- {
- if(itr->second->GetSpellProto()->Dispel == DISPEL_POISON)
- {
- found = true;
- break;
- }
- }
- }
-
- if(found)
- totalDamagePercentMod *= 1.5f; // 150% if poisoned
- }
- break;
- }
- case SPELLFAMILY_PALADIN:
- {
- // Seal of Command - receive benefit from Spell Damage and Healing
- if(m_spellInfo->SpellFamilyFlags & 0x00000002000000LL)
- {
- spell_bonus += int32(0.20f*m_caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellInfo)));
- spell_bonus += int32(0.29f*m_caster->SpellBaseDamageBonusForVictim(GetSpellSchoolMask(m_spellInfo), unitTarget));
- }
- break;
- }
- case SPELLFAMILY_SHAMAN:
- {
- // Skyshatter Harness item set bonus
- // Stormstrike
- if(m_spellInfo->SpellFamilyFlags & 0x001000000000LL)
- {
- Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
- for(Unit::AuraList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i)
- {
- // Stormstrike AP Buff
- if ( (*i)->GetModifier()->m_miscvalue == 5634 )
- {
- m_caster->CastSpell(m_caster,38430,true,NULL,*i);
- break;
- }
- }
- }
- }
- }
-
- int32 fixed_bonus = 0;
- for (int j = 0; j < 3; j++)
- {
- switch(m_spellInfo->Effect[j])
- {
- case SPELL_EFFECT_WEAPON_DAMAGE:
- case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
- fixed_bonus += CalculateDamage(j,unitTarget);
- break;
- case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
- fixed_bonus += CalculateDamage(j,unitTarget);
- normalized = true;
- break;
- case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
- weaponDamagePercentMod *= float(CalculateDamage(j,unitTarget)) / 100.0f;
-
- // applied only to prev.effects fixed damage
- if(customBonusDamagePercentMod)
- fixed_bonus = int32(fixed_bonus*bonusDamagePercentMod);
- else
- fixed_bonus = int32(fixed_bonus*weaponDamagePercentMod);
- break;
- default:
- break; // not weapon damage effect, just skip
- }
- }
-
- // non-weapon damage
- int32 bonus = spell_bonus + fixed_bonus;
-
- // apply to non-weapon bonus weapon total pct effect, weapon total flat effect included in weapon damage
- if(bonus)
- {
- UnitMods unitMod;
- switch(m_attackType)
- {
- default:
- case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break;
- case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break;
- case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break;
- }
-
- float weapon_total_pct = m_caster->GetModifierValue(unitMod, TOTAL_PCT);
- bonus = int32(bonus*weapon_total_pct);
- }
-
- // + weapon damage with applied weapon% dmg to base weapon damage in call
- bonus += int32(m_caster->CalculateDamage(m_attackType, normalized)*weaponDamagePercentMod);
-
- // total damage
- bonus = int32(bonus*totalDamagePercentMod);
-
- // prevent negative damage
- uint32 eff_damage = uint32(bonus > 0 ? bonus : 0);
-
- const uint32 nohitMask = HITINFO_ABSORB | HITINFO_RESIST | HITINFO_MISS;
-
- uint32 hitInfo = 0;
- VictimState victimState = VICTIMSTATE_NORMAL;
- uint32 blocked_dmg = 0;
- uint32 absorbed_dmg = 0;
- uint32 resisted_dmg = 0;
- CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
-
- m_caster->DoAttackDamage(unitTarget, &eff_damage, &cleanDamage, &blocked_dmg, m_spellSchoolMask, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, m_attackType, m_spellInfo, m_IsTriggeredSpell);
-
- if ((hitInfo & nohitMask) && m_attackType != RANGED_ATTACK) // not send ranged miss/etc
- m_caster->SendAttackStateUpdate(hitInfo & nohitMask, unitTarget, 1, m_spellSchoolMask, eff_damage, absorbed_dmg, resisted_dmg, VICTIMSTATE_NORMAL, blocked_dmg);
-
- bool criticalhit = (hitInfo & HITINFO_CRITICALHIT);
- m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, eff_damage, m_spellSchoolMask, absorbed_dmg, resisted_dmg, false, blocked_dmg, criticalhit);
-
- if (eff_damage > (absorbed_dmg + resisted_dmg + blocked_dmg))
- {
- eff_damage -= (absorbed_dmg + resisted_dmg + blocked_dmg);
- }
- else
- {
- cleanDamage.damage += eff_damage;
- eff_damage = 0;
- }
-
- // SPELL_SCHOOL_NORMAL use for weapon-like threat and rage calculation
- m_caster->DealDamage(unitTarget, eff_damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, true);
-
- // Hemorrhage
- if(m_spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & 0x2000000))
- {
- if(m_caster->GetTypeId()==TYPEID_PLAYER)
- ((Player*)m_caster)->AddComboPoints(unitTarget, 1);
- }
- // Mangle (Cat): CP
- if(m_spellInfo->SpellFamilyName==SPELLFAMILY_DRUID && (m_spellInfo->SpellFamilyFlags==0x0000040000000000LL))
- {
- if(m_caster->GetTypeId()==TYPEID_PLAYER)
- ((Player*)m_caster)->AddComboPoints(unitTarget,1);
- }
-
-
- // take ammo
- if(m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK );
-
- // wands don't have ammo
- if(!pItem || pItem->IsBroken() || pItem->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_WAND)
- return;
-
- if( pItem->GetProto()->InventoryType == INVTYPE_THROWN )
- {
- if(pItem->GetMaxStackCount()==1)
- {
- // decrease durability for non-stackable throw weapon
- ((Player*)m_caster)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED);
- }
- else
- {
- // decrease items amount for stackable throw weapon
- uint32 count = 1;
- ((Player*)m_caster)->DestroyItemCount( pItem, count, true);
- }
- }
- else if(uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID))
- ((Player*)m_caster)->DestroyItemCount(ammo, 1, true);
- }
-}
-
-void Spell::EffectThreat(uint32 /*i*/)
-{
- if(!unitTarget || !unitTarget->isAlive() || !m_caster->isAlive())
- return;
-
- if(!unitTarget->CanHaveThreatList())
- return;
-
- unitTarget->AddThreat(m_caster, float(damage));
-}
-
-void Spell::EffectHealMaxHealth(uint32 /*i*/)
-{
- if(!unitTarget)
- return;
- if(!unitTarget->isAlive())
- return;
-
- uint32 heal = m_caster->GetMaxHealth();
-
- int32 gain = unitTarget->ModifyHealth(heal);
- unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
-
- m_caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, heal);
-}
-
-void Spell::EffectInterruptCast(uint32 /*i*/)
-{
- if(!unitTarget)
- return;
- if(!unitTarget->isAlive())
- return;
-
- // TODO: not all spells that used this effect apply cooldown at school spells
- // also exist case: apply cooldown to interrupted cast only and to all spells
- for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
- {
- if (unitTarget->m_currentSpells[i])
- {
- // check if we can interrupt spell
- if ( unitTarget->m_currentSpells[i]->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT && unitTarget->m_currentSpells[i]->m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE )
- {
- unitTarget->ProhibitSpellScholl(GetSpellSchoolMask(unitTarget->m_currentSpells[i]->m_spellInfo), GetSpellDuration(m_spellInfo));
- unitTarget->InterruptSpell(i,false);
- }
- }
- }
-}
-
-void Spell::EffectSummonObjectWild(uint32 i)
-{
- uint32 gameobject_id = m_spellInfo->EffectMiscValue[i];
-
- GameObject* pGameObj = new GameObject;
-
- WorldObject* target = focusObject;
- if( !target )
- target = m_caster;
-
- float x,y,z;
- if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
- {
- x = m_targets.m_destX;
- y = m_targets.m_destY;
- z = m_targets.m_destZ;
- }
- else
- m_caster->GetClosePoint(x,y,z,DEFAULT_WORLD_OBJECT_SIZE);
-
- Map *map = target->GetMap();
-
- if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map,
- x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1))
- {
- delete pGameObj;
- return;
- }
-
- int32 duration = GetSpellDuration(m_spellInfo);
- pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
- pGameObj->SetSpellId(m_spellInfo->Id);
-
- if(pGameObj->GetGoType() != GAMEOBJECT_TYPE_FLAGDROP) // make dropped flag clickable for other players (not set owner guid (created by) for this)...
- m_caster->AddGameObject(pGameObj);
- map->Add(pGameObj);
-
- if(pGameObj->GetMapId() == 489 && pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP) //WS
- {
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- Player *pl = (Player*)m_caster;
- BattleGround* bg = ((Player *)m_caster)->GetBattleGround();
- if(bg && bg->GetTypeID()==BATTLEGROUND_WS && bg->GetStatus() == STATUS_IN_PROGRESS)
- {
- uint32 team = ALLIANCE;
-
- if(pl->GetTeam() == team)
- team = HORDE;
-
- ((BattleGroundWS*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID(),team);
- }
- }
- }
-
- if(pGameObj->GetMapId() == 566 && pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP) //EY
- {
- if(m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- BattleGround* bg = ((Player *)m_caster)->GetBattleGround();
- if(bg && bg->GetTypeID()==BATTLEGROUND_EY && bg->GetStatus() == STATUS_IN_PROGRESS)
- {
- ((BattleGroundEY*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID());
- }
- }
- }
-
- if(uint32 linkedEntry = pGameObj->GetLinkedGameObjectEntry())
- {
- GameObject* linkedGO = new GameObject;
- if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, map,
- x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1))
- {
- linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0);
- linkedGO->SetSpellId(m_spellInfo->Id);
-
- m_caster->AddGameObject(linkedGO);
- map->Add(linkedGO);
- }
- else
- {
- delete linkedGO;
- linkedGO = NULL;
- return;
- }
- }
-}
-
-void Spell::EffectScriptEffect(uint32 effIndex)
-{
- // TODO: we must implement hunter pet summon at login there (spell 6962)
-
- // by spell id
- switch(m_spellInfo->Id)
- {
- // Bending Shinbone
- case 8856:
- {
- if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- uint32 spell_id = 0;
- switch(urand(1,5))
- {
- case 1: spell_id = 8854; break;
- default: spell_id = 8855; break;
- }
-
- m_caster->CastSpell(m_caster,spell_id,true,NULL);
- return;
- }
-
- // Healthstone creating spells
- case 6201:
- case 6202:
- case 5699:
- case 11729:
- case 11730:
- case 27230:
- {
- uint32 itemtype;
- uint32 rank = 0;
- Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY);
- for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
- {
- if((*i)->GetId() == 18692)
- {
- rank = 1;
- break;
- }
- else if((*i)->GetId() == 18693)
- {
- rank = 2;
- break;
- }
- }
-
- static uint32 const itypes[6][3] = {
- { 5512,19004,19005}, // Minor Healthstone
- { 5511,19006,19007}, // Lesser Healthstone
- { 5509,19008,19009}, // Healthstone
- { 5510,19010,19011}, // Greater Healthstone
- { 9421,19012,19013}, // Major Healthstone
- {22103,22104,22105} // Master Healthstone
- };
-
- switch(m_spellInfo->Id)
- {
- case 6201: itemtype=itypes[0][rank];break; // Minor Healthstone
- case 6202: itemtype=itypes[1][rank];break; // Lesser Healthstone
- case 5699: itemtype=itypes[2][rank];break; // Healthstone
- case 11729: itemtype=itypes[3][rank];break; // Greater Healthstone
- case 11730: itemtype=itypes[4][rank];break; // Major Healthstone
- case 27230: itemtype=itypes[5][rank];break; // Master Healthstone
- default:
- return;
- }
- DoCreateItem( effIndex, itemtype );
- return;
- }
- // Brittle Armor - need remove one 24575 Brittle Armor aura
- case 24590:
- unitTarget->RemoveSingleAuraFromStack(24575, 0);
- unitTarget->RemoveSingleAuraFromStack(24575, 1);
- return;
- // Mercurial Shield - need remove one 26464 Mercurial Shield aura
- case 26465:
- unitTarget->RemoveSingleAuraFromStack(26464, 0);
- return;
- // Orb teleport spells
- case 25140:
- case 25143:
- case 25650:
- case 25652:
- case 29128:
- case 29129:
- case 35376:
- case 35727:
- {
- if(!unitTarget)
- return;
-
- uint32 spellid;
- switch(m_spellInfo->Id)
- {
- case 25140: spellid = 32571; break;
- case 25143: spellid = 32572; break;
- case 25650: spellid = 30140; break;
- case 25652: spellid = 30141; break;
- case 29128: spellid = 32568; break;
- case 29129: spellid = 32569; break;
- case 35376: spellid = 25649; break;
- case 35727: spellid = 35730; break;
- default:
- return;
- }
-
- unitTarget->CastSpell(unitTarget,spellid,false);
- return;
- }
-
- // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell)
- case 22539:
- case 22972:
- case 22975:
- case 22976:
- case 22977:
- case 22978:
- case 22979:
- case 22980:
- case 22981:
- case 22982:
- case 22983:
- case 22984:
- case 22985:
- {
- if(!unitTarget || !unitTarget->isAlive())
- return;
-
- // Onyxia Scale Cloak
- if(unitTarget->GetDummyAura(22683))
- return;
-
- // Shadow Flame
- m_caster->CastSpell(unitTarget, 22682, true);
- return;
- }
- break;
-
- // Summon Black Qiraji Battle Tank
- case 26656:
- {
- if(!unitTarget)
- return;
-
- // Prevent stacking of mounts
- unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
-
- // Two separate mounts depending on area id (allows use both in and out of specific instance)
- if (unitTarget->GetAreaId() == 3428)
- unitTarget->CastSpell(unitTarget, 25863, false);
- else
- unitTarget->CastSpell(unitTarget, 26655, false);
- break;
- }
- // Piccolo of the Flaming Fire
- case 17512:
- {
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
- unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE);
- break;
- }
-
- // Dreaming Glory
- case 28698:
- {
- if(!unitTarget)
- return;
- unitTarget->CastSpell(unitTarget, 28694, true);
- break;
- }
-
- // Netherbloom
- case 28702:
- {
- if(!unitTarget)
- return;
- // 25% chance of casting a random buff
- if(roll_chance_i(75))
- return;
-
- // triggered spells are 28703 to 28707
- // Note: some sources say, that there was the possibility of
- // receiving a debuff. However, this seems to be removed by a patch.
- const uint32 spellid = 28703;
-
- // don't overwrite an existing aura
- for(uint8 i=0; i<5; i++)
- if(unitTarget->HasAura(spellid+i, 0))
- return;
- unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true);
- break;
- }
-
- // Nightmare Vine
- case 28720:
- {
- if(!unitTarget)
- return;
- // 25% chance of casting Nightmare Pollen
- if(roll_chance_i(75))
- return;
- unitTarget->CastSpell(unitTarget, 28721, true);
- break;
- }
-
- // Mirren's Drinking Hat
- case 29830:
- {
- uint32 item = 0;
- switch ( urand(1,6) )
- {
- case 1: case 2: case 3: item = 23584; break;// Loch Modan Lager
- case 4: case 5: item = 23585; break;// Stouthammer Lite
- case 6: item = 23586; break;// Aerie Peak Pale Ale
- }
- if (item)
- DoCreateItem(effIndex,item);
- break;
- }
- // Improved Sprint
- case 30918:
- {
- // Removes snares and roots.
- uint32 mechanic_mask = (1<<MECHANIC_ROOT) | (1<<MECHANIC_SNARE);
- Unit::AuraMap& Auras = unitTarget->GetAuras();
- for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next)
- {
- next = iter;
- ++next;
- Aura *aur = iter->second;
- if (!aur->IsPositive()) //only remove negative spells
- {
- // check for mechanic mask
- if(GetSpellMechanicMask(aur->GetSpellProto(), aur->GetEffIndex()) & mechanic_mask)
- {
- unitTarget->RemoveAurasDueToSpell(aur->GetId());
- if(Auras.empty())
- break;
- else
- next = Auras.begin();
- }
- }
- }
- break;
- }
- case 41126: // Flame Crash
- {
- if(!unitTarget)
- return;
-
- unitTarget->CastSpell(unitTarget, 41131, true);
- break;
- }
- case 44876: // Force Cast - Portal Effect: Sunwell Isle
- {
- if(!unitTarget)
- return;
-
- unitTarget->CastSpell(unitTarget, 44870, true);
- break;
- }
-
- // Goblin Weather Machine
- case 46203:
- {
- if(!unitTarget)
- return;
-
- uint32 spellId;
- switch(rand()%4)
- {
- case 0:
- spellId=46740;
- break;
- case 1:
- spellId=46739;
- break;
- case 2:
- spellId=46738;
- break;
- case 3:
- spellId=46736;
- break;
- }
- unitTarget->CastSpell(unitTarget, spellId, true);
- break;
- }
- //5,000 Gold
- case 46642:
- {
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- ((Player*)unitTarget)->ModifyMoney(50000000);
-
- break;
- }
- }
-
- if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN )
- {
- switch(m_spellInfo->SpellFamilyFlags)
- {
- // Judgement
- case 0x800000:
- {
- if(!unitTarget || !unitTarget->isAlive())
- return;
- uint32 spellId2 = 0;
-
- // all seals have aura dummy
- Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
- for(Unit::AuraList::const_iterator itr = m_dummyAuras.begin(); itr != m_dummyAuras.end(); ++itr)
- {
- SpellEntry const *spellInfo = (*itr)->GetSpellProto();
-
- // search seal (all seals have judgement's aura dummy spell id in 2 effect
- if ( !spellInfo || !IsSealSpell((*itr)->GetSpellProto()) || (*itr)->GetEffIndex() != 2 )
- continue;
-
- // must be calculated base at raw base points in spell proto, GetModifier()->m_value for S.Righteousness modified by SPELLMOD_DAMAGE
- spellId2 = (*itr)->GetSpellProto()->EffectBasePoints[2]+1;
-
- if(spellId2 <= 1)
- continue;
-
- // found, remove seal
- m_caster->RemoveAurasDueToSpell((*itr)->GetId());
-
- // Sanctified Judgement
- Unit::AuraList const& m_auras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
- for(Unit::AuraList::const_iterator i = m_auras.begin(); i != m_auras.end(); ++i)
- {
- if ((*i)->GetSpellProto()->SpellIconID == 205 && (*i)->GetSpellProto()->Attributes == 0x01D0LL)
- {
- int32 chance = (*i)->GetModifier()->m_amount;
- if ( roll_chance_i(chance) )
- {
- int32 mana = spellInfo->manaCost;
- if ( Player* modOwner = m_caster->GetSpellModOwner() )
- modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, mana);
- mana = int32(mana* 0.8f);
- m_caster->CastCustomSpell(m_caster,31930,&mana,NULL,NULL,true,NULL,*i);
- }
- break;
- }
- }
-
- break;
- }
-
- m_caster->CastSpell(unitTarget,spellId2,true);
- return;
- }
- }
- }
-
- // normal DB scripted effect
- if(!unitTarget)
- return;
-
- sLog.outDebug("Spell ScriptStart spellid %u in EffectScriptEffect ", m_spellInfo->Id);
- sWorld.ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget);
-}
-
-void Spell::EffectSanctuary(uint32 /*i*/)
-{
- if(!unitTarget)
- return;
- //unitTarget->CombatStop();
-
- unitTarget->CombatStop();
- unitTarget->getHostilRefManager().deleteReferences(); // stop all fighting
- // Vanish allows to remove all threat and cast regular stealth so other spells can be used
- if(m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE_VANISH))
- {
- ((Player *)m_caster)->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
- }
-}
-
-void Spell::EffectAddComboPoints(uint32 /*i*/)
-{
- if(!unitTarget)
- return;
-
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if(damage <= 0)
- return;
-
- ((Player*)m_caster)->AddComboPoints(unitTarget, damage);
-}
-
-void Spell::EffectDuel(uint32 i)
-{
- if(!m_caster || !unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player *caster = (Player*)m_caster;
- Player *target = (Player*)unitTarget;
-
- // caster or target already have requested duel
- if( caster->duel || target->duel || target->GetSocial()->HasIgnore(caster->GetGUIDLow()) )
- return;
-
- // Players can only fight a duel with each other outside (=not inside dungeons and not in capital cities)
- // Don't have to check the target's map since you cannot challenge someone across maps
- if( caster->GetMapId() != 0 && caster->GetMapId() != 1 && caster->GetMapId() != 530)
- {
- SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
- return;
- }
-
- AreaTableEntry const* casterAreaEntry = GetAreaEntryByAreaID(caster->GetZoneId());
- if(casterAreaEntry && (casterAreaEntry->flags & AREA_FLAG_CAPITAL) )
- {
- SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
- return;
- }
-
- AreaTableEntry const* targetAreaEntry = GetAreaEntryByAreaID(target->GetZoneId());
- if(targetAreaEntry && (targetAreaEntry->flags & AREA_FLAG_CAPITAL) )
- {
- SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
- return;
- }
-
- //CREATE DUEL FLAG OBJECT
- GameObject* pGameObj = new GameObject;
-
- uint32 gameobject_id = m_spellInfo->EffectMiscValue[i];
-
- Map *map = m_caster->GetMap();
- if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map,
- m_caster->GetPositionX()+(unitTarget->GetPositionX()-m_caster->GetPositionX())/2 ,
- m_caster->GetPositionY()+(unitTarget->GetPositionY()-m_caster->GetPositionY())/2 ,
- m_caster->GetPositionZ(),
- m_caster->GetOrientation(), 0, 0, 0, 0, 0, 1))
- {
- delete pGameObj;
- return;
- }
-
- pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, m_caster->getFaction() );
- pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()+1 );
- int32 duration = GetSpellDuration(m_spellInfo);
- pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
- pGameObj->SetSpellId(m_spellInfo->Id);
-
- m_caster->AddGameObject(pGameObj);
- map->Add(pGameObj);
- //END
-
- // Send request
- WorldPacket data(SMSG_DUEL_REQUESTED, 16);
- data << pGameObj->GetGUID();
- data << caster->GetGUID();
- caster->GetSession()->SendPacket(&data);
- target->GetSession()->SendPacket(&data);
-
- // create duel-info
- DuelInfo *duel = new DuelInfo;
- duel->initiator = caster;
- duel->opponent = target;
- duel->startTime = 0;
- duel->startTimer = 0;
- caster->duel = duel;
-
- DuelInfo *duel2 = new DuelInfo;
- duel2->initiator = caster;
- duel2->opponent = caster;
- duel2->startTime = 0;
- duel2->startTimer = 0;
- target->duel = duel2;
-
- caster->SetUInt64Value(PLAYER_DUEL_ARBITER,pGameObj->GetGUID());
- target->SetUInt64Value(PLAYER_DUEL_ARBITER,pGameObj->GetGUID());
-}
-
-void Spell::EffectStuck(uint32 /*i*/)
-{
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if(!sWorld.getConfig(CONFIG_CAST_UNSTUCK))
- return;
-
- Player* pTarget = (Player*)unitTarget;
-
- sLog.outDebug("Spell Effect: Stuck");
- sLog.outDetail("Player %s (guid %u) used auto-unstuck future at map %u (%f, %f, %f)", pTarget->GetName(), pTarget->GetGUIDLow(), m_caster->GetMapId(), m_caster->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ());
-
- if(pTarget->isInFlight())
- return;
-
- // homebind location is loaded always
- pTarget->TeleportTo(pTarget->m_homebindMapId,pTarget->m_homebindX,pTarget->m_homebindY,pTarget->m_homebindZ,pTarget->GetOrientation(), (unitTarget==m_caster ? TELE_TO_SPELL : 0));
-
- // Stuck spell trigger Hearthstone cooldown
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(8690);
- if(!spellInfo)
- return;
- Spell spell(pTarget,spellInfo,true,0);
- spell.SendSpellCooldown();
-}
-
-void Spell::EffectSummonPlayer(uint32 /*i*/)
-{
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- // Evil Twin (ignore player summon, but hide this for summoner)
- if(unitTarget->GetDummyAura(23445))
- return;
-
- float x,y,z;
- m_caster->GetClosePoint(x,y,z,unitTarget->GetObjectSize());
-
- ((Player*)unitTarget)->SetSummonPoint(m_caster->GetMapId(),x,y,z);
-
- WorldPacket data(SMSG_SUMMON_REQUEST, 8+4+4);
- data << uint64(m_caster->GetGUID()); // summoner guid
- data << uint32(m_caster->GetZoneId()); // summoner zone
- data << uint32(MAX_PLAYER_SUMMON_DELAY*1000); // auto decline after msecs
- ((Player*)unitTarget)->GetSession()->SendPacket(&data);
-}
-
-static ScriptInfo generateActivateCommand()
-{
- ScriptInfo si;
- si.command = SCRIPT_COMMAND_ACTIVATE_OBJECT;
- return si;
-}
-
-void Spell::EffectActivateObject(uint32 effect_idx)
-{
- if(!gameObjTarget)
- return;
-
- static ScriptInfo activateCommand = generateActivateCommand();
-
- int32 delay_secs = m_spellInfo->EffectMiscValue[effect_idx];
-
- sWorld.ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget);
-}
-
-void Spell::EffectSummonTotem(uint32 i)
-{
- uint8 slot = 0;
- switch(m_spellInfo->EffectMiscValueB[i])
- {
- case SUMMON_TYPE_TOTEM_SLOT1: slot = 0; break;
- case SUMMON_TYPE_TOTEM_SLOT2: slot = 1; break;
- case SUMMON_TYPE_TOTEM_SLOT3: slot = 2; break;
- case SUMMON_TYPE_TOTEM_SLOT4: slot = 3; break;
- // Battle standard case
- case SUMMON_TYPE_TOTEM: slot = 254; break;
- // jewelery statue case, like totem without slot
- case SUMMON_TYPE_GUARDIAN: slot = 255; break;
- default: return;
- }
-
- if(slot < MAX_TOTEM)
- {
- uint64 guid = m_caster->m_TotemSlot[slot];
- if(guid != 0)
- {
- Creature *OldTotem = ObjectAccessor::GetCreature(*m_caster, guid);
- if(OldTotem && OldTotem->isTotem())
- ((Totem*)OldTotem)->UnSummon();
- }
- }
-
- uint32 team = 0;
- if (m_caster->GetTypeId()==TYPEID_PLAYER)
- team = ((Player*)m_caster)->GetTeam();
-
- Totem* pTotem = new Totem;
-
- if(!pTotem->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), m_caster->GetMap(), m_spellInfo->EffectMiscValue[i], team ))
- {
- delete pTotem;
- return;
- }
-
- float angle = slot < MAX_TOTEM ? M_PI/MAX_TOTEM - (slot*2*M_PI/MAX_TOTEM) : 0;
-
- float x,y,z;
- m_caster->GetClosePoint(x,y,z,pTotem->GetObjectSize(),2.0f,angle);
-
- // totem must be at same Z in case swimming caster and etc.
- if( fabs( z - m_caster->GetPositionZ() ) > 5 )
- z = m_caster->GetPositionZ();
-
- pTotem->Relocate(x, y, z, m_caster->GetOrientation());
-
- if(slot < MAX_TOTEM)
- m_caster->m_TotemSlot[slot] = pTotem->GetGUID();
-
- pTotem->SetOwner(m_caster->GetGUID());
- pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initilized
-
- int32 duration=GetSpellDuration(m_spellInfo);
- if(Player* modOwner = m_caster->GetSpellModOwner())
- modOwner->ApplySpellMod(m_spellInfo->Id,SPELLMOD_DURATION, duration);
- pTotem->SetDuration(duration);
-
- if (damage) // if not spell info, DB values used
- {
- pTotem->SetMaxHealth(damage);
- pTotem->SetHealth(damage);
- }
-
- pTotem->SetUInt32Value(UNIT_CREATED_BY_SPELL,m_spellInfo->Id);
- pTotem->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE);
-
- pTotem->ApplySpellImmune(m_spellInfo->Id,IMMUNITY_STATE,SPELL_AURA_MOD_FEAR,true);
- pTotem->ApplySpellImmune(m_spellInfo->Id,IMMUNITY_STATE,SPELL_AURA_TRANSFORM,true);
-
- pTotem->Summon(m_caster);
-
- if(slot < MAX_TOTEM && m_caster->GetTypeId() == TYPEID_PLAYER)
- {
- WorldPacket data(SMSG_TOTEM_CREATED, 1+8+4+4);
- data << uint8(slot);
- data << uint64(pTotem->GetGUID());
- data << uint32(duration);
- data << uint32(m_spellInfo->Id);
- ((Player*)m_caster)->SendDirectMessage(&data);
- }
-}
-
-void Spell::EffectEnchantHeldItem(uint32 i)
-{
- // this is only item spell effect applied to main-hand weapon of target player (players in area)
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player* item_owner = (Player*)unitTarget;
- Item* item = item_owner->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
-
- if(!item )
- return;
-
- // must be equipped
- if(!item ->IsEquipped())
- return;
-
- if (m_spellInfo->EffectMiscValue[i])
- {
- uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
- int32 duration = GetSpellDuration(m_spellInfo); //Try duration index first ..
- if(!duration)
- duration = m_currentBasePoints[i]+1; //Base points after ..
- if(!duration)
- duration = 10; //10 seconds for enchants which don't have listed duration
-
- SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
- if(!pEnchant)
- return;
-
- // Always go to temp enchantment slot
- EnchantmentSlot slot = TEMP_ENCHANTMENT_SLOT;
-
- // Enchantment will not be applied if a different one already exists
- if(item->GetEnchantmentId(slot) && item->GetEnchantmentId(slot) != enchant_id)
- return;
-
- // Apply the temporary enchantment
- item->SetEnchantment(slot, enchant_id, duration*1000, 0);
- item_owner->ApplyEnchantment(item,slot,true);
- }
-}
-
-void Spell::EffectDisEnchant(uint32 /*i*/)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player* p_caster = (Player*)m_caster;
- if(!itemTarget || !itemTarget->GetProto()->DisenchantID)
- return;
-
- p_caster->UpdateCraftSkill(m_spellInfo->Id);
-
- ((Player*)m_caster)->SendLoot(itemTarget->GetGUID(),LOOT_DISENCHANTING);
-
- // item will be removed at disenchanting end
-}
-
-void Spell::EffectInebriate(uint32 /*i*/)
-{
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player *player = (Player*)unitTarget;
- uint16 currentDrunk = player->GetDrunkValue();
- uint16 drunkMod = damage * 256;
- if (currentDrunk + drunkMod > 0xFFFF)
- currentDrunk = 0xFFFF;
- else
- currentDrunk += drunkMod;
- player->SetDrunkValue(currentDrunk, m_CastItem?m_CastItem->GetEntry():0);
-}
-
-void Spell::EffectFeedPet(uint32 i)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player *_player = (Player*)m_caster;
-
- if(!itemTarget)
- return;
-
- Pet *pet = _player->GetPet();
- if(!pet)
- return;
-
- if(!pet->isAlive())
- return;
-
- int32 benefit = pet->GetCurrentFoodBenefitLevel(itemTarget->GetProto()->ItemLevel);
- if(benefit <= 0)
- return;
-
- uint32 count = 1;
- _player->DestroyItemCount(itemTarget,count,true);
- // TODO: fix crash when a spell has two effects, both pointed at the same item target
-
- m_caster->CastCustomSpell(m_caster,m_spellInfo->EffectTriggerSpell[i],&benefit,NULL,NULL,true);
-}
-
-void Spell::EffectDismissPet(uint32 /*i*/)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Pet* pet = m_caster->GetPet();
-
- // not let dismiss dead pet
- if(!pet||!pet->isAlive())
- return;
-
- ((Player*)m_caster)->RemovePet(pet,PET_SAVE_NOT_IN_SLOT);
-}
-
-void Spell::EffectSummonObject(uint32 i)
-{
- uint32 go_id = m_spellInfo->EffectMiscValue[i];
-
- uint8 slot = 0;
- switch(m_spellInfo->Effect[i])
- {
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: slot = 0; break;
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: slot = 1; break;
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: slot = 2; break;
- case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: slot = 3; break;
- default: return;
- }
-
- uint64 guid = m_caster->m_ObjectSlot[slot];
- if(guid != 0)
- {
- GameObject* obj = NULL;
- if( m_caster )
- obj = ObjectAccessor::GetGameObject(*m_caster, guid);
-
- if(obj) obj->Delete();
- m_caster->m_ObjectSlot[slot] = 0;
- }
-
- GameObject* pGameObj = new GameObject;
-
- float rot2 = sin(m_caster->GetOrientation()/2);
- float rot3 = cos(m_caster->GetOrientation()/2);
-
- float x,y,z;
- // If dest location if present
- if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
- {
- x = m_targets.m_destX;
- y = m_targets.m_destY;
- z = m_targets.m_destZ;
- }
- // Summon in random point all other units if location present
- else
- m_caster->GetClosePoint(x,y,z,DEFAULT_WORLD_OBJECT_SIZE);
-
- Map *map = m_caster->GetMap();
- if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), go_id, map, x, y, z, m_caster->GetOrientation(), 0, 0, rot2, rot3, 0, 1))
- {
- delete pGameObj;
- return;
- }
-
- pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL,m_caster->getLevel());
- int32 duration = GetSpellDuration(m_spellInfo);
- pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
- pGameObj->SetSpellId(m_spellInfo->Id);
- m_caster->AddGameObject(pGameObj);
-
- map->Add(pGameObj);
- WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
- data << pGameObj->GetGUID();
- m_caster->SendMessageToSet(&data,true);
-
- m_caster->m_ObjectSlot[slot] = pGameObj->GetGUID();
-}
-
-void Spell::EffectResurrect(uint32 i)
-{
- if(!unitTarget)
- return;
- if(unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- if(unitTarget->isAlive())
- return;
- if(!unitTarget->IsInWorld())
- return;
-
- switch (m_spellInfo->Id)
- {
- // Defibrillate (Goblin Jumper Cables) have 33% chance on success
- case 8342:
- if (roll_chance_i(67))
- {
- m_caster->CastSpell(m_caster, 8338, true, m_CastItem);
- return;
- }
- break;
- // Defibrillate (Goblin Jumper Cables XL) have 50% chance on success
- case 22999:
- if (roll_chance_i(50))
- {
- m_caster->CastSpell(m_caster, 23055, true, m_CastItem);
- return;
- }
- break;
- default:
- break;
- }
-
- Player* pTarget = ((Player*)unitTarget);
-
- if(pTarget->isRessurectRequested()) // already have one active request
- return;
-
- uint32 health = pTarget->GetMaxHealth() * damage / 100;
- uint32 mana = pTarget->GetMaxPower(POWER_MANA) * damage / 100;
-
- pTarget->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana);
- SendResurrectRequest(pTarget);
-}
-
-void Spell::EffectAddExtraAttacks(uint32 /*i*/)
-{
- if(!unitTarget || !unitTarget->isAlive())
- return;
-
- if( unitTarget->m_extraAttacks )
- return;
-
- unitTarget->m_extraAttacks = damage;
-}
-
-void Spell::EffectParry(uint32 /*i*/)
-{
- if (unitTarget->GetTypeId() == TYPEID_PLAYER)
- {
- ((Player*)unitTarget)->SetCanParry(true);
- }
-}
-
-void Spell::EffectBlock(uint32 /*i*/)
-{
- if (unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- ((Player*)unitTarget)->SetCanBlock(true);
-}
-
-void Spell::EffectMomentMove(uint32 i)
-{
- if(unitTarget->isInFlight())
- return;
-
- if( m_spellInfo->rangeIndex== 1) //self range
- {
- uint32 mapid = m_caster->GetMapId();
- float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
-
- // before caster
- float fx,fy,fz;
- unitTarget->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis);
- float ox,oy,oz;
- unitTarget->GetPosition(ox,oy,oz);
-
- float fx2,fy2,fz2; // getObjectHitPos overwrite last args in any result case
- if(VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(mapid, ox,oy,oz+0.5, fx,fy,oz+0.5,fx2,fy2,fz2, -0.5))
- {
- fx = fx2;
- fy = fy2;
- fz = fz2;
- unitTarget->UpdateGroundPositionZ(fx,fy,fz);
- }
-
- if(unitTarget->GetTypeId() == TYPEID_PLAYER)
- ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, unitTarget->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
- else
- MapManager::Instance().GetMap(mapid, unitTarget)->CreatureRelocation((Creature*)unitTarget, fx, fy, fz, unitTarget->GetOrientation());
- }
-}
-
-void Spell::EffectReputation(uint32 i)
-{
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player *_player = (Player*)unitTarget;
-
- int32 rep_change = m_currentBasePoints[i]+1; // field store reputation change -1
-
- uint32 faction_id = m_spellInfo->EffectMiscValue[i];
-
- FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
-
- if(!factionEntry)
- return;
-
- _player->ModifyFactionReputation(factionEntry,rep_change);
-}
-
-void Spell::EffectQuestComplete(uint32 i)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player *_player = (Player*)m_caster;
-
- uint32 quest_id = m_spellInfo->EffectMiscValue[i];
- _player->AreaExploredOrEventHappens(quest_id);
-}
-
-void Spell::EffectSelfResurrect(uint32 i)
-{
- if(!unitTarget || unitTarget->isAlive())
- return;
- if(unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
- if(!unitTarget->IsInWorld())
- return;
-
- uint32 health = 0;
- uint32 mana = 0;
-
- // flat case
- if(damage < 0)
- {
- health = uint32(-damage);
- mana = m_spellInfo->EffectMiscValue[i];
- }
- // percent case
- else
- {
- health = uint32(damage/100.0f*unitTarget->GetMaxHealth());
- if(unitTarget->GetMaxPower(POWER_MANA) > 0)
- mana = uint32(damage/100.0f*unitTarget->GetMaxPower(POWER_MANA));
- }
-
- Player *plr = ((Player*)unitTarget);
- plr->ResurrectPlayer(0.0f);
-
- plr->SetHealth( health );
- plr->SetPower(POWER_MANA, mana );
- plr->SetPower(POWER_RAGE, 0 );
- plr->SetPower(POWER_ENERGY, plr->GetMaxPower(POWER_ENERGY) );
-
- plr->SpawnCorpseBones();
-
- plr->SaveToDB();
-}
-
-void Spell::EffectSkinning(uint32 /*i*/)
-{
- if(unitTarget->GetTypeId() != TYPEID_UNIT )
- return;
- if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Creature* creature = (Creature*) unitTarget;
- int32 targetLevel = creature->getLevel();
-
- uint32 skill;
- if(creature->GetCreatureInfo()->flag1 & 256)
- skill = SKILL_HERBALISM; // special case
- else if(creature->GetCreatureInfo()->flag1 & 512)
- skill = SKILL_MINING; // special case
- else
- skill = SKILL_SKINNING; // normal case
-
- ((Player*)m_caster)->SendLoot(creature->GetGUID(),LOOT_SKINNING);
- creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
-
- int32 reqValue = targetLevel < 10 ? 0 : targetLevel < 20 ? (targetLevel-10)*10 : targetLevel*5;
-
- int32 skillValue = ((Player*)m_caster)->GetPureSkillValue(skill);
-
- // Double chances for elites
- ((Player*)m_caster)->UpdateGatherSkill(skill, skillValue, reqValue, creature->isElite() ? 2 : 1 );
-}
-
-void Spell::EffectCharge(uint32 /*i*/)
-{
- if(!unitTarget || !m_caster)
- return;
-
- float x, y, z;
- unitTarget->GetContactPoint(m_caster, x, y, z);
- if(unitTarget->GetTypeId() != TYPEID_PLAYER)
- ((Creature *)unitTarget)->StopMoving();
-
- // Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags
- m_caster->SendMonsterMove(x, y, z, 0, MOVEMENTFLAG_WALK_MODE, 1);
-
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)->CreatureRelocation((Creature*)m_caster,x,y,z,m_caster->GetOrientation());
-
- // not all charge effects used in negative spells
- if ( !IsPositiveSpell(m_spellInfo->Id))
- m_caster->Attack(unitTarget,true);
-}
-
-void Spell::EffectSummonCritter(uint32 i)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
- Player* player = (Player*)m_caster;
-
- uint32 pet_entry = m_spellInfo->EffectMiscValue[i];
- if(!pet_entry)
- return;
-
- Pet* old_critter = player->GetMiniPet();
-
- // for same pet just despawn
- if(old_critter && old_critter->GetEntry() == pet_entry)
- {
- player->RemoveMiniPet();
- return;
- }
-
- // despawn old pet before summon new
- if(old_critter)
- player->RemoveMiniPet();
-
- // summon new pet
- Pet* critter = new Pet(MINI_PET);
-
- Map *map = m_caster->GetMap();
- uint32 pet_number = objmgr.GeneratePetNumber();
- if(!critter->Create(objmgr.GenerateLowGuid(HIGHGUID_PET),
- map, pet_entry, pet_number))
- {
- sLog.outError("Spell::EffectSummonCritter, spellid %u: no such creature entry %u", m_spellInfo->Id, pet_entry);
- delete critter;
- return;
- }
-
- float x,y,z;
- // If dest location if present
- if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
- {
- x = m_targets.m_destX;
- y = m_targets.m_destY;
- z = m_targets.m_destZ;
- }
- // Summon if dest location not present near caster
- else
- m_caster->GetClosePoint(x,y,z,critter->GetObjectSize());
-
- critter->Relocate(x,y,z,m_caster->GetOrientation());
-
- if(!critter->IsPositionValid())
- {
- sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", critter->GetGUIDLow(), critter->GetEntry(), critter->GetPositionX(), critter->GetPositionY());
- delete critter;
- return;
- }
-
- critter->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID());
- critter->SetUInt64Value(UNIT_FIELD_CREATEDBY,m_caster->GetGUID());
- critter->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
- critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
-
- critter->AIM_Initialize();
- critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter...
- critter->SetMaxHealth(1);
- critter->SetHealth(1);
- critter->SetLevel(1);
-
- // set timer for unsummon
- int32 duration = GetSpellDuration(m_spellInfo);
- if(duration > 0)
- critter->SetDuration(duration);
-
- std::string name = player->GetName();
- name.append(petTypeSuffix[critter->getPetType()]);
- critter->SetName( name );
- player->SetMiniPet(critter);
-
- map->Add((Creature*)critter);
-}
-
-void Spell::EffectKnockBack(uint32 i)
-{
- if(!unitTarget || !m_caster)
- return;
-
- // Effect only works on players
- if(unitTarget->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- float vsin = sin(m_caster->GetAngle(unitTarget));
- float vcos = cos(m_caster->GetAngle(unitTarget));
-
- WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4));
- data.append(unitTarget->GetPackGUID());
- data << uint32(0); // Sequence
- data << float(vcos); // x direction
- data << float(vsin); // y direction
- data << float(m_spellInfo->EffectMiscValue[i])/10; // Horizontal speed
- data << float(damage/-10); // Z Movement speed (vertical)
-
- ((Player*)unitTarget)->GetSession()->SendPacket(&data);
-}
-
-void Spell::EffectSendTaxi(uint32 i)
-{
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(m_spellInfo->EffectMiscValue[i]);
- if(!entry)
- return;
-
- std::vector<uint32> nodes;
-
- nodes.resize(2);
- nodes[0] = entry->from;
- nodes[1] = entry->to;
-
- uint32 mountid = 0;
- switch(m_spellInfo->Id)
- {
- case 31606: //Stormcrow Amulet
- mountid = 17447;
- break;
- case 45071: //Quest - Sunwell Daily - Dead Scar Bombing Run
- case 45113: //Quest - Sunwell Daily - Ship Bombing Run
- case 45353: //Quest - Sunwell Daily - Ship Bombing Run Return
- mountid = 22840;
- break;
- case 34905: //Stealth Flight
- mountid = 6851;
- break;
- }
-
- ((Player*)unitTarget)->ActivateTaxiPathTo(nodes,mountid);
-
-}
-
-void Spell::EffectPlayerPull(uint32 i)
-{
- if(!unitTarget || !m_caster)
- return;
-
- // Effect only works on players
- if(unitTarget->GetTypeId()!=TYPEID_PLAYER)
- return;
-
- float vsin = sin(unitTarget->GetAngle(m_caster));
- float vcos = cos(unitTarget->GetAngle(m_caster));
-
- WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4));
- data.append(unitTarget->GetPackGUID());
- data << uint32(0); // Sequence
- data << float(vcos); // x direction
- data << float(vsin); // y direction
- // Horizontal speed
- data << float(damage ? damage : unitTarget->GetDistance2d(m_caster));
- data << float(m_spellInfo->EffectMiscValue[i])/-10; // Z Movement speed
-
- ((Player*)unitTarget)->GetSession()->SendPacket(&data);
-}
-
-void Spell::EffectDispelMechanic(uint32 i)
-{
- if(!unitTarget)
- return;
-
- uint32 mechanic = m_spellInfo->EffectMiscValue[i];
-
- Unit::AuraMap& Auras = unitTarget->GetAuras();
- for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next)
- {
- next = iter;
- ++next;
- SpellEntry const *spell = sSpellStore.LookupEntry(iter->second->GetSpellProto()->Id);
- if(spell->Mechanic == mechanic || spell->EffectMechanic[iter->second->GetEffIndex()] == mechanic)
- {
- unitTarget->RemoveAurasDueToSpell(spell->Id);
- if(Auras.empty())
- break;
- else
- next = Auras.begin();
- }
- }
- return;
-}
-
-void Spell::EffectSummonDeadPet(uint32 /*i*/)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
- Player *_player = (Player*)m_caster;
- Pet *pet = _player->GetPet();
- if(!pet)
- return;
- if(pet->isAlive())
- return;
- if(damage < 0)
- return;
- pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
- pet->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
- pet->setDeathState( ALIVE );
- pet->clearUnitState(UNIT_STAT_ALL_STATE);
- pet->SetHealth( uint32(pet->GetMaxHealth()*(float(damage)/100)));
-
- pet->AIM_Initialize();
-
- _player->PetSpellInitialize();
- pet->SavePetToDB(PET_SAVE_AS_CURRENT);
-}
-
-void Spell::EffectDestroyAllTotems(uint32 /*i*/)
-{
- float mana = 0;
- for(int slot = 0; slot < MAX_TOTEM; ++slot)
- {
- if(!m_caster->m_TotemSlot[slot])
- continue;
-
- Creature* totem = ObjectAccessor::GetCreature(*m_caster,m_caster->m_TotemSlot[slot]);
- if(totem && totem->isTotem())
- {
- uint32 spell_id = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id);
- if(spellInfo)
- mana += spellInfo->manaCost * damage / 100;
- ((Totem*)totem)->UnSummon();
- }
- }
-
- int32 gain = m_caster->ModifyPower(POWER_MANA,int32(mana));
- m_caster->SendEnergizeSpellLog(m_caster, m_spellInfo->Id, gain, POWER_MANA);
-}
-
-void Spell::EffectDurabilityDamage(uint32 i)
-{
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- int32 slot = m_spellInfo->EffectMiscValue[i];
-
- // FIXME: some spells effects have value -1/-2
- // Possibly its mean -1 all player equipped items and -2 all items
- if(slot < 0)
- {
- ((Player*)unitTarget)->DurabilityPointsLossAll(damage,(slot < -1));
- return;
- }
-
- // invalid slot value
- if(slot >= INVENTORY_SLOT_BAG_END)
- return;
-
- if(Item* item = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0,slot))
- ((Player*)unitTarget)->DurabilityPointsLoss(item,damage);
-}
-
-void Spell::EffectDurabilityDamagePCT(uint32 i)
-{
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- int32 slot = m_spellInfo->EffectMiscValue[i];
-
- // FIXME: some spells effects have value -1/-2
- // Possibly its mean -1 all player equipped items and -2 all items
- if(slot < 0)
- {
- ((Player*)unitTarget)->DurabilityLossAll(double(damage)/100.0f,(slot < -1));
- return;
- }
-
- // invalid slot value
- if(slot >= INVENTORY_SLOT_BAG_END)
- return;
-
- if(damage <= 0)
- return;
-
- if(Item* item = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0,slot))
- ((Player*)unitTarget)->DurabilityLoss(item,double(damage)/100.0f);
-}
-
-void Spell::EffectModifyThreatPercent(uint32 /*effIndex*/)
-{
- if(!unitTarget)
- return;
-
- unitTarget->getThreatManager().modifyThreatPercent(m_caster, damage);
-}
-
-void Spell::EffectTransmitted(uint32 effIndex)
-{
- uint32 name_id = m_spellInfo->EffectMiscValue[effIndex];
-
- GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id);
-
- if (!goinfo)
- {
- sLog.outErrorDb("Gameobject (Entry: %u) not exist and not created at spell (ID: %u) cast",name_id, m_spellInfo->Id);
- return;
- }
-
- float fx,fy,fz;
-
- if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
- {
- fx = m_targets.m_destX;
- fy = m_targets.m_destY;
- fz = m_targets.m_destZ;
- }
- //FIXME: this can be better check for most objects but still hack
- else if(m_spellInfo->EffectRadiusIndex[effIndex] && m_spellInfo->speed==0)
- {
- float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[effIndex]));
- m_caster->GetClosePoint(fx,fy,fz,DEFAULT_WORLD_OBJECT_SIZE, dis);
- }
- else
- {
- float min_dis = GetSpellMinRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
- float max_dis = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
- float dis = rand_norm() * (max_dis - min_dis) + min_dis;
-
- m_caster->GetClosePoint(fx,fy,fz,DEFAULT_WORLD_OBJECT_SIZE, dis);
- }
-
- Map *cMap = m_caster->GetMap();
-
- if(goinfo->type==GAMEOBJECT_TYPE_FISHINGNODE)
- {
- if ( !cMap->IsInWater(fx,fy,fz-0.5f)) // Hack to prevent fishing bobber from failing to land on fishing hole
- { // but this is not proper, we really need to ignore not materialized objects
- SendCastResult(SPELL_FAILED_NOT_HERE);
- SendChannelUpdate(0);
- return;
- }
-
- // replace by water level in this case
- fz = cMap->GetWaterLevel(fx,fy);
- }
- // if gameobject is summoning object, it should be spawned right on caster's position
- else if(goinfo->type==GAMEOBJECT_TYPE_SUMMONING_RITUAL)
- {
- m_caster->GetPosition(fx,fy,fz);
- }
-
- GameObject* pGameObj = new GameObject;
-
- if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), name_id, cMap,
- fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1))
- {
- delete pGameObj;
- return;
- }
-
- int32 duration = GetSpellDuration(m_spellInfo);
-
- switch(goinfo->type)
- {
- case GAMEOBJECT_TYPE_FISHINGNODE:
- {
- m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,pGameObj->GetGUID());
- // Orientation3
- pGameObj->SetFloatValue(GAMEOBJECT_ROTATION + 2, 0.88431775569915771 );
- // Orientation4
- pGameObj->SetFloatValue(GAMEOBJECT_ROTATION + 3, -0.4668855369091033 );
- m_caster->AddGameObject(pGameObj); // will removed at spell cancel
-
- // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo))
- // start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME)
- int32 lastSec;
- switch(urand(0, 3))
- {
- case 0: lastSec = 3; break;
- case 1: lastSec = 7; break;
- case 2: lastSec = 13; break;
- case 3: lastSec = 17; break;
- }
-
- duration = duration - lastSec*1000 + FISHING_BOBBER_READY_TIME*1000;
- break;
- }
- case GAMEOBJECT_TYPE_SUMMONING_RITUAL:
- {
- if(m_caster->GetTypeId()==TYPEID_PLAYER)
- {
- pGameObj->AddUniqueUse((Player*)m_caster);
- m_caster->AddGameObject(pGameObj); // will removed at spell cancel
- }
- break;
- }
- case GAMEOBJECT_TYPE_FISHINGHOLE:
- case GAMEOBJECT_TYPE_CHEST:
- default:
- {
- break;
- }
- }
-
- pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
-
- pGameObj->SetOwnerGUID(m_caster->GetGUID() );
-
- pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() );
- pGameObj->SetSpellId(m_spellInfo->Id);
-
- DEBUG_LOG("AddObject at SpellEfects.cpp EffectTransmitted\n");
- //m_caster->AddGameObject(pGameObj);
- //m_ObjToDel.push_back(pGameObj);
-
- cMap->Add(pGameObj);
-
- WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
- data << uint64(pGameObj->GetGUID());
- m_caster->SendMessageToSet(&data,true);
-
- if(uint32 linkedEntry = pGameObj->GetLinkedGameObjectEntry())
- {
- GameObject* linkedGO = new GameObject;
- if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, cMap,
- fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1))
- {
- linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0);
- linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() );
- linkedGO->SetSpellId(m_spellInfo->Id);
- linkedGO->SetOwnerGUID(m_caster->GetGUID() );
-
- MapManager::Instance().GetMap(linkedGO->GetMapId(), linkedGO)->Add(linkedGO);
- }
- else
- {
- delete linkedGO;
- linkedGO = NULL;
- return;
- }
- }
-}
-
-void Spell::EffectProspecting(uint32 /*i*/)
-{
- if(m_caster->GetTypeId() != TYPEID_PLAYER)
- return;
-
- Player* p_caster = (Player*)m_caster;
- if(!itemTarget || !(itemTarget->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP))
- return;
-
- if(itemTarget->GetCount() < 5)
- return;
-
- if( sWorld.getConfig(CONFIG_SKILL_PROSPECTING))
- {
- uint32 SkillValue = p_caster->GetPureSkillValue(SKILL_JEWELCRAFTING);
- uint32 reqSkillValue = itemTarget->GetProto()->RequiredSkillRank;
- p_caster->UpdateGatherSkill(SKILL_JEWELCRAFTING, SkillValue, reqSkillValue);
- }
-
- ((Player*)m_caster)->SendLoot(itemTarget->GetGUID(), LOOT_PROSPECTING);
-}
-
-void Spell::EffectSkill(uint32 /*i*/)
-{
- sLog.outDebug("WORLD: SkillEFFECT");
-}
-
-void Spell::EffectSummonDemon(uint32 i)
-{
- float px = m_targets.m_destX;
- float py = m_targets.m_destY;
- float pz = m_targets.m_destZ;
-
- Creature* Charmed = m_caster->SummonCreature(m_spellInfo->EffectMiscValue[i], px, py, pz, m_caster->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,3600000);
- if (!Charmed)
- return;
-
- // might not always work correctly, maybe the creature that dies from CoD casts the effect on itself and is therefore the caster?
- Charmed->SetLevel(m_caster->getLevel());
-
- // TODO: Add damage/mana/hp according to level
-
- if (m_spellInfo->EffectMiscValue[i] == 89) // Inferno summon
- {
- // Enslave demon effect, without mana cost and cooldown
- m_caster->CastSpell(Charmed, 20882, true); // FIXME: enslave does not scale with level, level 62+ minions cannot be enslaved
-
- // Inferno effect
- Charmed->CastSpell(Charmed, 22703, true, 0);
- }
-}
-
-/* There is currently no need for this effect. We handle it in BattleGround.cpp
- If we would handle the resurrection here, the spiritguide would instantly disappear as the
- player revives, and so we wouldn't see the spirit heal visual effect on the npc.
- This is why we use a half sec delay between the visual effect and the resurrection itself */
-void Spell::EffectSpiritHeal(uint32 /*i*/)
-{
- /*
- if(!unitTarget || unitTarget->isAlive())
- return;
- if(unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
- if(!unitTarget->IsInWorld())
- return;
-
- //m_spellInfo->EffectBasePoints[i]; == 99 (percent?)
- //((Player*)unitTarget)->setResurrect(m_caster->GetGUID(), unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), unitTarget->GetMaxHealth(), unitTarget->GetMaxPower(POWER_MANA));
- ((Player*)unitTarget)->ResurrectPlayer(1.0f);
- ((Player*)unitTarget)->SpawnCorpseBones();
- */
-}
-
-// remove insignia spell effect
-void Spell::EffectSkinPlayerCorpse(uint32 /*i*/)
-{
- sLog.outDebug("Effect: SkinPlayerCorpse");
- if ( (m_caster->GetTypeId() != TYPEID_PLAYER) || (unitTarget->GetTypeId() != TYPEID_PLAYER) || (unitTarget->isAlive()) )
- return;
-
- ((Player*)unitTarget)->RemovedInsignia( (Player*)m_caster );
-}
-
-void Spell::EffectStealBeneficialBuff(uint32 i)
-{
- sLog.outDebug("Effect: StealBeneficialBuff");
-
- if(!unitTarget || unitTarget==m_caster) // can't steal from self
- return;
-
- std::vector <Aura *> steal_list;
- // Create dispel mask by dispel type
- uint32 dispelMask = GetDispellMask( DispelType(m_spellInfo->EffectMiscValue[i]) );
- Unit::AuraMap const& auras = unitTarget->GetAuras();
- for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
- {
- Aura *aur = (*itr).second;
- if (aur && (1<<aur->GetSpellProto()->Dispel) & dispelMask)
- {
- // Need check for passive? this
- if (aur->IsPositive() && !aur->IsPassive())
- steal_list.push_back(aur);
- }
- }
- // Ok if exist some buffs for dispel try dispel it
- if (!steal_list.empty())
- {
- std::list < std::pair<uint32,uint64> > success_list;
- int32 list_size = steal_list.size();
- // Dispell N = damage buffs (or while exist buffs for dispel)
- for (int32 count=0; count < damage && list_size > 0; ++count)
- {
- // Random select buff for dispel
- Aura *aur = steal_list[urand(0, list_size-1)];
- // Not use chance for steal
- // TODO possible need do it
- success_list.push_back( std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID()));
-
- // Remove buff from list for prevent doubles
- for (std::vector<Aura *>::iterator j = steal_list.begin(); j != steal_list.end(); )
- {
- Aura *stealed = *j;
- if (stealed->GetId() == aur->GetId() && stealed->GetCasterGUID() == aur->GetCasterGUID())
- {
- j = steal_list.erase(j);
- --list_size;
- }
- else
- ++j;
- }
- }
- // Really try steal and send log
- if (!success_list.empty())
- {
- int32 count = success_list.size();
- WorldPacket data(SMSG_SPELLSTEALLOG, 8+8+4+1+4+count*5);
- data.append(unitTarget->GetPackGUID()); // Victim GUID
- data.append(m_caster->GetPackGUID()); // Caster GUID
- data << uint32(m_spellInfo->Id); // Dispell spell id
- data << uint8(0); // not used
- data << uint32(count); // count
- for (std::list<std::pair<uint32,uint64> >::iterator j = success_list.begin(); j != success_list.end(); ++j)
- {
- SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first);
- data << uint32(spellInfo->Id); // Spell Id
- data << uint8(0); // 0 - steals !=0 transfers
- unitTarget->RemoveAurasDueToSpellBySteal(spellInfo->Id, j->second, m_caster);
- }
- m_caster->SendMessageToSet(&data, true);
- }
- }
-}
-
-void Spell::EffectKillCredit(uint32 i)
-{
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- ((Player*)unitTarget)->KilledMonster(m_spellInfo->EffectMiscValue[i], 0);
-}
-
-void Spell::EffectQuestFail(uint32 i)
-{
- if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
- return;
-
- ((Player*)unitTarget)->FailQuest(m_spellInfo->EffectMiscValue[i]);
-}
+/*
+ * 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 "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 "SkillExtraItems.h"
+#include "Unit.h"
+#include "CreatureAI.h"
+#include "Spell.h"
+#include "DynamicObject.h"
+#include "SpellAuras.h"
+#include "Group.h"
+#include "UpdateData.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "SharedDefines.h"
+#include "Pet.h"
+#include "GameObject.h"
+#include "GossipDef.h"
+#include "Creature.h"
+#include "Totem.h"
+#include "CreatureAI.h"
+#include "BattleGround.h"
+#include "BattleGroundEY.h"
+#include "BattleGroundWS.h"
+#include "VMapFactory.h"
+#include "Language.h"
+#include "SocialMgr.h"
+#include "Util.h"
+#include "TemporarySummon.h"
+
+
+pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
+{
+ &Spell::EffectNULL, // 0
+ &Spell::EffectInstaKill, // 1 SPELL_EFFECT_INSTAKILL
+ &Spell::EffectSchoolDMG, // 2 SPELL_EFFECT_SCHOOL_DAMAGE
+ &Spell::EffectDummy, // 3 SPELL_EFFECT_DUMMY
+ &Spell::EffectUnused, // 4 SPELL_EFFECT_PORTAL_TELEPORT unused
+ &Spell::EffectTeleportUnits, // 5 SPELL_EFFECT_TELEPORT_UNITS
+ &Spell::EffectApplyAura, // 6 SPELL_EFFECT_APPLY_AURA
+ &Spell::EffectEnvirinmentalDMG, // 7 SPELL_EFFECT_ENVIRONMENTAL_DAMAGE
+ &Spell::EffectPowerDrain, // 8 SPELL_EFFECT_POWER_DRAIN
+ &Spell::EffectHealthLeech, // 9 SPELL_EFFECT_HEALTH_LEECH
+ &Spell::EffectHeal, // 10 SPELL_EFFECT_HEAL
+ &Spell::EffectUnused, // 11 SPELL_EFFECT_BIND
+ &Spell::EffectNULL, // 12 SPELL_EFFECT_PORTAL
+ &Spell::EffectUnused, // 13 SPELL_EFFECT_RITUAL_BASE unused
+ &Spell::EffectUnused, // 14 SPELL_EFFECT_RITUAL_SPECIALIZE unused
+ &Spell::EffectUnused, // 15 SPELL_EFFECT_RITUAL_ACTIVATE_PORTAL unused
+ &Spell::EffectQuestComplete, // 16 SPELL_EFFECT_QUEST_COMPLETE
+ &Spell::EffectWeaponDmg, // 17 SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL
+ &Spell::EffectResurrect, // 18 SPELL_EFFECT_RESURRECT
+ &Spell::EffectAddExtraAttacks, // 19 SPELL_EFFECT_ADD_EXTRA_ATTACKS
+ &Spell::EffectUnused, // 20 SPELL_EFFECT_DODGE one spell: Dodge
+ &Spell::EffectUnused, // 21 SPELL_EFFECT_EVADE one spell: Evade (DND)
+ &Spell::EffectParry, // 22 SPELL_EFFECT_PARRY
+ &Spell::EffectBlock, // 23 SPELL_EFFECT_BLOCK one spell: Block
+ &Spell::EffectCreateItem, // 24 SPELL_EFFECT_CREATE_ITEM
+ &Spell::EffectUnused, // 25 SPELL_EFFECT_WEAPON
+ &Spell::EffectUnused, // 26 SPELL_EFFECT_DEFENSE one spell: Defense
+ &Spell::EffectPersistentAA, // 27 SPELL_EFFECT_PERSISTENT_AREA_AURA
+ &Spell::EffectSummonType, // 28 SPELL_EFFECT_SUMMON
+ &Spell::EffectMomentMove, // 29 SPELL_EFFECT_LEAP
+ &Spell::EffectEnergize, // 30 SPELL_EFFECT_ENERGIZE
+ &Spell::EffectWeaponDmg, // 31 SPELL_EFFECT_WEAPON_PERCENT_DAMAGE
+ &Spell::EffectTriggerMissileSpell, // 32 SPELL_EFFECT_TRIGGER_MISSILE
+ &Spell::EffectOpenLock, // 33 SPELL_EFFECT_OPEN_LOCK
+ &Spell::EffectSummonChangeItem, // 34 SPELL_EFFECT_SUMMON_CHANGE_ITEM
+ &Spell::EffectApplyAreaAura, // 35 SPELL_EFFECT_APPLY_AREA_AURA_PARTY
+ &Spell::EffectLearnSpell, // 36 SPELL_EFFECT_LEARN_SPELL
+ &Spell::EffectUnused, // 37 SPELL_EFFECT_SPELL_DEFENSE one spell: SPELLDEFENSE (DND)
+ &Spell::EffectDispel, // 38 SPELL_EFFECT_DISPEL
+ &Spell::EffectUnused, // 39 SPELL_EFFECT_LANGUAGE
+ &Spell::EffectDualWield, // 40 SPELL_EFFECT_DUAL_WIELD
+ &Spell::EffectSummonWild, // 41 SPELL_EFFECT_SUMMON_WILD
+ &Spell::EffectSummonGuardian, // 42 SPELL_EFFECT_SUMMON_GUARDIAN
+ &Spell::EffectTeleUnitsFaceCaster, // 43 SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER
+ &Spell::EffectLearnSkill, // 44 SPELL_EFFECT_SKILL_STEP
+ &Spell::EffectAddHonor, // 45 SPELL_EFFECT_ADD_HONOR honor/pvp related
+ &Spell::EffectNULL, // 46 SPELL_EFFECT_SPAWN we must spawn pet there
+ &Spell::EffectTradeSkill, // 47 SPELL_EFFECT_TRADE_SKILL
+ &Spell::EffectUnused, // 48 SPELL_EFFECT_STEALTH one spell: Base Stealth
+ &Spell::EffectUnused, // 49 SPELL_EFFECT_DETECT one spell: Detect
+ &Spell::EffectTransmitted, // 50 SPELL_EFFECT_TRANS_DOOR
+ &Spell::EffectUnused, // 51 SPELL_EFFECT_FORCE_CRITICAL_HIT unused
+ &Spell::EffectUnused, // 52 SPELL_EFFECT_GUARANTEE_HIT one spell: zzOLDCritical Shot
+ &Spell::EffectEnchantItemPerm, // 53 SPELL_EFFECT_ENCHANT_ITEM
+ &Spell::EffectEnchantItemTmp, // 54 SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY
+ &Spell::EffectTameCreature, // 55 SPELL_EFFECT_TAMECREATURE
+ &Spell::EffectSummonPet, // 56 SPELL_EFFECT_SUMMON_PET
+ &Spell::EffectLearnPetSpell, // 57 SPELL_EFFECT_LEARN_PET_SPELL
+ &Spell::EffectWeaponDmg, // 58 SPELL_EFFECT_WEAPON_DAMAGE
+ &Spell::EffectOpenSecretSafe, // 59 SPELL_EFFECT_OPEN_LOCK_ITEM
+ &Spell::EffectProficiency, // 60 SPELL_EFFECT_PROFICIENCY
+ &Spell::EffectSendEvent, // 61 SPELL_EFFECT_SEND_EVENT
+ &Spell::EffectPowerBurn, // 62 SPELL_EFFECT_POWER_BURN
+ &Spell::EffectThreat, // 63 SPELL_EFFECT_THREAT
+ &Spell::EffectTriggerSpell, // 64 SPELL_EFFECT_TRIGGER_SPELL
+ &Spell::EffectUnused, // 65 SPELL_EFFECT_HEALTH_FUNNEL unused
+ &Spell::EffectUnused, // 66 SPELL_EFFECT_POWER_FUNNEL unused
+ &Spell::EffectHealMaxHealth, // 67 SPELL_EFFECT_HEAL_MAX_HEALTH
+ &Spell::EffectInterruptCast, // 68 SPELL_EFFECT_INTERRUPT_CAST
+ &Spell::EffectDistract, // 69 SPELL_EFFECT_DISTRACT
+ &Spell::EffectPull, // 70 SPELL_EFFECT_PULL one spell: Distract Move
+ &Spell::EffectPickPocket, // 71 SPELL_EFFECT_PICKPOCKET
+ &Spell::EffectAddFarsight, // 72 SPELL_EFFECT_ADD_FARSIGHT
+ &Spell::EffectSummonGuardian, // 73 SPELL_EFFECT_SUMMON_POSSESSED
+ &Spell::EffectSummonTotem, // 74 SPELL_EFFECT_SUMMON_TOTEM
+ &Spell::EffectHealMechanical, // 75 SPELL_EFFECT_HEAL_MECHANICAL one spell: Mechanical Patch Kit
+ &Spell::EffectSummonObjectWild, // 76 SPELL_EFFECT_SUMMON_OBJECT_WILD
+ &Spell::EffectScriptEffect, // 77 SPELL_EFFECT_SCRIPT_EFFECT
+ &Spell::EffectUnused, // 78 SPELL_EFFECT_ATTACK
+ &Spell::EffectSanctuary, // 79 SPELL_EFFECT_SANCTUARY
+ &Spell::EffectAddComboPoints, // 80 SPELL_EFFECT_ADD_COMBO_POINTS
+ &Spell::EffectUnused, // 81 SPELL_EFFECT_CREATE_HOUSE one spell: Create House (TEST)
+ &Spell::EffectNULL, // 82 SPELL_EFFECT_BIND_SIGHT
+ &Spell::EffectDuel, // 83 SPELL_EFFECT_DUEL
+ &Spell::EffectStuck, // 84 SPELL_EFFECT_STUCK
+ &Spell::EffectSummonPlayer, // 85 SPELL_EFFECT_SUMMON_PLAYER
+ &Spell::EffectActivateObject, // 86 SPELL_EFFECT_ACTIVATE_OBJECT
+ &Spell::EffectSummonTotem, // 87 SPELL_EFFECT_SUMMON_TOTEM_SLOT1
+ &Spell::EffectSummonTotem, // 88 SPELL_EFFECT_SUMMON_TOTEM_SLOT2
+ &Spell::EffectSummonTotem, // 89 SPELL_EFFECT_SUMMON_TOTEM_SLOT3
+ &Spell::EffectSummonTotem, // 90 SPELL_EFFECT_SUMMON_TOTEM_SLOT4
+ &Spell::EffectUnused, // 91 SPELL_EFFECT_THREAT_ALL one spell: zzOLDBrainwash
+ &Spell::EffectEnchantHeldItem, // 92 SPELL_EFFECT_ENCHANT_HELD_ITEM
+ &Spell::EffectUnused, // 93 SPELL_EFFECT_SUMMON_PHANTASM
+ &Spell::EffectSelfResurrect, // 94 SPELL_EFFECT_SELF_RESURRECT
+ &Spell::EffectSkinning, // 95 SPELL_EFFECT_SKINNING
+ &Spell::EffectCharge, // 96 SPELL_EFFECT_CHARGE
+ &Spell::EffectSummonCritter, // 97 SPELL_EFFECT_SUMMON_CRITTER
+ &Spell::EffectKnockBack, // 98 SPELL_EFFECT_KNOCK_BACK
+ &Spell::EffectDisEnchant, // 99 SPELL_EFFECT_DISENCHANT
+ &Spell::EffectInebriate, //100 SPELL_EFFECT_INEBRIATE
+ &Spell::EffectFeedPet, //101 SPELL_EFFECT_FEED_PET
+ &Spell::EffectDismissPet, //102 SPELL_EFFECT_DISMISS_PET
+ &Spell::EffectReputation, //103 SPELL_EFFECT_REPUTATION
+ &Spell::EffectSummonObject, //104 SPELL_EFFECT_SUMMON_OBJECT_SLOT1
+ &Spell::EffectSummonObject, //105 SPELL_EFFECT_SUMMON_OBJECT_SLOT2
+ &Spell::EffectSummonObject, //106 SPELL_EFFECT_SUMMON_OBJECT_SLOT3
+ &Spell::EffectSummonObject, //107 SPELL_EFFECT_SUMMON_OBJECT_SLOT4
+ &Spell::EffectDispelMechanic, //108 SPELL_EFFECT_DISPEL_MECHANIC
+ &Spell::EffectSummonDeadPet, //109 SPELL_EFFECT_SUMMON_DEAD_PET
+ &Spell::EffectDestroyAllTotems, //110 SPELL_EFFECT_DESTROY_ALL_TOTEMS
+ &Spell::EffectDurabilityDamage, //111 SPELL_EFFECT_DURABILITY_DAMAGE
+ &Spell::EffectSummonDemon, //112 SPELL_EFFECT_SUMMON_DEMON
+ &Spell::EffectResurrectNew, //113 SPELL_EFFECT_RESURRECT_NEW
+ &Spell::EffectTaunt, //114 SPELL_EFFECT_ATTACK_ME
+ &Spell::EffectDurabilityDamagePCT, //115 SPELL_EFFECT_DURABILITY_DAMAGE_PCT
+ &Spell::EffectSkinPlayerCorpse, //116 SPELL_EFFECT_SKIN_PLAYER_CORPSE one spell: Remove Insignia, bg usage, required special corpse flags...
+ &Spell::EffectSpiritHeal, //117 SPELL_EFFECT_SPIRIT_HEAL one spell: Spirit Heal
+ &Spell::EffectSkill, //118 SPELL_EFFECT_SKILL professions and more
+ &Spell::EffectApplyAreaAura, //119 SPELL_EFFECT_APPLY_AREA_AURA_PET
+ &Spell::EffectUnused, //120 SPELL_EFFECT_TELEPORT_GRAVEYARD one spell: Graveyard Teleport Test
+ &Spell::EffectWeaponDmg, //121 SPELL_EFFECT_NORMALIZED_WEAPON_DMG
+ &Spell::EffectUnused, //122 SPELL_EFFECT_122 unused
+ &Spell::EffectSendTaxi, //123 SPELL_EFFECT_SEND_TAXI taxi/flight related (misc value is taxi path id)
+ &Spell::EffectPlayerPull, //124 SPELL_EFFECT_PLAYER_PULL opposite of knockback effect (pulls player twoard caster)
+ &Spell::EffectModifyThreatPercent, //125 SPELL_EFFECT_MODIFY_THREAT_PERCENT
+ &Spell::EffectStealBeneficialBuff, //126 SPELL_EFFECT_STEAL_BENEFICIAL_BUFF spell steal effect?
+ &Spell::EffectProspecting, //127 SPELL_EFFECT_PROSPECTING Prospecting spell
+ &Spell::EffectApplyAreaAura, //128 SPELL_EFFECT_APPLY_AREA_AURA_FRIEND
+ &Spell::EffectApplyAreaAura, //129 SPELL_EFFECT_APPLY_AREA_AURA_ENEMY
+ &Spell::EffectNULL, //130 SPELL_EFFECT_REDIRECT_THREAT
+ &Spell::EffectUnused, //131 SPELL_EFFECT_131 used in some test spells
+ &Spell::EffectNULL, //132 SPELL_EFFECT_PLAY_MUSIC sound id in misc value
+ &Spell::EffectUnlearnSpecialization, //133 SPELL_EFFECT_UNLEARN_SPECIALIZATION unlearn profession specialization
+ &Spell::EffectKillCredit, //134 SPELL_EFFECT_KILL_CREDIT misc value is creature entry
+ &Spell::EffectNULL, //135 SPELL_EFFECT_CALL_PET
+ &Spell::EffectHealPct, //136 SPELL_EFFECT_HEAL_PCT
+ &Spell::EffectEnergisePct, //137 SPELL_EFFECT_ENERGIZE_PCT
+ &Spell::EffectNULL, //138 SPELL_EFFECT_138 Leap
+ &Spell::EffectUnused, //139 SPELL_EFFECT_139 unused
+ &Spell::EffectForceCast, //140 SPELL_EFFECT_FORCE_CAST
+ &Spell::EffectNULL, //141 SPELL_EFFECT_141 damage and reduce speed?
+ &Spell::EffectTriggerSpellWithValue, //142 SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE
+ &Spell::EffectApplyAreaAura, //143 SPELL_EFFECT_APPLY_AREA_AURA_OWNER
+ &Spell::EffectNULL, //144 SPELL_EFFECT_144 Spectral Blast
+ &Spell::EffectNULL, //145 SPELL_EFFECT_145 Black Hole Effect
+ &Spell::EffectUnused, //146 SPELL_EFFECT_146 unused
+ &Spell::EffectQuestFail, //147 SPELL_EFFECT_QUEST_FAIL quest fail
+ &Spell::EffectUnused, //148 SPELL_EFFECT_148 unused
+ &Spell::EffectNULL, //149 SPELL_EFFECT_149 swoop
+ &Spell::EffectUnused, //150 SPELL_EFFECT_150 unused
+ &Spell::EffectTriggerRitualOfSummoning, //151 SPELL_EFFECT_TRIGGER_SPELL_2
+ &Spell::EffectNULL, //152 SPELL_EFFECT_152 summon Refer-a-Friend
+ &Spell::EffectNULL, //153 SPELL_EFFECT_CREATE_PET misc value is creature entry
+};
+
+void Spell::EffectNULL(uint32 /*i*/)
+{
+ sLog.outDebug("WORLD: Spell Effect DUMMY");
+}
+
+void Spell::EffectUnused(uint32 /*i*/)
+{
+ // NOT USED BY ANY SPELL OR USELESS OR IMPLEMENTED IN DIFFERENT WAY IN MANGOS
+}
+
+void Spell::EffectResurrectNew(uint32 i)
+{
+ if(!unitTarget || unitTarget->isAlive())
+ return;
+
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(!unitTarget->IsInWorld())
+ return;
+
+ Player* pTarget = ((Player*)unitTarget);
+
+ if(pTarget->isRessurectRequested()) // already have one active request
+ return;
+
+ uint32 health = damage;
+ uint32 mana = m_spellInfo->EffectMiscValue[i];
+ pTarget->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana);
+ SendResurrectRequest(pTarget);
+}
+
+void Spell::EffectInstaKill(uint32 /*i*/)
+{
+ if( !unitTarget || !unitTarget->isAlive() )
+ return;
+
+ // Demonic Sacrifice
+ if(m_spellInfo->Id==18788 && unitTarget->GetTypeId()==TYPEID_UNIT)
+ {
+ uint32 entry = unitTarget->GetEntry();
+ uint32 spellID;
+ switch(entry)
+ {
+ case 416: spellID=18789; break; //imp
+ case 417: spellID=18792; break; //fellhunter
+ case 1860: spellID=18790; break; //void
+ case 1863: spellID=18791; break; //succubus
+ case 17252: spellID=35701; break; //fellguard
+ default:
+ sLog.outError("EffectInstaKill: Unhandled creature entry (%u) case.",entry);
+ return;
+ }
+
+ m_caster->CastSpell(m_caster,spellID,true);
+ }
+
+ if(m_caster==unitTarget) // prevent interrupt message
+ finish();
+
+ uint32 health = unitTarget->GetHealth();
+ m_caster->DealDamage(unitTarget, health, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+}
+
+void Spell::EffectEnvirinmentalDMG(uint32 i)
+{
+ uint32 absorb = 0;
+ uint32 resist = 0;
+
+ // Note: this hack with damage replace required until GO casting not implemented
+ // environment damage spells already have around enemies targeting but this not help in case not existed GO casting support
+ // currently each enemy selected explicitly and self cast damage, we prevent apply self casted spell bonuses/etc
+ damage = m_spellInfo->EffectBasePoints[i]+m_spellInfo->EffectBaseDice[i];
+
+ m_caster->CalcAbsorbResist(m_caster,GetSpellSchoolMask(m_spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
+
+ m_caster->SendSpellNonMeleeDamageLog(m_caster, m_spellInfo->Id, damage, GetSpellSchoolMask(m_spellInfo), absorb, resist, false, 0, false);
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_caster)->EnvironmentalDamage(m_caster->GetGUID(),DAMAGE_FIRE,damage);
+}
+
+void Spell::EffectSchoolDMG(uint32 effect_idx)
+{
+ if( unitTarget && unitTarget->isAlive())
+ {
+ switch(m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ //Gore
+ if(m_spellInfo->SpellIconID == 2269 )
+ {
+ damage+= rand()%2 ? damage : 0;
+ }
+
+ switch(m_spellInfo->Id) // better way to check unknown
+ {
+ // Meteor like spells (divided damage to targets)
+ case 24340: case 26558: case 28884: // Meteor
+ case 36837: case 38903: case 41276: // Meteor
+ case 26789: // Shard of the Fallen Star
+ case 31436: // Malevolent Cleave
+ case 35181: // Dive Bomb
+ case 40810: case 43267: case 43268: // Saber Lash
+ case 42384: // Brutal Swipe
+ case 45150: // Meteor Slash
+ {
+ uint32 count = 0;
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ if(ihit->effectMask & (1<<effect_idx))
+ ++count;
+
+ damage /= count; // divide to all targets
+ break;
+ }
+ // percent from health with min
+ case 25599: // Thundercrash
+ {
+ damage = unitTarget->GetHealth() / 2;
+ if(damage < 200)
+ damage = 200;
+ break;
+ }
+ }
+ break;
+ }
+
+ case SPELLFAMILY_MAGE:
+ {
+ // Arcane Blast
+ if(m_spellInfo->SpellFamilyFlags & 0x20000000LL)
+ {
+ m_caster->CastSpell(m_caster,36032,true);
+ }
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Bloodthirst
+ if(m_spellInfo->SpellFamilyFlags & 0x40000000000LL)
+ {
+ damage = uint32(damage * (m_caster->GetTotalAttackPowerValue(BASE_ATTACK)) / 100);
+ }
+ // Shield Slam
+ else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL)
+ damage += int32(m_caster->GetShieldBlockValue());
+ // Victory Rush
+ else if(m_spellInfo->SpellFamilyFlags & 0x10000000000LL)
+ {
+ damage = uint32(damage * m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
+ m_caster->ModifyAuraState(AURA_STATE_WARRIOR_VICTORY_RUSH, false);
+ }
+ break;
+ }
+ case SPELLFAMILY_WARLOCK:
+ {
+ // Incinerate Rank 1 & 2
+ if((m_spellInfo->SpellFamilyFlags & 0x00004000000000LL) && m_spellInfo->SpellIconID==2128)
+ {
+ // Incinerate does more dmg (dmg*0.25) if the target is Immolated.
+ if(unitTarget->HasAuraState(AURA_STATE_IMMOLATE))
+ damage += int32(damage*0.25);
+ }
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ // Ferocious Bite
+ if((m_spellInfo->SpellFamilyFlags & 0x000800000) && m_spellInfo->SpellVisual==6587)
+ {
+ // converts each extra point of energy into ($f1+$AP/630) additional damage
+ float multiple = m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 630 + m_spellInfo->DmgMultiplier[effect_idx];
+ damage += int32(m_caster->GetPower(POWER_ENERGY) * multiple);
+ m_caster->SetPower(POWER_ENERGY,0);
+ }
+ // Rake
+ else if(m_spellInfo->SpellFamilyFlags & 0x0000000000001000LL)
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) / 100);
+ }
+ // Swipe
+ else if(m_spellInfo->SpellFamilyFlags & 0x0010000000000000LL)
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.08f);
+ }
+ // Starfire
+ else if ( m_spellInfo->SpellFamilyFlags & 0x0004LL )
+ {
+ Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i)
+ {
+ // Starfire Bonus (caster)
+ switch((*i)->GetModifier()->m_miscvalue)
+ {
+ case 5481: // Nordrassil Regalia - bonus
+ {
+ Unit::AuraList const& m_periodicDamageAuras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
+ for(Unit::AuraList::const_iterator itr = m_periodicDamageAuras.begin(); itr != m_periodicDamageAuras.end(); ++itr)
+ {
+ // Moonfire or Insect Swarm (target debuff from any casters)
+ if ( (*itr)->GetSpellProto()->SpellFamilyFlags & 0x00200002LL )
+ {
+ int32 mod = (*i)->GetModifier()->m_amount;
+ damage += damage*mod/100;
+ break;
+ }
+ }
+ break;
+ }
+ case 5148: //Improved Starfire - Ivory Idol of the Moongoddes Aura
+ {
+ damage += (*i)->GetModifier()->m_amount;
+ break;
+ }
+ }
+ }
+ }
+ //Mangle Bonus for the initial damage of Lacerate and Rake
+ if((m_spellInfo->SpellFamilyFlags==0x0000000000001000LL && m_spellInfo->SpellIconID==494) ||
+ (m_spellInfo->SpellFamilyFlags==0x0000010000000000LL && m_spellInfo->SpellIconID==2246))
+ {
+ Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
+ if((*i)->GetSpellProto()->SpellFamilyFlags & 0x0000044000000000LL && (*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID)
+ {
+ damage = int32(damage*(100.0f+(*i)->GetModifier()->m_amount)/100.0f);
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ // Envenom
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & 0x800000000LL))
+ {
+ // consume from stack dozes not more that have combo-points
+ if(uint32 combo = ((Player*)m_caster)->GetComboPoints())
+ {
+ // count consumed deadly poison doses at target
+ uint32 doses = 0;
+
+ // remove consumed poison doses
+ Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
+ for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end() && combo;)
+ {
+ // Deadly poison (only attacker applied)
+ if( (*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && ((*itr)->GetSpellProto()->SpellFamilyFlags & 0x10000) &&
+ (*itr)->GetSpellProto()->SpellVisual==5100 && (*itr)->GetCasterGUID()==m_caster->GetGUID() )
+ {
+ --combo;
+ ++doses;
+
+ unitTarget->RemoveSingleAuraFromStack((*itr)->GetId(), (*itr)->GetEffIndex());
+
+ itr = auras.begin();
+ }
+ else
+ ++itr;
+ }
+
+ damage *= doses;
+ damage += int32(((Player*)m_caster)->GetTotalAttackPowerValue(BASE_ATTACK) * 0.03f * doses);
+
+ // Eviscerate and Envenom Bonus Damage (item set effect)
+ if(m_caster->GetDummyAura(37169))
+ damage += ((Player*)m_caster)->GetComboPoints()*40;
+ }
+ }
+ // Eviscerate
+ else if((m_spellInfo->SpellFamilyFlags & 0x00020000LL) && m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(uint32 combo = ((Player*)m_caster)->GetComboPoints())
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK) * combo * 0.03f);
+
+ // Eviscerate and Envenom Bonus Damage (item set effect)
+ if(m_caster->GetDummyAura(37169))
+ damage += combo*40;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Mongoose Bite
+ if((m_spellInfo->SpellFamilyFlags & 0x000000002) && m_spellInfo->SpellVisual==342)
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2);
+ }
+ // Arcane Shot
+ else if((m_spellInfo->SpellFamilyFlags & 0x00000800) && m_spellInfo->maxLevel > 0)
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.15);
+ }
+ // Steady Shot
+ else if(m_spellInfo->SpellFamilyFlags & 0x100000000LL)
+ {
+ int32 base = irand((int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE),(int32)m_caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE));
+ damage += int32(float(base)/m_caster->GetAttackTime(RANGED_ATTACK)*2800 + m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.2f);
+ }
+ //Explosive Trap Effect
+ else if(m_spellInfo->SpellFamilyFlags & 0x00000004)
+ {
+ damage += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK)*0.1);
+ }
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ //Judgement of Vengeance
+ if((m_spellInfo->SpellFamilyFlags & 0x800000000LL) && m_spellInfo->SpellIconID==2292)
+ {
+ uint32 stacks = 0;
+ Unit::AuraList const& auras = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
+ for(Unit::AuraList::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
+ if((*itr)->GetId() == 31803 && (*itr)->GetCasterGUID()==m_caster->GetGUID())
+ ++stacks;
+ if(!stacks)
+ //No damage if the target isn't affected by this
+ damage = -1;
+ else
+ damage *= stacks;
+ }
+ break;
+ }
+ }
+
+ if(damage >= 0)
+ {
+ uint32 finalDamage;
+ if(m_originalCaster) // m_caster only passive source of cast
+ finalDamage = m_originalCaster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
+ else
+ finalDamage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
+
+ // post effects
+ switch(m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Bloodthirst
+ if(m_spellInfo->SpellFamilyFlags & 0x40000000000LL)
+ {
+ uint32 BTAura = 0;
+ switch(m_spellInfo->Id)
+ {
+ case 23881: BTAura = 23885; break;
+ case 23892: BTAura = 23886; break;
+ case 23893: BTAura = 23887; break;
+ case 23894: BTAura = 23888; break;
+ case 25251: BTAura = 25252; break;
+ case 30335: BTAura = 30339; break;
+ default:
+ sLog.outError("Spell::EffectSchoolDMG: Spell %u not handled in BTAura",m_spellInfo->Id);
+ break;
+ }
+
+ if (BTAura)
+ m_caster->CastSpell(m_caster,BTAura,true);
+ }
+ break;
+ }
+ case SPELLFAMILY_PRIEST:
+ {
+ // Shadow Word: Death
+ if(finalDamage > 0 && (m_spellInfo->SpellFamilyFlags & 0x0000000200000000LL) && unitTarget->isAlive())
+ // deals damage equal to damage done to caster if victim is not killed
+ m_caster->SpellNonMeleeDamageLog( m_caster, m_spellInfo->Id, finalDamage, m_IsTriggeredSpell, false);
+
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Judgement of Blood
+ if(finalDamage > 0 && (m_spellInfo->SpellFamilyFlags & 0x0000000800000000LL) && m_spellInfo->SpellIconID==153)
+ {
+ int32 damagePoint = finalDamage * 33 / 100;
+ m_caster->CastCustomSpell(m_caster, 32220, &damagePoint, NULL, NULL, true);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+void Spell::EffectDummy(uint32 i)
+{
+ if(!unitTarget && !gameObjTarget && !itemTarget)
+ return;
+
+ // selection by spell family
+ switch(m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ // Gnomish Poultryizer trinket
+ switch(m_spellInfo->Id )
+ {
+ case 8063: // Deviate Fish
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = 0;
+ switch(urand(1,5))
+ {
+ case 1: spell_id = 8064; break; // Sleepy
+ case 2: spell_id = 8065; break; // Invigorate
+ case 3: spell_id = 8066; break; // Shrink
+ case 4: spell_id = 8067; break; // Party Time!
+ case 5: spell_id = 8068; break; // Healthy Spirit
+ }
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 8213: // Savory Deviate Delight
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = 0;
+ switch(urand(1,2))
+ {
+ // Flip Out - ninja
+ case 1: spell_id = (m_caster->getGender() == GENDER_MALE ? 8219 : 8220); break;
+ // Yaaarrrr - pirate
+ case 2: spell_id = (m_caster->getGender() == GENDER_MALE ? 8221 : 8222); break;
+ }
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 8593: // Symbol of life (restore creature to life)
+ case 31225: // Shimmering Vessel (restore creature to life)
+ {
+ if(!unitTarget || unitTarget->GetTypeId()!=TYPEID_UNIT)
+ return;
+ ((Creature*)unitTarget)->setDeathState(JUST_ALIVED);
+ return;
+ }
+ case 12162: // Deep wounds
+ case 12850: // (now good common check for this spells)
+ case 12868:
+ {
+ if(!unitTarget)
+ return;
+
+ float damage;
+ // DW should benefit of attack power, damage percent mods etc.
+ // TODO: check if using offhand damage is correct and if it should be divided by 2
+ if (m_caster->haveOffhandWeapon() && m_caster->getAttackTimer(BASE_ATTACK) > m_caster->getAttackTimer(OFF_ATTACK))
+ damage = (m_caster->GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE))/2;
+ else
+ damage = (m_caster->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXDAMAGE))/2;
+
+ switch (m_spellInfo->Id)
+ {
+ case 12850: damage *= 0.2f; break;
+ case 12162: damage *= 0.4f; break;
+ case 12868: damage *= 0.6f; break;
+ default:
+ sLog.outError("Spell::EffectDummy: Spell %u not handled in DW",m_spellInfo->Id);
+ return;
+ };
+
+ int32 deepWoundsDotBasePoints0 = int32(damage / 4);
+ m_caster->CastCustomSpell(unitTarget, 12721, &deepWoundsDotBasePoints0, NULL, NULL, true, NULL);
+ return;
+ }
+ case 12975: //Last Stand
+ {
+ int32 healthModSpellBasePoints0 = int32(m_caster->GetMaxHealth()*0.3);
+ m_caster->CastCustomSpell(m_caster, 12976, &healthModSpellBasePoints0, NULL, NULL, true, NULL);
+ return;
+ }
+ case 13120: // net-o-matic
+ {
+ if(!unitTarget)
+ return;
+
+ uint32 spell_id = 0;
+
+ uint32 roll = urand(0, 99);
+
+ if(roll < 2) // 2% for 30 sec self root (off-like chance unknown)
+ spell_id = 16566;
+ else if(roll < 4) // 2% for 20 sec root, charge to target (off-like chance unknown)
+ spell_id = 13119;
+ else // normal root
+ spell_id = 13099;
+
+ m_caster->CastSpell(unitTarget,spell_id,true,NULL);
+ return;
+ }
+ case 13567: // Dummy Trigger
+ {
+ // can be used for different aura triggreing, so select by aura
+ if(!m_triggeredByAuraSpell || !unitTarget)
+ return;
+
+ switch(m_triggeredByAuraSpell->Id)
+ {
+ case 26467: // Persistent Shield
+ m_caster->CastCustomSpell(unitTarget, 26470, &damage, NULL, NULL, true);
+ break;
+ default:
+ sLog.outError("EffectDummy: Non-handled case for spell 13567 for triggered aura %u",m_triggeredByAuraSpell->Id);
+ break;
+ }
+ return;
+ }
+ case 14185: // Preparation Rogue
+ {
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ //immediately finishes the cooldown on certain Rogue abilities
+ const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap();
+ for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
+ {
+ uint32 classspell = itr->first;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell);
+
+ if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & 0x26000000860LL))
+ {
+ ((Player*)m_caster)->RemoveSpellCooldown(classspell);
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(classspell);
+ data << uint64(m_caster->GetGUID());
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+ }
+ return;
+ }
+ case 15998: // Capture Worg Pup
+ case 29435: // Capture Female Kaliri Hatchling
+ {
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
+ return;
+
+ Creature* creatureTarget = (Creature*)unitTarget;
+ creatureTarget->setDeathState(JUST_DIED);
+ creatureTarget->RemoveCorpse();
+ creatureTarget->SetHealth(0); // just for nice GM-mode view
+ return;
+ }
+ case 16589: // Noggenfogger Elixir
+ {
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = 0;
+ switch(urand(1,3))
+ {
+ case 1: spell_id = 16595; break;
+ case 2: spell_id = 16593; break;
+ default:spell_id = 16591; break;
+ }
+
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 17251: // Spirit Healer Res
+ {
+ if(!unitTarget || !m_originalCaster)
+ return;
+
+ if(m_originalCaster->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8);
+ data << unitTarget->GetGUID();
+ ((Player*)m_originalCaster)->GetSession()->SendPacket( &data );
+ }
+ return;
+ }
+ case 17271: // Test Fetid Skull
+ {
+ if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = roll_chance_i(50) ? 17269 : 17270;
+
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 20577: // Cannibalize
+ if (unitTarget)
+ m_caster->CastSpell(m_caster,20578,false,NULL);
+ return;
+ case 23019: // Crystal Prison Dummy DND
+ {
+ if(!unitTarget || !unitTarget->isAlive() || unitTarget->GetTypeId() != TYPEID_UNIT || ((Creature*)unitTarget)->isPet())
+ return;
+
+ Creature* creatureTarget = (Creature*)unitTarget;
+ if(creatureTarget->isPet())
+ return;
+
+ creatureTarget->setDeathState(JUST_DIED);
+ creatureTarget->RemoveCorpse();
+ creatureTarget->SetHealth(0); // just for nice GM-mode view
+
+ GameObject* pGameObj = new GameObject;
+
+ Map *map = creatureTarget->GetMap();
+
+ if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), 179644, map,
+ creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(),
+ creatureTarget->GetOrientation(), 0, 0, 0, 0, 100, 1) )
+ {
+ delete pGameObj;
+ return;
+ }
+
+ pGameObj->SetRespawnTime(creatureTarget->GetRespawnTime()-time(NULL));
+ pGameObj->SetOwnerGUID(m_caster->GetGUID() );
+ pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() );
+ pGameObj->SetSpellId(m_spellInfo->Id);
+
+ DEBUG_LOG("AddObject at SpellEfects.cpp EffectDummy\n");
+ map->Add(pGameObj);
+
+ WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
+ data << uint64(pGameObj->GetGUID());
+ m_caster->SendMessageToSet(&data,true);
+
+ return;
+ }
+ case 23074: // Arc. Dragonling
+ if (!m_CastItem) return;
+ m_caster->CastSpell(m_caster,19804,true,m_CastItem);
+ return;
+ case 23075: // Mithril Mechanical Dragonling
+ if (!m_CastItem) return;
+ m_caster->CastSpell(m_caster,12749,true,m_CastItem);
+ return;
+ case 23076: // Mechanical Dragonling
+ if (!m_CastItem) return;
+ m_caster->CastSpell(m_caster,4073,true,m_CastItem);
+ return;
+ case 23133: // Gnomish Battle Chicken
+ if (!m_CastItem) return;
+ m_caster->CastSpell(m_caster,13166,true,m_CastItem);
+ return;
+ case 23448: // Ultrasafe Transporter: Gadgetzan - backfires
+ {
+ int32 r = irand(0, 119);
+ if ( r < 20 ) // 1/6 polymorph
+ m_caster->CastSpell(m_caster,23444,true);
+ else if ( r < 100 ) // 4/6 evil twin
+ m_caster->CastSpell(m_caster,23445,true);
+ else // 1/6 miss the target
+ m_caster->CastSpell(m_caster,36902,true);
+ return;
+ }
+ case 23453: // Ultrasafe Transporter: Gadgetzan
+ if ( roll_chance_i(50) ) // success
+ m_caster->CastSpell(m_caster,23441,true);
+ else // failure
+ m_caster->CastSpell(m_caster,23446,true);
+ return;
+ case 23645: // Hourglass Sand
+ m_caster->RemoveAurasDueToSpell(23170);
+ return;
+ case 23725: // Gift of Life (warrior bwl trinket)
+ m_caster->CastSpell(m_caster,23782,true);
+ m_caster->CastSpell(m_caster,23783,true);
+ return;
+ case 25860: // Reindeer Transformation
+ {
+ if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED))
+ return;
+
+ float flyspeed = m_caster->GetSpeedRate(MOVE_FLY);
+ float speed = m_caster->GetSpeedRate(MOVE_RUN);
+
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+
+ //5 different spells used depending on mounted speed and if mount can fly or not
+ if (flyspeed >= 4.1f)
+ m_caster->CastSpell(m_caster, 44827, true); //310% flying Reindeer
+ else if (flyspeed >= 3.8f)
+ m_caster->CastSpell(m_caster, 44825, true); //280% flying Reindeer
+ else if (flyspeed >= 1.6f)
+ m_caster->CastSpell(m_caster, 44824, true); //60% flying Reindeer
+ else if (speed >= 2.0f)
+ m_caster->CastSpell(m_caster, 25859, true); //100% ground Reindeer
+ else
+ m_caster->CastSpell(m_caster, 25858, true); //60% ground Reindeer
+
+ return;
+ }
+ //case 26074: // Holiday Cheer
+ // return; -- implemented at client side
+ case 28006: // Arcane Cloaking
+ {
+ if( unitTarget->GetTypeId() == TYPEID_PLAYER )
+ m_caster->CastSpell(unitTarget,29294,true);
+ return;
+ }
+ case 28730: // Arcane Torrent (Mana)
+ {
+ int32 count = 0;
+ Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i)
+ if ((*i)->GetId() == 28734)
+ ++count;
+ if (count)
+ {
+ m_caster->RemoveAurasDueToSpell(28734);
+ int32 bp = damage * count;
+ m_caster->CastCustomSpell(m_caster, 28733, &bp, NULL, NULL, true);
+ }
+ return;
+ }
+ case 29200: // Purify Helboar Meat
+ {
+ if( m_caster->GetTypeId() != TYPEID_PLAYER )
+ return;
+
+ uint32 spell_id = roll_chance_i(50) ? 29277 : 29278;
+
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 29858: // Soulshatter
+ if (unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT && unitTarget->IsHostileTo(m_caster))
+ m_caster->CastSpell(unitTarget,32835,true);
+ return;
+ case 30458: // Nigh Invulnerability
+ if (!m_CastItem) return;
+ if(roll_chance_i(86)) // success
+ m_caster->CastSpell(m_caster, 30456, true, m_CastItem);
+ else // backfire in 14% casts
+ m_caster->CastSpell(m_caster, 30457, true, m_CastItem);
+ return;
+ case 30507: // Poultryizer
+ if (!m_CastItem) return;
+ if(roll_chance_i(80)) // success
+ m_caster->CastSpell(unitTarget, 30501, true, m_CastItem);
+ else // backfire 20%
+ m_caster->CastSpell(unitTarget, 30504, true, m_CastItem);
+ return;
+ case 33060: // Make a Wish
+ {
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = 0;
+
+ switch(urand(1,5))
+ {
+ case 1: spell_id = 33053; break;
+ case 2: spell_id = 33057; break;
+ case 3: spell_id = 33059; break;
+ case 4: spell_id = 33062; break;
+ case 5: spell_id = 33064; break;
+ }
+
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+ case 35745:
+ {
+ uint32 spell_id;
+ switch(m_caster->GetAreaId())
+ {
+ case 3900: spell_id = 35743; break;
+ case 3742: spell_id = 35744; break;
+ default: return;
+ }
+
+ m_caster->CastSpell(m_caster,spell_id,true);
+ return;
+ }
+ case 37674: // Chaos Blast
+ if(unitTarget)
+ m_caster->CastSpell(unitTarget,37675,true);
+ return;
+ case 44875: // Complete Raptor Capture
+ {
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
+ return;
+
+ Creature* creatureTarget = (Creature*)unitTarget;
+
+ creatureTarget->setDeathState(JUST_DIED);
+ creatureTarget->RemoveCorpse();
+ creatureTarget->SetHealth(0); // just for nice GM-mode view
+
+ //cast spell Raptor Capture Credit
+ m_caster->CastSpell(m_caster,42337,true,NULL);
+ return;
+ }
+ case 37573: //Temporal Phase Modulator
+ {
+ if(!unitTarget)
+ return;
+
+ TemporarySummon* tempSummon = dynamic_cast<TemporarySummon*>(unitTarget);
+ if(!tempSummon)
+ return;
+
+ uint32 health = tempSummon->GetHealth();
+ const uint32 entry_list[6] = {21821, 21820, 21817};
+
+ float x = tempSummon->GetPositionX();
+ float y = tempSummon->GetPositionY();
+ float z = tempSummon->GetPositionZ();
+ float o = tempSummon->GetOrientation();
+
+ tempSummon->UnSummon();
+
+ Creature* pCreature = m_caster->SummonCreature(entry_list[urand(0, 2)], x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,180000);
+ if (!pCreature)
+ return;
+
+ pCreature->SetHealth(health);
+
+ if(pCreature->AI())
+ pCreature->AI()->AttackStart(m_caster);
+
+ return;
+ }
+ case 34665: //Administer Antidote
+ {
+ if(!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER )
+ return;
+
+ if(!unitTarget)
+ return;
+
+ TemporarySummon* tempSummon = dynamic_cast<TemporarySummon*>(unitTarget);
+ if(!tempSummon)
+ return;
+
+ uint32 health = tempSummon->GetHealth();
+
+ float x = tempSummon->GetPositionX();
+ float y = tempSummon->GetPositionY();
+ float z = tempSummon->GetPositionZ();
+ float o = tempSummon->GetOrientation();
+ tempSummon->UnSummon();
+
+ Creature* pCreature = m_caster->SummonCreature(16992, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,180000);
+ if (!pCreature)
+ return;
+
+ pCreature->SetHealth(health);
+ ((Player*)m_caster)->KilledMonster(16992,pCreature->GetGUID());
+
+ if (pCreature->AI())
+ pCreature->AI()->AttackStart(m_caster);
+
+ return;
+ }
+ case 44997: // Converting Sentry
+ {
+ //Converted Sentry Credit
+ m_caster->CastSpell(m_caster, 45009, true);
+ return;
+ }
+ case 45030: // Impale Emissary
+ {
+ // Emissary of Hate Credit
+ m_caster->CastSpell(m_caster, 45088, true);
+ return;
+ }
+ case 50243: // Teach Language
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // spell has a 1/3 chance to trigger one of the below
+ if(roll_chance_i(66))
+ return;
+ if(((Player*)m_caster)->GetTeam() == ALLIANCE)
+ {
+ // 1000001 - gnomish binary
+ m_caster->CastSpell(m_caster, 50242, true);
+ }
+ else
+ {
+ // 01001000 - goblin binary
+ m_caster->CastSpell(m_caster, 50246, true);
+ }
+
+ return;
+ }
+ case 51582: //Rocket Boots Engaged (Rocket Boots Xtreme and Rocket Boots Xtreme Lite)
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(BattleGround* bg = ((Player*)m_caster)->GetBattleGround())
+ bg->EventPlayerDroppedFlag((Player*)m_caster);
+
+ m_caster->CastSpell(m_caster, 30452, true, NULL);
+ return;
+ }
+ }
+
+ //All IconID Check in there
+ switch(m_spellInfo->SpellIconID)
+ {
+ // Berserking (troll racial traits)
+ case 1661:
+ {
+ uint32 healthPerc = uint32((float(m_caster->GetHealth())/m_caster->GetMaxHealth())*100);
+ int32 melee_mod = 10;
+ if (healthPerc <= 40)
+ melee_mod = 30;
+ if (healthPerc < 100 && healthPerc > 40)
+ melee_mod = 10+(100-healthPerc)/3;
+
+ int32 hasteModBasePoints0 = melee_mod; // (EffectBasePoints[0]+1)-1+(5-melee_mod) = (melee_mod-1+1)-1+5-melee_mod = 5-1
+ int32 hasteModBasePoints1 = (5-melee_mod);
+ int32 hasteModBasePoints2 = 5;
+
+ // FIXME: custom spell required this aura state by some unknown reason, we not need remove it anyway
+ m_caster->ModifyAuraState(AURA_STATE_BERSERKING,true);
+ m_caster->CastCustomSpell(m_caster,26635,&hasteModBasePoints0,&hasteModBasePoints1,&hasteModBasePoints2,true,NULL);
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_MAGE:
+ switch(m_spellInfo->Id )
+ {
+ case 11958: // Cold Snap
+ {
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ // immediately finishes the cooldown on Frost spells
+ const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap();
+ for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
+ {
+ if (itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+
+ uint32 classspell = itr->first;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell);
+
+ if( spellInfo->SpellFamilyName == SPELLFAMILY_MAGE &&
+ (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) &&
+ spellInfo->Id != 11958 && GetSpellRecoveryTime(spellInfo) > 0 )
+ {
+ ((Player*)m_caster)->RemoveSpellCooldown(classspell);
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(classspell);
+ data << uint64(m_caster->GetGUID());
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+ }
+ return;
+ }
+ case 32826:
+ {
+ if ( unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT )
+ {
+ //Polymorph Cast Visual Rank 1
+ const uint32 spell_list[6] = {32813, 32816, 32817, 32818, 32819, 32820};
+ unitTarget->CastSpell( unitTarget, spell_list[urand(0, 5)], true);
+ }
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_WARRIOR:
+ // Charge
+ if(m_spellInfo->SpellFamilyFlags & 0x1 && m_spellInfo->SpellVisual == 867)
+ {
+ int32 chargeBasePoints0 = damage;
+ m_caster->CastCustomSpell(m_caster,34846,&chargeBasePoints0,NULL,NULL,true);
+ return;
+ }
+ // Execute
+ if(m_spellInfo->SpellFamilyFlags & 0x20000000)
+ {
+ if(!unitTarget)
+ return;
+
+ int32 basePoints0 = damage+int32(m_caster->GetPower(POWER_RAGE) * m_spellInfo->DmgMultiplier[i]);
+ m_caster->CastCustomSpell(unitTarget, 20647, &basePoints0, NULL, NULL, true, 0);
+ m_caster->SetPower(POWER_RAGE,0);
+ return;
+ }
+ if(m_spellInfo->Id==21977) //Warrior's Wrath
+ {
+ if(!unitTarget)
+ return;
+
+ m_caster->CastSpell(unitTarget,21887,true); // spell mod
+ return;
+ }
+ break;
+ case SPELLFAMILY_WARLOCK:
+ //Life Tap (only it have this with dummy effect)
+ if (m_spellInfo->SpellFamilyFlags == 0x40000)
+ {
+ float cost = m_currentBasePoints[0]+1;
+
+ if(Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COST, cost,this);
+
+ int32 dmg = m_caster->SpellDamageBonus(m_caster, m_spellInfo,uint32(cost > 0 ? cost : 0), SPELL_DIRECT_DAMAGE);
+
+ if(int32(m_caster->GetHealth()) > dmg)
+ {
+ // Shouldn't Appear in Combat Log
+ m_caster->ModifyHealth(-dmg);
+
+ int32 mana = dmg;
+
+ Unit::AuraList const& auraDummy = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr != auraDummy.end(); ++itr)
+ {
+ // only Imp. Life Tap have this in combination with dummy aura
+ if((*itr)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (*itr)->GetSpellProto()->SpellIconID == 208)
+ mana = ((*itr)->GetModifier()->m_amount + 100)* mana / 100;
+ }
+
+ m_caster->CastCustomSpell(m_caster,31818,&mana,NULL,NULL,true,NULL);
+
+ // Mana Feed
+ int32 manaFeedVal = m_caster->CalculateSpellDamage(m_spellInfo,1, m_spellInfo->EffectBasePoints[1],m_caster);
+ manaFeedVal = manaFeedVal * mana / 100;
+ if(manaFeedVal > 0)
+ m_caster->CastCustomSpell(m_caster,32553,&manaFeedVal,NULL,NULL,true,NULL);
+ }
+ else
+ SendCastResult(SPELL_FAILED_FIZZLE);
+ return;
+ }
+ break;
+ case SPELLFAMILY_PRIEST:
+ switch(m_spellInfo->Id )
+ {
+ case 28598: // Touch of Weakness triggered spell
+ {
+ if(!unitTarget || !m_triggeredByAuraSpell)
+ return;
+
+ uint32 spellid = 0;
+ switch(m_triggeredByAuraSpell->Id)
+ {
+ case 2652: spellid = 2943; break; // Rank 1
+ case 19261: spellid = 19249; break; // Rank 2
+ case 19262: spellid = 19251; break; // Rank 3
+ case 19264: spellid = 19252; break; // Rank 4
+ case 19265: spellid = 19253; break; // Rank 5
+ case 19266: spellid = 19254; break; // Rank 6
+ case 25461: spellid = 25460; break; // Rank 7
+ default:
+ sLog.outError("Spell::EffectDummy: Spell 28598 triggered by unhandeled spell %u",m_triggeredByAuraSpell->Id);
+ return;
+ }
+ m_caster->CastSpell(unitTarget, spellid, true, NULL);
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_DRUID:
+ switch(m_spellInfo->Id )
+ {
+ case 5420: // Tree of Life passive
+ {
+ // Tree of Life area effect
+ int32 health_mod = int32(m_caster->GetStat(STAT_SPIRIT)/4);
+ m_caster->CastCustomSpell(m_caster,34123,&health_mod,NULL,NULL,true,NULL);
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_ROGUE:
+ switch(m_spellInfo->Id )
+ {
+ case 31231: // Cheat Death
+ {
+ m_caster->CastSpell(m_caster,45182,true);
+ return;
+ }
+ case 5938: // Shiv
+ {
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *pCaster = ((Player*)m_caster);
+
+ Item *item = pCaster->GetWeaponForAttack(OFF_ATTACK);
+ if(!item)
+ return;
+
+ // all poison enchantments is temporary
+ uint32 enchant_id = item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT);
+ if(!enchant_id)
+ return;
+
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return;
+
+ for (int s=0;s<3;s++)
+ {
+ if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
+ continue;
+
+ SpellEntry const* combatEntry = sSpellStore.LookupEntry(pEnchant->spellid[s]);
+ if(!combatEntry || combatEntry->Dispel != DISPEL_POISON)
+ continue;
+
+ m_caster->CastSpell(unitTarget, combatEntry, true, item);
+ }
+
+ m_caster->CastSpell(unitTarget, 5940, true);
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_HUNTER:
+ // Steady Shot
+ if(m_spellInfo->SpellFamilyFlags & 0x100000000LL)
+ {
+ if( !unitTarget || !unitTarget->isAlive())
+ return;
+
+ bool found = false;
+
+ // check dazed affect
+ Unit::AuraList const& decSpeedList = unitTarget->GetAurasByType(SPELL_AURA_MOD_DECREASE_SPEED);
+ for(Unit::AuraList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter)
+ {
+ if((*iter)->GetSpellProto()->SpellIconID==15 && (*iter)->GetSpellProto()->Dispel==0)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if(found)
+ m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
+ return;
+ }
+ // Kill command
+ if(m_spellInfo->SpellFamilyFlags & 0x00080000000000LL)
+ {
+ if(m_caster->getClass()!=CLASS_HUNTER)
+ return;
+
+ // clear hunter crit aura state
+ m_caster->ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE,false);
+
+ // additional damage from pet to pet target
+ Pet* pet = m_caster->GetPet();
+ if(!pet || !pet->getVictim())
+ return;
+
+ uint32 spell_id = 0;
+ switch (m_spellInfo->Id)
+ {
+ case 34026: spell_id = 34027; break; // rank 1
+ default:
+ sLog.outError("Spell::EffectDummy: Spell %u not handled in KC",m_spellInfo->Id);
+ return;
+ }
+
+ pet->CastSpell(pet->getVictim(), spell_id, true);
+ return;
+ }
+
+ switch(m_spellInfo->Id)
+ {
+ case 23989: //Readiness talent
+ {
+ if(m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ //immediately finishes the cooldown for hunter abilities
+ const PlayerSpellMap& sp_list = ((Player *)m_caster)->GetSpellMap();
+ for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
+ {
+ uint32 classspell = itr->first;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(classspell);
+
+ if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && spellInfo->Id != 23989 && GetSpellRecoveryTime(spellInfo) > 0 )
+ {
+ ((Player*)m_caster)->RemoveSpellCooldown(classspell);
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(classspell);
+ data << uint64(m_caster->GetGUID());
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+ }
+ return;
+ }
+ case 37506: // Scatter Shot
+ {
+ if (m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ // break Auto Shot and autohit
+ m_caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+ m_caster->AttackStop();
+ ((Player*)m_caster)->SendAttackSwingCancelAttack();
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_PALADIN:
+ switch(m_spellInfo->SpellIconID)
+ {
+ case 156: // Holy Shock
+ {
+ if(!unitTarget)
+ return;
+
+ int hurt = 0;
+ int heal = 0;
+
+ switch(m_spellInfo->Id)
+ {
+ case 20473: hurt = 25912; heal = 25914; break;
+ case 20929: hurt = 25911; heal = 25913; break;
+ case 20930: hurt = 25902; heal = 25903; break;
+ case 27174: hurt = 27176; heal = 27175; break;
+ case 33072: hurt = 33073; heal = 33074; break;
+ default:
+ sLog.outError("Spell::EffectDummy: Spell %u not handled in HS",m_spellInfo->Id);
+ return;
+ }
+
+ if(m_caster->IsFriendlyTo(unitTarget))
+ m_caster->CastSpell(unitTarget, heal, true, 0);
+ else
+ m_caster->CastSpell(unitTarget, hurt, true, 0);
+
+ return;
+ }
+ case 561: // Judgement of command
+ {
+ if(!unitTarget)
+ return;
+
+ uint32 spell_id = m_currentBasePoints[i]+1;
+ SpellEntry const* spell_proto = sSpellStore.LookupEntry(spell_id);
+ if(!spell_proto)
+ return;
+
+ if( !unitTarget->hasUnitState(UNIT_STAT_STUNNED) && m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ // decreased damage (/2) for non-stunned target.
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_DAMAGE;
+ mod->value = -50;
+ mod->type = SPELLMOD_PCT;
+ mod->spellId = m_spellInfo->Id;
+ mod->effectId = i;
+ mod->lastAffected = NULL;
+ mod->mask = 0x0000020000000000LL;
+ mod->charges = 0;
+
+ ((Player*)m_caster)->AddSpellMod(mod, true);
+ m_caster->CastSpell(unitTarget,spell_proto,true,NULL);
+ // mod deleted
+ ((Player*)m_caster)->AddSpellMod(mod, false);
+ }
+ else
+ m_caster->CastSpell(unitTarget,spell_proto,true,NULL);
+
+ return;
+ }
+ }
+
+ switch(m_spellInfo->Id)
+ {
+ case 31789: // Righteous Defense (step 1)
+ {
+ // 31989 -> dummy effect (step 1) + dummy effect (step 2) -> 31709 (taunt like spell for each target)
+
+ // non-standard cast requirement check
+ if (!unitTarget || unitTarget->getAttackers().empty())
+ {
+ // clear cooldown at fail
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ ((Player*)m_caster)->RemoveSpellCooldown(m_spellInfo->Id);
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(m_spellInfo->Id);
+ data << uint64(m_caster->GetGUID());
+ ((Player*)m_caster)->GetSession()->SendPacket(&data);
+ }
+
+ SendCastResult(SPELL_FAILED_TARGET_AFFECTING_COMBAT);
+ return;
+ }
+
+ // Righteous Defense (step 2) (in old version 31980 dummy effect)
+ // Clear targets for eff 1
+ for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
+ ihit->effectMask &= ~(1<<1);
+
+ // not empty (checked)
+ Unit::AttackerSet const& attackers = unitTarget->getAttackers();
+
+ // chance to be selected from list
+ float chance = 100.0f/attackers.size();
+ uint32 count=0;
+ for(Unit::AttackerSet::const_iterator aItr = attackers.begin(); aItr != attackers.end() && count < 3; ++aItr)
+ {
+ if(!roll_chance_f(chance))
+ continue;
+ ++count;
+ AddUnitTarget((*aItr), 1);
+ }
+
+ // now let next effect cast spell at each target.
+ return;
+ }
+ case 37877: // Blessing of Faith
+ {
+ if(!unitTarget)
+ return;
+
+ uint32 spell_id = 0;
+ switch(unitTarget->getClass())
+ {
+ case CLASS_DRUID: spell_id = 37878; break;
+ case CLASS_PALADIN: spell_id = 37879; break;
+ case CLASS_PRIEST: spell_id = 37880; break;
+ case CLASS_SHAMAN: spell_id = 37881; break;
+ default: return; // ignore for not healing classes
+ }
+
+ m_caster->CastSpell(m_caster,spell_id,true);
+ return;
+ }
+ }
+ break;
+ case SPELLFAMILY_SHAMAN:
+ //Shaman Rockbiter Weapon
+ if (m_spellInfo->SpellFamilyFlags == 0x400000)
+ {
+ uint32 spell_id = 0;
+ switch(m_spellInfo->Id)
+ {
+ case 8017: spell_id = 36494; break; // Rank 1
+ case 8018: spell_id = 36750; break; // Rank 2
+ case 8019: spell_id = 36755; break; // Rank 3
+ case 10399: spell_id = 36759; break; // Rank 4
+ case 16314: spell_id = 36763; break; // Rank 5
+ case 16315: spell_id = 36766; break; // Rank 6
+ case 16316: spell_id = 36771; break; // Rank 7
+ case 25479: spell_id = 36775; break; // Rank 8
+ case 25485: spell_id = 36499; break; // Rank 9
+ default:
+ sLog.outError("Spell::EffectDummy: Spell %u not handled in RW",m_spellInfo->Id);
+ return;
+ }
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %i\n", spell_id);
+ return;
+ }
+
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ for(int i = BASE_ATTACK; i <= OFF_ATTACK; ++i)
+ {
+ if(Item* item = ((Player*)m_caster)->GetWeaponForAttack(WeaponAttackType(i)))
+ {
+ if(item->IsFitToSpellRequirements(m_spellInfo))
+ {
+ Spell *spell = new Spell(m_caster, spellInfo, true);
+
+ // enchanting spell selected by calculated damage-per-sec in enchanting effect
+ // at calculation applied affect from Elemental Weapons talent
+ // real enchantment damage-1
+ spell->m_currentBasePoints[1] = damage-1;
+
+ SpellCastTargets targets;
+ targets.setItemTarget( item );
+ spell->prepare(&targets);
+ }
+ }
+ }
+ return;
+ }
+
+ if(m_spellInfo->Id == 39610) // Mana-Tide Totem effect
+ {
+ if(!unitTarget || unitTarget->getPowerType() != POWER_MANA)
+ return;
+
+ // Regenerate 6% of Total Mana Every 3 secs
+ int32 EffectBasePoints0 = unitTarget->GetMaxPower(POWER_MANA) * damage / 100;
+ m_caster->CastCustomSpell(unitTarget,39609,&EffectBasePoints0,NULL,NULL,true,NULL,NULL,m_originalCasterGUID);
+ return;
+ }
+
+ break;
+ }
+
+ // pet auras
+ if(PetAura const* petSpell = spellmgr.GetPetAura(m_spellInfo->Id))
+ {
+ m_caster->AddPetAura(petSpell);
+ return;
+ }
+}
+
+void Spell::EffectTriggerSpellWithValue(uint32 i)
+{
+ uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
+
+ // normal case
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("EffectTriggerSpellWithValue of spell %u: triggering unknown spell id %i\n", m_spellInfo->Id,triggered_spell_id);
+ return;
+ }
+
+ int32 bp = damage;
+ m_caster->CastCustomSpell(unitTarget,triggered_spell_id,&bp,&bp,&bp,true,NULL,NULL,m_originalCasterGUID);
+}
+
+void Spell::EffectTriggerRitualOfSummoning(uint32 i)
+{
+ uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("EffectTriggerRitualOfSummoning of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id);
+ return;
+ }
+
+ finish();
+ Spell *spell = new Spell(m_caster, spellInfo, true);
+
+ SpellCastTargets targets;
+ targets.setUnitTarget( unitTarget);
+ spell->prepare(&targets);
+
+ m_caster->SetCurrentCastedSpell(spell);
+ spell->m_selfContainer = &(m_caster->m_currentSpells[spell->GetCurrentContainer()]);
+
+}
+
+void Spell::EffectForceCast(uint32 i)
+{
+ if( !unitTarget )
+ return;
+
+ uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
+
+ // normal case
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("EffectForceCast of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id);
+ return;
+ }
+
+ unitTarget->CastSpell(unitTarget,spellInfo,true,NULL,NULL,m_originalCasterGUID);
+}
+
+void Spell::EffectTriggerSpell(uint32 i)
+{
+ uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[i];
+
+ // special cases
+ switch(triggered_spell_id)
+ {
+ // Vanish
+ case 18461:
+ {
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_DECREASE_SPEED);
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STALKED);
+
+ // if this spell is given to NPC it must handle rest by it's own AI
+ if ( m_caster->GetTypeId() != TYPEID_PLAYER )
+ return;
+
+ // get highest rank of the Stealth spell
+ uint32 spellId = 0;
+ const PlayerSpellMap& sp_list = ((Player*)m_caster)->GetSpellMap();
+ for (PlayerSpellMap::const_iterator itr = sp_list.begin(); itr != sp_list.end(); ++itr)
+ {
+ // only highest rank is shown in spell book, so simply check if shown in spell book
+ if(!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED)
+ continue;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+ if (!spellInfo)
+ continue;
+
+ if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE_STEALTH)
+ {
+ spellId = spellInfo->Id;
+ break;
+ }
+ }
+
+ // no Stealth spell found
+ if (!spellId)
+ return;
+
+ // reset cooldown on it if needed
+ if(((Player*)m_caster)->HasSpellCooldown(spellId))
+ ((Player*)m_caster)->RemoveSpellCooldown(spellId);
+
+ m_caster->CastSpell(m_caster, spellId, true);
+ return;
+ }
+ // just skip
+ case 23770: // Sayge's Dark Fortune of *
+ // not exist, common cooldown can be implemented in scripts if need.
+ return;
+ // Brittle Armor - (need add max stack of 24575 Brittle Armor)
+ case 29284:
+ {
+ const SpellEntry *spell = sSpellStore.LookupEntry(24575);
+ if (!spell)
+ return;
+
+ for (int i=0; i < spell->StackAmount; ++i)
+ m_caster->CastSpell(unitTarget,spell->Id, true, m_CastItem, NULL, m_originalCasterGUID);
+ return;
+ }
+ // Mercurial Shield - (need add max stack of 26464 Mercurial Shield)
+ case 29286:
+ {
+ const SpellEntry *spell = sSpellStore.LookupEntry(26464);
+ if (!spell)
+ return;
+
+ for (int i=0; i < spell->StackAmount; ++i)
+ m_caster->CastSpell(unitTarget,spell->Id, true, m_CastItem, NULL, m_originalCasterGUID);
+ return;
+ }
+ // Righteous Defense
+ case 31980:
+ {
+ m_caster->CastSpell(unitTarget, 31790, true,m_CastItem,NULL,m_originalCasterGUID);
+ return;
+ }
+ // Cloak of Shadows
+ case 35729 :
+ {
+ Unit::AuraMap& Auras = m_caster->GetAuras();
+ for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end(); ++iter)
+ {
+ // remove all harmful spells on you...
+ if( // ignore positive and passive auras
+ !iter->second->IsPositive() && !iter->second->IsPassive() &&
+ // ignore physical auras
+ (GetSpellSchoolMask(iter->second->GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL)==0 &&
+ // ignore immunity persistent spells
+ !( iter->second->GetSpellProto()->AttributesEx & 0x10000 ) )
+ {
+ m_caster->RemoveAurasDueToSpell(iter->second->GetSpellProto()->Id);
+ iter = Auras.begin();
+ }
+ }
+ return;
+ }
+ // Priest Shadowfiend (34433) need apply mana gain trigger aura on pet
+ case 41967:
+ {
+ if (Unit *pet = m_caster->GetPet())
+ pet->CastSpell(pet, 28305, true);
+ return;
+ }
+ }
+
+ // normal case
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("EffectTriggerSpell of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id);
+ return;
+ }
+
+ // some triggered spells require specific equipment
+ if(spellInfo->EquippedItemClass >=0 && m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ // main hand weapon required
+ if(spellInfo->AttributesEx3 & SPELL_ATTR_EX3_MAIN_HAND)
+ {
+ Item* item = ((Player*)m_caster)->GetWeaponForAttack(BASE_ATTACK);
+
+ // skip spell if no weapon in slot or broken
+ if(!item || item->IsBroken() )
+ return;
+
+ // skip spell if weapon not fit to triggered spell
+ if(!item->IsFitToSpellRequirements(spellInfo))
+ return;
+ }
+
+ // offhand hand weapon required
+ if(spellInfo->AttributesEx3 & SPELL_ATTR_EX3_REQ_OFFHAND)
+ {
+ Item* item = ((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK);
+
+ // skip spell if no weapon in slot or broken
+ if(!item || item->IsBroken() )
+ return;
+
+ // skip spell if weapon not fit to triggered spell
+ if(!item->IsFitToSpellRequirements(spellInfo))
+ return;
+ }
+ }
+
+ // some triggered spells must be casted instantly (for example, if next effect case instant kill caster)
+ bool instant = false;
+ for(uint32 j = i+1; j < 3; ++j)
+ {
+ if(m_spellInfo->Effect[j]==SPELL_EFFECT_INSTAKILL && m_spellInfo->EffectImplicitTargetA[j]==TARGET_SELF)
+ {
+ instant = true;
+ break;
+ }
+ }
+
+ if(instant)
+ {
+ if (unitTarget)
+ m_caster->CastSpell(unitTarget,spellInfo,true,m_CastItem,NULL,m_originalCasterGUID);
+ }
+ else
+ m_TriggerSpells.push_back(spellInfo);
+}
+
+void Spell::EffectTriggerMissileSpell(uint32 effect_idx)
+{
+ uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[effect_idx];
+
+ // normal case
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
+
+ if(!spellInfo)
+ {
+ sLog.outError("EffectTriggerMissileSpell of spell %u: triggering unknown spell id %effect_idx", m_spellInfo->Id,triggered_spell_id);
+ return;
+ }
+
+ if (m_CastItem)
+ DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
+ Spell *spell = new Spell(m_caster, spellInfo, true, m_originalCasterGUID );
+
+ SpellCastTargets targets;
+ targets.setDestination(m_targets.m_destX,m_targets.m_destY,m_targets.m_destZ);
+ spell->m_CastItem = m_CastItem;
+ spell->prepare(&targets, NULL);
+}
+
+void Spell::EffectTeleportUnits(uint32 i)
+{
+ if(!unitTarget || unitTarget->isInFlight())
+ return;
+
+ switch (m_spellInfo->EffectImplicitTargetB[i])
+ {
+ case TARGET_INNKEEPER_COORDINATES:
+ {
+ // Only players can teleport to innkeeper
+ if (unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)unitTarget)->TeleportTo(((Player*)unitTarget)->m_homebindMapId,((Player*)unitTarget)->m_homebindX,((Player*)unitTarget)->m_homebindY,((Player*)unitTarget)->m_homebindZ,unitTarget->GetOrientation(),unitTarget==m_caster ? TELE_TO_SPELL : 0);
+ return;
+ }
+ case TARGET_TABLE_X_Y_Z_COORDINATES:
+ {
+ // TODO: Only players can teleport?
+ if (unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ SpellTargetPosition const* st = spellmgr.GetSpellTargetPosition(m_spellInfo->Id);
+ if(!st)
+ {
+ sLog.outError( "Spell::EffectTeleportUnits - unknown Teleport coordinates for spell ID %u\n", m_spellInfo->Id );
+ return;
+ }
+ ((Player*)unitTarget)->TeleportTo(st->target_mapId,st->target_X,st->target_Y,st->target_Z,st->target_Orientation,unitTarget==m_caster ? TELE_TO_SPELL : 0);
+ break;
+ }
+ case TARGET_BEHIND_VICTIM:
+ {
+ // Get selected target for player (or victim for units)
+ Unit *pTarget = NULL;
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection());
+ else
+ pTarget = m_caster->getVictim();
+ // No target present - return
+ if (!pTarget)
+ return;
+ // Init dest coordinates
+ uint32 mapid = m_caster->GetMapId();
+ float x = m_targets.m_destX;
+ float y = m_targets.m_destY;
+ float z = m_targets.m_destZ;
+ float orientation = pTarget->GetOrientation();
+ // Teleport
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
+ else
+ {
+ MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation);
+ WorldPacket data;
+ unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation);
+ unitTarget->SendMessageToSet(&data, false);
+ }
+ return;
+ }
+ default:
+ {
+ // If not exist data for dest location - return
+ if(!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION))
+ {
+ sLog.outError( "Spell::EffectTeleportUnits - unknown EffectImplicitTargetB[%u] = %u for spell ID %u\n", i, m_spellInfo->EffectImplicitTargetB[i], m_spellInfo->Id );
+ return;
+ }
+ // Init dest coordinates
+ uint32 mapid = m_caster->GetMapId();
+ float x = m_targets.m_destX;
+ float y = m_targets.m_destY;
+ float z = m_targets.m_destZ;
+ float orientation = unitTarget->GetOrientation();
+ // Teleport
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
+ else
+ {
+ MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation);
+ WorldPacket data;
+ unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation);
+ unitTarget->SendMessageToSet(&data, false);
+ }
+ return;
+ }
+ }
+
+ // post effects for TARGET_TABLE_X_Y_Z_COORDINATES
+ switch ( m_spellInfo->Id )
+ {
+ // Dimensional Ripper - Everlook
+ case 23442:
+ {
+ int32 r = irand(0, 119);
+ if ( r >= 70 ) // 7/12 success
+ {
+ if ( r < 100 ) // 4/12 evil twin
+ m_caster->CastSpell(m_caster,23445,true);
+ else // 1/12 fire
+ m_caster->CastSpell(m_caster,23449,true);
+ }
+ return;
+ }
+ // Ultrasafe Transporter: Toshley's Station
+ case 36941:
+ {
+ if ( roll_chance_i(50) ) // 50% success
+ {
+ int32 rand_eff = urand(1,7);
+ switch ( rand_eff )
+ {
+ case 1:
+ // soul split - evil
+ m_caster->CastSpell(m_caster,36900,true);
+ break;
+ case 2:
+ // soul split - good
+ m_caster->CastSpell(m_caster,36901,true);
+ break;
+ case 3:
+ // Increase the size
+ m_caster->CastSpell(m_caster,36895,true);
+ break;
+ case 4:
+ // Decrease the size
+ m_caster->CastSpell(m_caster,36893,true);
+ break;
+ case 5:
+ // Transform
+ {
+ if (((Player*)m_caster)->GetTeam() == ALLIANCE )
+ m_caster->CastSpell(m_caster,36897,true);
+ else
+ m_caster->CastSpell(m_caster,36899,true);
+ break;
+ }
+ case 6:
+ // chicken
+ m_caster->CastSpell(m_caster,36940,true);
+ break;
+ case 7:
+ // evil twin
+ m_caster->CastSpell(m_caster,23445,true);
+ break;
+ }
+ }
+ return;
+ }
+ // Dimensional Ripper - Area 52
+ case 36890:
+ {
+ if ( roll_chance_i(50) ) // 50% success
+ {
+ int32 rand_eff = urand(1,4);
+ switch ( rand_eff )
+ {
+ case 1:
+ // soul split - evil
+ m_caster->CastSpell(m_caster,36900,true);
+ break;
+ case 2:
+ // soul split - good
+ m_caster->CastSpell(m_caster,36901,true);
+ break;
+ case 3:
+ // Increase the size
+ m_caster->CastSpell(m_caster,36895,true);
+ break;
+ case 4:
+ // Transform
+ {
+ if (((Player*)m_caster)->GetTeam() == ALLIANCE )
+ m_caster->CastSpell(m_caster,36897,true);
+ else
+ m_caster->CastSpell(m_caster,36899,true);
+ break;
+ }
+ }
+ }
+ return;
+ }
+ }
+}
+
+void Spell::EffectApplyAura(uint32 i)
+{
+ if(!unitTarget)
+ return;
+
+ SpellImmuneList const& list = unitTarget->m_spellImmune[IMMUNITY_STATE];
+ for(SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr)
+ if(itr->type == m_spellInfo->EffectApplyAuraName[i])
+ return;
+
+ // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load)
+ if( !unitTarget->isAlive() && m_spellInfo->Id != 20584 && m_spellInfo->Id != 8326 &&
+ (unitTarget->GetTypeId()!=TYPEID_PLAYER || !((Player*)unitTarget)->GetSession()->PlayerLoading()) )
+ return;
+
+ Unit* caster = m_originalCasterGUID ? m_originalCaster : m_caster;
+ if(!caster)
+ return;
+
+ sLog.outDebug("Spell: Aura is: %u", m_spellInfo->EffectApplyAuraName[i]);
+
+ Aura* Aur = CreateAura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget, caster, m_CastItem);
+
+ // Now Reduce spell duration using data received at spell hit
+ int32 duration = Aur->GetAuraMaxDuration();
+ unitTarget->ApplyDiminishingToDuration(m_diminishGroup,duration,m_caster,m_diminishLevel);
+ Aur->setDiminishGroup(m_diminishGroup);
+
+ // if Aura removed and deleted, do not continue.
+ if(duration== 0 && !(Aur->IsPermanent()))
+ {
+ delete Aur;
+ return;
+ }
+
+ if(duration != Aur->GetAuraMaxDuration())
+ {
+ Aur->SetAuraMaxDuration(duration);
+ Aur->SetAuraDuration(duration);
+ }
+
+ bool added = unitTarget->AddAura(Aur);
+
+ // Aura not added and deleted in AddAura call;
+ if (!added)
+ return;
+
+ // found crash at character loading, broken pointer to Aur...
+ // Aur was deleted in AddAura()...
+ if(!Aur)
+ return;
+
+ // TODO Make a way so it works for every related spell!
+ if(unitTarget->GetTypeId()==TYPEID_PLAYER) // Negative buff should only be applied on players
+ {
+ uint32 spellId = 0;
+ if(m_spellInfo->CasterAuraStateNot==AURA_STATE_WEAKENED_SOUL || m_spellInfo->TargetAuraStateNot==AURA_STATE_WEAKENED_SOUL)
+ spellId = 6788; // Weakened Soul
+ else if(m_spellInfo->CasterAuraStateNot==AURA_STATE_FORBEARANCE || m_spellInfo->TargetAuraStateNot==AURA_STATE_FORBEARANCE)
+ spellId = 25771; // Forbearance
+ else if(m_spellInfo->CasterAuraStateNot==AURA_STATE_HYPOTHERMIA)
+ spellId = 41425; // Hypothermia
+ else if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages
+ spellId = 11196; // Recently Bandaged
+ else if( (m_spellInfo->AttributesEx & 0x20) && (m_spellInfo->AttributesEx2 & 0x20000) )
+ spellId = 23230; // Blood Fury - Healing Reduction
+
+ SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(spellId);
+ if (AdditionalSpellInfo)
+ {
+ // applied at target by target
+ Aura* AdditionalAura = CreateAura(AdditionalSpellInfo, 0, &m_currentBasePoints[0], unitTarget,unitTarget, 0);
+ unitTarget->AddAura(AdditionalAura);
+ sLog.outDebug("Spell: Additional Aura is: %u", AdditionalSpellInfo->EffectApplyAuraName[0]);
+ }
+ }
+
+ // Prayer of Mending (jump animation), we need formal caster instead original for correct animation
+ if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && (m_spellInfo->SpellFamilyFlags & 0x00002000000000LL))
+ m_caster->CastSpell(unitTarget,41637,true,NULL,Aur);
+}
+
+void Spell::EffectUnlearnSpecialization( uint32 i )
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *_player = (Player*)unitTarget;
+ uint32 spellToUnlearn = m_spellInfo->EffectTriggerSpell[i];
+
+ _player->removeSpell(spellToUnlearn);
+
+ sLog.outDebug( "Spell: Player %u have unlearned spell %u from NpcGUID: %u", _player->GetGUIDLow(), spellToUnlearn, m_caster->GetGUIDLow() );
+}
+
+void Spell::EffectPowerDrain(uint32 i)
+{
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ return;
+
+ Powers drain_power = Powers(m_spellInfo->EffectMiscValue[i]);
+
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+ if(unitTarget->getPowerType() != drain_power)
+ return;
+ if(damage < 0)
+ return;
+
+ uint32 curPower = unitTarget->GetPower(drain_power);
+
+ //add spell damage bonus
+ damage=m_caster->SpellDamageBonus(unitTarget,m_spellInfo,uint32(damage),SPELL_DIRECT_DAMAGE);
+
+ // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
+ uint32 power = damage;
+ if ( drain_power == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER )
+ power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power);
+
+ int32 new_damage;
+ if(curPower < power)
+ new_damage = curPower;
+ else
+ new_damage = power;
+
+ unitTarget->ModifyPower(drain_power,-new_damage);
+
+ if(drain_power == POWER_MANA)
+ {
+ float manaMultiplier = m_spellInfo->EffectMultipleValue[i];
+ if(manaMultiplier==0)
+ manaMultiplier = 1;
+
+ if(Player *modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, manaMultiplier);
+
+ int32 gain = int32(new_damage*manaMultiplier);
+
+ m_caster->ModifyPower(POWER_MANA,gain);
+ //send log
+ m_caster->SendEnergizeSpellLog(m_caster, m_spellInfo->Id,gain,POWER_MANA,false);
+ }
+}
+
+void Spell::EffectSendEvent(uint32 EffectIndex)
+{
+ if (m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->InBattleGround())
+ {
+ BattleGround* bg = ((Player *)m_caster)->GetBattleGround();
+ if(bg && bg->GetStatus() == STATUS_IN_PROGRESS)
+ {
+ switch(m_spellInfo->Id)
+ {
+ case 23333: // Pickup Horde Flag
+ /*do not uncomment .
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
+ sLog.outDebug("Send Event Horde Flag Picked Up");
+ break;
+ /* not used :
+ case 23334: // Drop Horde Flag
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerDroppedFlag((Player*)m_caster);
+ sLog.outDebug("Drop Horde Flag");
+ break;
+ */
+ case 23335: // Pickup Alliance Flag
+ /*do not uncomment ... (it will cause crash, because of null targetobject!) anyway this is a bad way to call that event, because it would cause recursion
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
+ sLog.outDebug("Send Event Alliance Flag Picked Up");
+ break;
+ /* not used :
+ case 23336: // Drop Alliance Flag
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerDroppedFlag((Player*)m_caster);
+ sLog.outDebug("Drop Alliance Flag");
+ break;
+ case 23385: // Alliance Flag Returns
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
+ sLog.outDebug("Alliance Flag Returned");
+ break;
+ case 23386: // Horde Flag Returns
+ if(bg->GetTypeID()==BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
+ sLog.outDebug("Horde Flag Returned");
+ break;*/
+ case 34976:
+ /*
+ if(bg->GetTypeID()==BATTLEGROUND_EY)
+ bg->EventPlayerClickedOnFlag((Player*)m_caster, this->gameObjTarget);
+ */
+ break;
+ default:
+ sLog.outDebug("Unknown spellid %u in BG event", m_spellInfo->Id);
+ break;
+ }
+ }
+ }
+ sLog.outDebug("Spell ScriptStart %u for spellid %u in EffectSendEvent ", m_spellInfo->EffectMiscValue[EffectIndex], m_spellInfo->Id);
+ sWorld.ScriptsStart(sEventScripts, m_spellInfo->EffectMiscValue[EffectIndex], m_caster, focusObject);
+}
+
+void Spell::EffectPowerBurn(uint32 i)
+{
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ return;
+
+ Powers powertype = Powers(m_spellInfo->EffectMiscValue[i]);
+
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+ if(unitTarget->getPowerType()!=powertype)
+ return;
+ if(damage < 0)
+ return;
+
+ int32 curPower = int32(unitTarget->GetPower(powertype));
+
+ // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
+ uint32 power = damage;
+ if ( powertype == POWER_MANA && unitTarget->GetTypeId() == TYPEID_PLAYER )
+ power -= ((Player*)unitTarget)->GetSpellCritDamageReduction(power);
+
+ int32 new_damage = (curPower < power) ? curPower : power;
+
+ unitTarget->ModifyPower(powertype,-new_damage);
+ float multiplier = m_spellInfo->EffectMultipleValue[i];
+
+ if(Player *modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
+
+ new_damage = int32(new_damage*multiplier);
+ m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage, m_IsTriggeredSpell, true);
+}
+
+void Spell::EffectHeal( uint32 /*i*/ )
+{
+ if( unitTarget && unitTarget->isAlive() && damage >= 0)
+ {
+ // Try to get original caster
+ Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
+
+ // Skip if m_originalCaster not available
+ if (!caster)
+ return;
+
+ int32 addhealth = damage;
+
+ // Vessel of the Naaru (Vial of the Sunwell trinket)
+ if (m_spellInfo->Id == 45064)
+ {
+ // Amount of heal - depends from stacked Holy Energy
+ int damageAmount = 0;
+ Unit::AuraList const& mDummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
+ if((*i)->GetId() == 45062)
+ damageAmount+=(*i)->GetModifier()->m_amount;
+ if (damageAmount)
+ m_caster->RemoveAurasDueToSpell(45062);
+
+ addhealth += damageAmount;
+ }
+ // Swiftmend - consumes Regrowth or Rejuvenation
+ else if (m_spellInfo->TargetAuraState == AURA_STATE_SWIFTMEND && unitTarget->HasAuraState(AURA_STATE_SWIFTMEND))
+ {
+ Unit::AuraList const& RejorRegr = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_HEAL);
+ // find most short by duration
+ Aura *targetAura = NULL;
+ 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) )
+ {
+ if(!targetAura || (*i)->GetAuraDuration() < targetAura->GetAuraDuration())
+ targetAura = *i;
+ }
+ }
+
+ if(!targetAura)
+ {
+ sLog.outError("Target(GUID:" I64FMTD ") has aurastate AURA_STATE_SWIFTMEND but no matching aura.", unitTarget->GetGUID());
+ return;
+ }
+ int idx = 0;
+ while(idx < 3)
+ {
+ if(targetAura->GetSpellProto()->EffectApplyAuraName[idx] == SPELL_AURA_PERIODIC_HEAL)
+ break;
+ idx++;
+ }
+
+ int32 tickheal = caster->SpellHealingBonus(targetAura->GetSpellProto(), targetAura->GetModifier()->m_amount, DOT, unitTarget);
+ int32 tickcount = GetSpellDuration(targetAura->GetSpellProto()) / targetAura->GetSpellProto()->EffectAmplitude[idx];
+ unitTarget->RemoveAurasDueToSpell(targetAura->GetId());
+
+ addhealth += tickheal * tickcount;
+ }
+ else
+ addhealth = caster->SpellHealingBonus(m_spellInfo, addhealth,HEAL, unitTarget);
+
+ bool crit = caster->isSpellCrit(unitTarget, m_spellInfo, m_spellSchoolMask, m_attackType);
+ if (crit)
+ addhealth = caster->SpellCriticalBonus(m_spellInfo, addhealth, unitTarget);
+ caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, crit);
+
+ int32 gain = unitTarget->ModifyHealth( int32(addhealth) );
+ unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
+
+ if(caster->GetTypeId()==TYPEID_PLAYER)
+ if(BattleGround *bg = ((Player*)caster)->GetBattleGround())
+ bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain);
+
+ // ignore item heals
+ if(m_CastItem)
+ return;
+
+ uint32 procHealer = PROC_FLAG_HEAL;
+ if (crit)
+ procHealer |= PROC_FLAG_CRIT_HEAL;
+
+ m_caster->ProcDamageAndSpell(unitTarget,procHealer,PROC_FLAG_HEALED,addhealth,SPELL_SCHOOL_MASK_NONE,m_spellInfo,m_IsTriggeredSpell);
+ }
+}
+
+void Spell::EffectHealPct( uint32 /*i*/ )
+{
+ if( unitTarget && unitTarget->isAlive() && damage >= 0)
+ {
+ // Try to get original caster
+ Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
+
+ // Skip if m_originalCaster not available
+ if (!caster)
+ return;
+
+ uint32 addhealth = unitTarget->GetMaxHealth() * damage / 100;
+ caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, false);
+
+ int32 gain = unitTarget->ModifyHealth( int32(addhealth) );
+ unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
+
+ if(caster->GetTypeId()==TYPEID_PLAYER)
+ if(BattleGround *bg = ((Player*)caster)->GetBattleGround())
+ bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain);
+ }
+}
+
+void Spell::EffectHealMechanical( uint32 /*i*/ )
+{
+ // Mechanic creature type should be correctly checked by targetCreatureType field
+ if( unitTarget && unitTarget->isAlive() && damage >= 0)
+ {
+ // Try to get original caster
+ Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
+
+ // Skip if m_originalCaster not available
+ if (!caster)
+ return;
+
+ uint32 addhealth = caster->SpellHealingBonus(m_spellInfo, uint32(damage), HEAL, unitTarget);
+ caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, false);
+ unitTarget->ModifyHealth( int32(damage) );
+ }
+}
+
+void Spell::EffectHealthLeech(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ if(damage < 0)
+ return;
+
+ sLog.outDebug("HealthLeech :%i", damage);
+
+ float multiplier = m_spellInfo->EffectMultipleValue[i];
+
+ if(Player *modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
+
+ int32 new_damage = int32(damage*multiplier);
+ uint32 curHealth = unitTarget->GetHealth();
+ new_damage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage, m_IsTriggeredSpell, true);
+ if(curHealth < new_damage)
+ new_damage = curHealth;
+
+ if(m_caster->isAlive())
+ {
+ new_damage = m_caster->SpellHealingBonus(m_spellInfo, new_damage, HEAL, m_caster);
+
+ m_caster->ModifyHealth(new_damage);
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ m_caster->SendHealSpellLog(m_caster, m_spellInfo->Id, uint32(new_damage));
+ }
+}
+
+void Spell::DoCreateItem(uint32 i, uint32 itemtype)
+{
+ if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)unitTarget;
+
+ uint32 newitemid = itemtype;
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( newitemid );
+ if(!pProto)
+ {
+ player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return;
+ }
+
+ uint32 num_to_add;
+
+ // TODO: maybe all this can be replaced by using correct calculated `damage` value
+ if(pProto->Class != ITEM_CLASS_CONSUMABLE || m_spellInfo->SpellFamilyName != SPELLFAMILY_MAGE)
+ {
+ int32 basePoints = m_currentBasePoints[i];
+ int32 randomPoints = m_spellInfo->EffectDieSides[i];
+ if (randomPoints)
+ num_to_add = basePoints + irand(1, randomPoints);
+ else
+ num_to_add = basePoints + 1;
+ }
+ else if (pProto->MaxCount == 1)
+ num_to_add = 1;
+ else if(player->getLevel() >= m_spellInfo->spellLevel)
+ {
+ int32 basePoints = m_currentBasePoints[i];
+ float pointPerLevel = m_spellInfo->EffectRealPointsPerLevel[i];
+ num_to_add = basePoints + 1 + uint32((player->getLevel() - m_spellInfo->spellLevel)*pointPerLevel);
+ }
+ else
+ num_to_add = 2;
+
+ if (num_to_add < 1)
+ num_to_add = 1;
+ if (num_to_add > pProto->Stackable)
+ num_to_add = pProto->Stackable;
+
+ // init items_count to 1, since 1 item will be created regardless of specialization
+ int items_count=1;
+ // the chance to create additional items
+ float additionalCreateChance=0.0f;
+ // the maximum number of created additional items
+ uint8 additionalMaxNum=0;
+ // get the chance and maximum number for creating extra items
+ if ( canCreateExtraItems(player, m_spellInfo->Id, additionalCreateChance, additionalMaxNum) )
+ {
+ // roll with this chance till we roll not to create or we create the max num
+ while ( roll_chance_f(additionalCreateChance) && items_count<=additionalMaxNum )
+ ++items_count;
+ }
+
+ // really will be created more items
+ num_to_add *= items_count;
+
+ // can the player store the new item?
+ ItemPosCountVec dest;
+ uint32 no_space = 0;
+ uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, newitemid, num_to_add, &no_space );
+ if( msg != EQUIP_ERR_OK )
+ {
+ // convert to possible store amount
+ if( msg == EQUIP_ERR_INVENTORY_FULL || msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
+ num_to_add -= no_space;
+ else
+ {
+ // if not created by another reason from full inventory or unique items amount limitation
+ player->SendEquipError( msg, NULL, NULL );
+ return;
+ }
+ }
+
+ if(num_to_add)
+ {
+ // create the new item and store it
+ Item* pItem = player->StoreNewItem( dest, newitemid, true, Item::GenerateItemRandomPropertyId(newitemid));
+
+ // was it successful? return error if not
+ if(!pItem)
+ {
+ player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return;
+ }
+
+ // set the "Crafted by ..." property of the item
+ if( pItem->GetProto()->Class != ITEM_CLASS_CONSUMABLE && pItem->GetProto()->Class != ITEM_CLASS_QUEST)
+ pItem->SetUInt32Value(ITEM_FIELD_CREATOR,player->GetGUIDLow());
+
+ // send info to the client
+ if(pItem)
+ player->SendNewItem(pItem, num_to_add, true, true);
+
+ // we succeeded in creating at least one item, so a levelup is possible
+ player->UpdateCraftSkill(m_spellInfo->Id);
+ }
+
+ // for battleground marks send by mail if not add all expected
+ if(no_space > 0 )
+ {
+ BattleGroundTypeId bgType;
+ switch(m_spellInfo->Id)
+ {
+ case SPELL_AV_MARK_WINNER:
+ case SPELL_AV_MARK_LOSER:
+ bgType = BATTLEGROUND_AV;
+ break;
+ case SPELL_WS_MARK_WINNER:
+ case SPELL_WS_MARK_LOSER:
+ bgType = BATTLEGROUND_WS;
+ break;
+ case SPELL_AB_MARK_WINNER:
+ case SPELL_AB_MARK_LOSER:
+ bgType = BATTLEGROUND_AB;
+ break;
+ default:
+ return;
+ }
+
+ if(BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(bgType))
+ bg->SendRewardMarkByMail(player,newitemid,no_space);
+ }
+}
+
+void Spell::EffectCreateItem(uint32 i)
+{
+ DoCreateItem(i,m_spellInfo->EffectItemType[i]);
+}
+
+void Spell::EffectPersistentAA(uint32 i)
+{
+ float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
+ if(Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius);
+
+ int32 duration = GetSpellDuration(m_spellInfo);
+ DynamicObject* dynObj = new DynamicObject;
+ if(!dynObj->Create(objmgr.GenerateLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, i, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, radius))
+ {
+ delete dynObj;
+ return;
+ }
+ dynObj->SetUInt32Value(OBJECT_FIELD_TYPE, 65);
+ dynObj->SetUInt32Value(GAMEOBJECT_DISPLAYID, 368003);
+ dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x01eeeeee);
+ m_caster->AddDynObject(dynObj);
+ MapManager::Instance().GetMap(dynObj->GetMapId(), dynObj)->Add(dynObj);
+}
+
+void Spell::EffectEnergize(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ return;
+
+ // Some level depends spells
+ int multipler = 0;
+ int level_diff = 0;
+ switch (m_spellInfo->Id)
+ {
+ // Restore Energy
+ case 9512:
+ level_diff = m_caster->getLevel() - 40;
+ multipler = 2;
+ break;
+ // Blood Fury
+ case 24571:
+ level_diff = m_caster->getLevel() - 60;
+ multipler = 10;
+ break;
+ // Burst of Energy
+ case 24532:
+ level_diff = m_caster->getLevel() - 60;
+ multipler = 4;
+ break;
+ default:
+ break;
+ }
+
+ if (level_diff > 0)
+ damage -= multipler * level_diff;
+
+ if(damage < 0)
+ return;
+
+ Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
+
+ if(unitTarget->GetMaxPower(power) == 0)
+ return;
+
+ unitTarget->ModifyPower(power,damage);
+ m_caster->SendEnergizeSpellLog(unitTarget, m_spellInfo->Id, damage, power);
+
+ // Mad Alchemist's Potion
+ if (m_spellInfo->Id == 45051)
+ {
+ // find elixirs on target
+ uint32 elixir_mask = 0;
+ Unit::AuraMap& Auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::iterator itr = Auras.begin(); itr != Auras.end(); ++itr)
+ {
+ uint32 spell_id = itr->second->GetId();
+ if(uint32 mask = spellmgr.GetSpellElixirMask(spell_id))
+ elixir_mask |= mask;
+ }
+
+ // get available elixir mask any not active type from battle/guardian (and flask if no any)
+ elixir_mask = (elixir_mask & ELIXIR_FLASK_MASK) ^ ELIXIR_FLASK_MASK;
+
+ // get all available elixirs by mask and spell level
+ std::vector<uint32> elixirs;
+ SpellElixirMap const& m_spellElixirs = spellmgr.GetSpellElixirMap();
+ for(SpellElixirMap::const_iterator itr = m_spellElixirs.begin(); itr != m_spellElixirs.end(); ++itr)
+ {
+ if (itr->second & elixir_mask)
+ {
+ if (itr->second & (ELIXIR_UNSTABLE_MASK | ELIXIR_SHATTRATH_MASK))
+ continue;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+ if (spellInfo && (spellInfo->spellLevel < m_spellInfo->spellLevel || spellInfo->spellLevel > unitTarget->getLevel()))
+ continue;
+
+ elixirs.push_back(itr->first);
+ }
+ }
+
+ if (!elixirs.empty())
+ {
+ // cast random elixir on target
+ uint32 rand_spell = urand(0,elixirs.size()-1);
+ m_caster->CastSpell(unitTarget,elixirs[rand_spell],true,m_CastItem);
+ }
+ }
+}
+
+void Spell::EffectEnergisePct(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS)
+ return;
+
+ Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
+
+ uint32 maxPower = unitTarget->GetMaxPower(power);
+ if(maxPower == 0)
+ return;
+
+ uint32 gain = damage * maxPower / 100;
+ unitTarget->ModifyPower(power, gain);
+ m_caster->SendEnergizeSpellLog(unitTarget, m_spellInfo->Id, damage, power);
+}
+
+void Spell::SendLoot(uint64 guid, LootType loottype)
+{
+ Player* player = (Player*)m_caster;
+ if (!player)
+ return;
+
+ if (gameObjTarget)
+ {
+ switch (gameObjTarget->GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ gameObjTarget->UseDoorOrButton();
+ sWorld.ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget);
+ return;
+
+ case GAMEOBJECT_TYPE_QUESTGIVER:
+ // start or end quest
+ player->PrepareQuestMenu(guid);
+ player->SendPreparedQuest(guid);
+ return;
+
+ case GAMEOBJECT_TYPE_SPELL_FOCUS:
+ // triggering linked GO
+ if(uint32 trapEntry = gameObjTarget->GetGOInfo()->spellFocus.linkedTrapId)
+ gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster);
+ return;
+
+ case GAMEOBJECT_TYPE_GOOBER:
+ // goober_scripts can be triggered if the player don't have the quest
+ if (gameObjTarget->GetGOInfo()->goober.eventId)
+ {
+ sLog.outDebug("Goober ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->goober.eventId,gameObjTarget->GetDBTableGUIDLow());
+ sWorld.ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->goober.eventId, player, gameObjTarget);
+ }
+
+ // cast goober spell
+ if (gameObjTarget->GetGOInfo()->goober.questId)
+ ///Quest require to be active for GO using
+ if(player->GetQuestStatus(gameObjTarget->GetGOInfo()->goober.questId) != QUEST_STATUS_INCOMPLETE)
+ return;
+
+ gameObjTarget->AddUniqueUse(player);
+ gameObjTarget->SetLootState(GO_JUST_DEACTIVATED);
+
+ //TODO? Objective counting called without spell check but with quest objective check
+ // if send spell id then this line will duplicate to spell casting call (double counting)
+ // So we or have this line and not required in quest_template have reqSpellIdN
+ // or must remove this line and required in DB have data in quest_template have reqSpellIdN for all quest using cases.
+ player->CastedCreatureOrGO(gameObjTarget->GetEntry(), gameObjTarget->GetGUID(), 0);
+
+ // triggering linked GO
+ if(uint32 trapEntry = gameObjTarget->GetGOInfo()->goober.linkedTrapId)
+ gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster);
+
+ return;
+
+ case GAMEOBJECT_TYPE_CHEST:
+ // TODO: possible must be moved to loot release (in different from linked triggering)
+ if (gameObjTarget->GetGOInfo()->chest.eventId)
+ {
+ sLog.outDebug("Chest ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->chest.eventId,gameObjTarget->GetDBTableGUIDLow());
+ sWorld.ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->chest.eventId, player, gameObjTarget);
+ }
+
+ // triggering linked GO
+ if(uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrapId)
+ gameObjTarget->TriggeringLinkedGameObject(trapEntry,m_caster);
+
+ // Don't return, let loots been taken
+ }
+ }
+
+ // Send loot
+ player->SendLoot(guid, loottype);
+}
+
+void Spell::EffectOpenLock(uint32 /*i*/)
+{
+ if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
+ {
+ sLog.outDebug( "WORLD: Open Lock - No Player Caster!");
+ return;
+ }
+
+ Player* player = (Player*)m_caster;
+
+ LootType loottype = LOOT_CORPSE;
+ uint32 lockId = 0;
+ uint64 guid = 0;
+
+ // Get lockId
+ if(gameObjTarget)
+ {
+ GameObjectInfo const* goInfo = gameObjTarget->GetGOInfo();
+ // Arathi Basin banner opening !
+ if( goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune ||
+ goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK )
+ {
+ //isAllowUseBattleGroundObject() already called in CanCast()
+ // in battleground check
+ if(BattleGround *bg = player->GetBattleGround())
+ {
+ // check if it's correct bg
+ if(bg && bg->GetTypeID() == BATTLEGROUND_AB)
+ bg->EventPlayerClickedOnFlag(player, gameObjTarget);
+ return;
+ }
+ }
+ else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND)
+ {
+ //isAllowUseBattleGroundObject() already called in CanCast()
+ // in battleground check
+ if(BattleGround *bg = player->GetBattleGround())
+ {
+ if(bg->GetTypeID() == BATTLEGROUND_EY)
+ bg->EventPlayerClickedOnFlag(player, gameObjTarget);
+ return;
+ }
+ }
+ lockId = gameObjTarget->GetLockId();
+ guid = gameObjTarget->GetGUID();
+ }
+ else if(itemTarget)
+ {
+ lockId = itemTarget->GetProto()->LockID;
+ guid = itemTarget->GetGUID();
+ }
+ else
+ {
+ sLog.outDebug( "WORLD: Open Lock - No GameObject/Item Target!");
+ return;
+ }
+
+ if(!lockId) // possible case for GO and maybe for items.
+ {
+ SendLoot(guid, loottype);
+ return;
+ }
+
+ // Get LockInfo
+ LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
+
+ if (!lockInfo)
+ {
+ sLog.outError( "Spell::EffectOpenLock: %s [guid = %u] has an unknown lockId: %u!",
+ (gameObjTarget ? "gameobject" : "item"), GUID_LOPART(guid), lockId);
+ SendCastResult(SPELL_FAILED_BAD_TARGETS);
+ return;
+ }
+
+ // check key
+ for(int i = 0; i < 5; ++i)
+ {
+ // type==1 This means lockInfo->key[i] is an item
+ if(lockInfo->keytype[i]==LOCK_KEY_ITEM && lockInfo->key[i] && m_CastItem && m_CastItem->GetEntry()==lockInfo->key[i])
+ {
+ SendLoot(guid, loottype);
+ return;
+ }
+ }
+
+ uint32 SkillId = 0;
+ // Check and skill-up skill
+ if( m_spellInfo->Effect[1] == SPELL_EFFECT_SKILL )
+ SkillId = m_spellInfo->EffectMiscValue[1];
+ // pickpocketing spells
+ else if( m_spellInfo->EffectMiscValue[0] == LOCKTYPE_PICKLOCK )
+ SkillId = SKILL_LOCKPICKING;
+
+ // skill bonus provided by casting spell (mostly item spells)
+ uint32 spellSkillBonus = uint32(m_currentBasePoints[0]+1);
+
+ uint32 reqSkillValue = lockInfo->requiredminingskill;
+
+ if(lockInfo->requiredlockskill) // required pick lock skill applying
+ {
+ if(SkillId != SKILL_LOCKPICKING) // wrong skill (cheating?)
+ {
+ SendCastResult(SPELL_FAILED_FIZZLE);
+ return;
+ }
+
+ reqSkillValue = lockInfo->requiredlockskill;
+ }
+ else if(SkillId == SKILL_LOCKPICKING) // apply picklock skill to wrong target
+ {
+ SendCastResult(SPELL_FAILED_BAD_TARGETS);
+ return;
+ }
+
+ if ( SkillId )
+ {
+ loottype = LOOT_SKINNING;
+ if ( player->GetSkillValue(SkillId) + spellSkillBonus < reqSkillValue )
+ {
+ SendCastResult(SPELL_FAILED_LOW_CASTLEVEL);
+ return;
+ }
+
+ // update skill if really known
+ uint32 SkillValue = player->GetPureSkillValue(SkillId);
+ if(SkillValue) // non only item base skill
+ {
+ if(gameObjTarget)
+ {
+ // Allow one skill-up until respawned
+ if ( !gameObjTarget->IsInSkillupList( player->GetGUIDLow() ) &&
+ player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue) )
+ gameObjTarget->AddToSkillupList( player->GetGUIDLow() );
+ }
+ else if(itemTarget)
+ {
+ // Do one skill-up
+ uint32 SkillValue = player->GetPureSkillValue(SkillId);
+ player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue);
+ }
+ }
+ }
+
+ SendLoot(guid, loottype);
+}
+
+void Spell::EffectSummonChangeItem(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *player = (Player*)m_caster;
+
+ // applied only to using item
+ if(!m_CastItem)
+ return;
+
+ // ... only to item in own inventory/bank/equip_slot
+ if(m_CastItem->GetOwnerGUID()!=player->GetGUID())
+ return;
+
+ uint32 newitemid = m_spellInfo->EffectItemType[i];
+ if(!newitemid)
+ return;
+
+ uint16 pos = m_CastItem->GetPos();
+
+ Item *pNewItem = Item::CreateItem( newitemid, 1, player);
+ if( !pNewItem )
+ return;
+
+ for(uint8 i= PERM_ENCHANTMENT_SLOT; i<=TEMP_ENCHANTMENT_SLOT; ++i)
+ {
+ if(m_CastItem->GetEnchantmentId(EnchantmentSlot(i)))
+ pNewItem->SetEnchantment(EnchantmentSlot(i), m_CastItem->GetEnchantmentId(EnchantmentSlot(i)), m_CastItem->GetEnchantmentDuration(EnchantmentSlot(i)), m_CastItem->GetEnchantmentCharges(EnchantmentSlot(i)));
+ }
+
+ if(m_CastItem->GetUInt32Value(ITEM_FIELD_DURABILITY) < m_CastItem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY))
+ {
+ double loosePercent = 1 - m_CastItem->GetUInt32Value(ITEM_FIELD_DURABILITY) / double(m_CastItem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY));
+ player->DurabilityLoss(pNewItem, loosePercent);
+ }
+
+ if( player->IsInventoryPos( pos ) )
+ {
+ ItemPosCountVec dest;
+ uint8 msg = player->CanStoreItem( m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true );
+ if( msg == EQUIP_ERR_OK )
+ {
+ player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true);
+
+ // prevent crash at access and unexpected charges counting with item update queue corrupt
+ if(m_CastItem==m_targets.getItemTarget())
+ m_targets.setItemTarget(NULL);
+
+ m_CastItem = NULL;
+
+ player->StoreItem( dest, pNewItem, true);
+ return;
+ }
+ }
+ else if( player->IsBankPos ( pos ) )
+ {
+ ItemPosCountVec dest;
+ uint8 msg = player->CanBankItem( m_CastItem->GetBagSlot(), m_CastItem->GetSlot(), dest, pNewItem, true );
+ if( msg == EQUIP_ERR_OK )
+ {
+ player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true);
+
+ // prevent crash at access and unexpected charges counting with item update queue corrupt
+ if(m_CastItem==m_targets.getItemTarget())
+ m_targets.setItemTarget(NULL);
+
+ m_CastItem = NULL;
+
+ player->BankItem( dest, pNewItem, true);
+ return;
+ }
+ }
+ else if( player->IsEquipmentPos ( pos ) )
+ {
+ uint16 dest;
+ uint8 msg = player->CanEquipItem( m_CastItem->GetSlot(), dest, pNewItem, true );
+ if( msg == EQUIP_ERR_OK )
+ {
+ player->DestroyItem(m_CastItem->GetBagSlot(), m_CastItem->GetSlot(),true);
+
+ // prevent crash at access and unexpected charges counting with item update queue corrupt
+ if(m_CastItem==m_targets.getItemTarget())
+ m_targets.setItemTarget(NULL);
+
+ m_CastItem = NULL;
+
+ player->EquipItem( dest, pNewItem, true);
+ player->AutoUnequipOffhandIfNeed();
+ return;
+ }
+ }
+
+ // fail
+ delete pNewItem;
+}
+
+void Spell::EffectOpenSecretSafe(uint32 i)
+{
+ EffectOpenLock(i); //no difference for now
+}
+
+void Spell::EffectProficiency(uint32 /*i*/)
+{
+ if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ Player *p_target = (Player*)unitTarget;
+
+ uint32 subClassMask = m_spellInfo->EquippedItemSubClassMask;
+ if(m_spellInfo->EquippedItemClass == 2 && !(p_target->GetWeaponProficiency() & subClassMask))
+ {
+ p_target->AddWeaponProficiency(subClassMask);
+ p_target->SendProficiency(uint8(0x02),p_target->GetWeaponProficiency());
+ }
+ if(m_spellInfo->EquippedItemClass == 4 && !(p_target->GetArmorProficiency() & subClassMask))
+ {
+ p_target->AddArmorProficiency(subClassMask);
+ p_target->SendProficiency(uint8(0x04),p_target->GetArmorProficiency());
+ }
+}
+
+void Spell::EffectApplyAreaAura(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ AreaAura* Aur = new AreaAura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget, m_caster, m_CastItem);
+ unitTarget->AddAura(Aur);
+}
+
+void Spell::EffectSummonType(uint32 i)
+{
+ switch(m_spellInfo->EffectMiscValueB[i])
+ {
+ case SUMMON_TYPE_GUARDIAN:
+ case SUMMON_TYPE_POSESSED:
+ case SUMMON_TYPE_POSESSED2:
+ EffectSummonGuardian(i);
+ break;
+ case SUMMON_TYPE_WILD:
+ EffectSummonWild(i);
+ break;
+ case SUMMON_TYPE_DEMON:
+ EffectSummonDemon(i);
+ break;
+ case SUMMON_TYPE_SUMMON:
+ EffectSummon(i);
+ break;
+ case SUMMON_TYPE_CRITTER:
+ case SUMMON_TYPE_CRITTER2:
+ EffectSummonCritter(i);
+ break;
+ case SUMMON_TYPE_TOTEM_SLOT1:
+ case SUMMON_TYPE_TOTEM_SLOT2:
+ case SUMMON_TYPE_TOTEM_SLOT3:
+ case SUMMON_TYPE_TOTEM_SLOT4:
+ case SUMMON_TYPE_TOTEM:
+ EffectSummonTotem(i);
+ break;
+ case SUMMON_TYPE_UNKNOWN1:
+ case SUMMON_TYPE_UNKNOWN2:
+ case SUMMON_TYPE_UNKNOWN3:
+ case SUMMON_TYPE_UNKNOWN4:
+ case SUMMON_TYPE_UNKNOWN5:
+ case SUMMON_TYPE_UNKNOWN6:
+ break;
+ default:
+ sLog.outError("EffectSummonType: Unhandled summon type %u", m_spellInfo->EffectMiscValueB[i]);
+ break;
+ }
+}
+
+void Spell::EffectSummon(uint32 i)
+{
+ if(m_caster->GetPetGUID())
+ return;
+
+ if(!unitTarget)
+ return;
+ uint32 pet_entry = m_spellInfo->EffectMiscValue[i];
+ if(!pet_entry)
+ return;
+ uint32 level = m_caster->getLevel();
+ Pet* spawnCreature = new Pet(SUMMON_PET);
+
+ if(spawnCreature->LoadPetFromDB(m_caster,pet_entry))
+ {
+ // set timer for unsummon
+ int32 duration = GetSpellDuration(m_spellInfo);
+ if(duration > 0)
+ spawnCreature->SetDuration(duration);
+
+ return;
+ }
+
+ Map *map = m_caster->GetMap();
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!spawnCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_PET),map,m_spellInfo->EffectMiscValue[i], pet_number))
+ {
+ sLog.outErrorDb("Spell::EffectSummon: no such creature entry %u",m_spellInfo->EffectMiscValue[i]);
+ delete spawnCreature;
+ return;
+ }
+
+ // Summon in dest location
+ float x,y,z;
+ if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ x = m_targets.m_destX;
+ y = m_targets.m_destY;
+ z = m_targets.m_destZ;
+ }
+ else
+ m_caster->GetClosePoint(x,y,z,spawnCreature->GetObjectSize());
+
+ spawnCreature->Relocate(x,y,z,-m_caster->GetOrientation());
+
+ if(!spawnCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", spawnCreature->GetGUIDLow(), spawnCreature->GetEntry(), spawnCreature->GetPositionX(), spawnCreature->GetPositionY());
+ delete spawnCreature;
+ return;
+ }
+
+ // set timer for unsummon
+ int32 duration = GetSpellDuration(m_spellInfo);
+ if(duration > 0)
+ spawnCreature->SetDuration(duration);
+
+ spawnCreature->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID());
+ spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS , 0);
+ spawnCreature->setPowerType(POWER_MANA);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
+ spawnCreature->SetUInt32Value(UNIT_FIELD_FLAGS,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
+ spawnCreature->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
+ spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
+ spawnCreature->InitStatsForLevel(level);
+
+ spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false);
+
+ spawnCreature->AIM_Initialize();
+ spawnCreature->InitPetCreateSpells();
+ spawnCreature->SetHealth(spawnCreature->GetMaxHealth());
+ spawnCreature->SetPower(POWER_MANA, spawnCreature->GetMaxPower(POWER_MANA));
+
+ std::string name = m_caster->GetName();
+ name.append(petTypeSuffix[spawnCreature->getPetType()]);
+ spawnCreature->SetName( name );
+
+ map->Add((Creature*)spawnCreature);
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ m_caster->SetPet(spawnCreature);
+ spawnCreature->GetCharmInfo()->SetReactState( REACT_DEFENSIVE );
+ spawnCreature->SavePetToDB(PET_SAVE_AS_CURRENT);
+ ((Player*)m_caster)->PetSpellInitialize();
+ }
+}
+
+void Spell::EffectLearnSpell(uint32 i)
+{
+ if(!unitTarget)
+ return;
+
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ {
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ EffectLearnPetSpell(i);
+
+ return;
+ }
+
+ Player *player = (Player*)unitTarget;
+
+ uint32 spellToLearn = (m_spellInfo->Id==SPELL_ID_GENERIC_LEARN) ? damage : m_spellInfo->EffectTriggerSpell[i];
+ player->learnSpell(spellToLearn);
+
+ sLog.outDebug( "Spell: Player %u have learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() );
+}
+
+void Spell::EffectDispel(uint32 i)
+{
+ if(!unitTarget)
+ return;
+
+ // Fill possible dispell list
+ std::vector <Aura *> dispel_list;
+
+ // Create dispel mask by dispel type
+ uint32 dispel_type = m_spellInfo->EffectMiscValue[i];
+ uint32 dispelMask = GetDispellMask( DispelType(dispel_type) );
+ Unit::AuraMap const& auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ Aura *aur = (*itr).second;
+ if (aur && (1<<aur->GetSpellProto()->Dispel) & dispelMask)
+ {
+ if(aur->GetSpellProto()->Dispel == DISPEL_MAGIC)
+ {
+ bool positive = true;
+ if (!aur->IsPositive())
+ positive = false;
+ else
+ positive = (aur->GetSpellProto()->AttributesEx & SPELL_ATTR_EX_NEGATIVE)==0;
+
+ // do not remove positive auras if friendly target
+ // negative auras if non-friendly target
+ if(positive == unitTarget->IsFriendlyTo(m_caster))
+ continue;
+ }
+ // Add aura to dispel list
+ dispel_list.push_back(aur);
+ }
+ }
+ // Ok if exist some buffs for dispel try dispel it
+ if (!dispel_list.empty())
+ {
+ std::list < std::pair<uint32,uint64> > success_list;// (spell_id,casterGuid)
+ std::list < uint32 > fail_list; // spell_id
+ int32 list_size = dispel_list.size();
+ // Dispell N = damage buffs (or while exist buffs for dispel)
+ for (int32 count=0; count < damage && list_size > 0; ++count)
+ {
+ // Random select buff for dispel
+ Aura *aur = dispel_list[urand(0, list_size-1)];
+
+ SpellEntry const* spellInfo = aur->GetSpellProto();
+ // Base dispel chance
+ // TODO: possible chance depend from spell level??
+ int32 miss_chance = 0;
+ // Apply dispel mod from aura caster
+ if (Unit *caster = aur->GetCaster())
+ {
+ if ( Player* modOwner = caster->GetSpellModOwner() )
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_RESIST_DISPEL_CHANCE, miss_chance, this);
+ }
+ // Try dispel
+ if (roll_chance_i(miss_chance))
+ fail_list.push_back(aur->GetId());
+ else
+ success_list.push_back(std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID()));
+ // Remove buff from list for prevent doubles
+ for (std::vector<Aura *>::iterator j = dispel_list.begin(); j != dispel_list.end(); )
+ {
+ Aura *dispeled = *j;
+ if (dispeled->GetId() == aur->GetId() && dispeled->GetCasterGUID() == aur->GetCasterGUID())
+ {
+ j = dispel_list.erase(j);
+ --list_size;
+ }
+ else
+ ++j;
+ }
+ }
+ // Send success log and really remove auras
+ if (!success_list.empty())
+ {
+ int32 count = success_list.size();
+ WorldPacket data(SMSG_SPELLDISPELLOG, 8+8+4+1+4+count*5);
+ data.append(unitTarget->GetPackGUID()); // Victim GUID
+ data.append(m_caster->GetPackGUID()); // Caster GUID
+ data << uint32(m_spellInfo->Id); // Dispell spell id
+ data << uint8(0); // not used
+ data << uint32(count); // count
+ for (std::list<std::pair<uint32,uint64> >::iterator j = success_list.begin(); j != success_list.end(); ++j)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first);
+ data << uint32(spellInfo->Id); // Spell Id
+ data << uint8(0); // 0 - dispeled !=0 cleansed
+ unitTarget->RemoveAurasDueToSpellByDispel(spellInfo->Id, j->second, m_caster);
+ }
+ m_caster->SendMessageToSet(&data, true);
+
+ // On succes dispel
+ // Devour Magic
+ if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == 12)
+ {
+ uint32 heal_spell = 0;
+ switch (m_spellInfo->Id)
+ {
+ case 19505: heal_spell = 19658; break;
+ case 19731: heal_spell = 19732; break;
+ case 19734: heal_spell = 19733; break;
+ case 19736: heal_spell = 19735; break;
+ case 27276: heal_spell = 27278; break;
+ case 27277: heal_spell = 27279; break;
+ default:
+ sLog.outDebug("Spell for Devour Magic %d not handled in Spell::EffectDispel", m_spellInfo->Id);
+ break;
+ }
+ if (heal_spell)
+ m_caster->CastSpell(m_caster, heal_spell, true);
+ }
+ }
+ // Send fail log to client
+ if (!fail_list.empty())
+ {
+ // Failed to dispell
+ WorldPacket data(SMSG_DISPEL_FAILED, 8+8+4+4*fail_list.size());
+ data << uint64(m_caster->GetGUID()); // Caster GUID
+ data << uint64(unitTarget->GetGUID()); // Victim GUID
+ data << uint32(m_spellInfo->Id); // Dispell spell id
+ for (std::list< uint32 >::iterator j = fail_list.begin(); j != fail_list.end(); ++j)
+ data << uint32(*j); // Spell Id
+ m_caster->SendMessageToSet(&data, true);
+ }
+ }
+}
+
+void Spell::EffectDualWield(uint32 /*i*/)
+{
+ if (unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->SetCanDualWield(true);
+}
+
+void Spell::EffectPull(uint32 /*i*/)
+{
+ // TODO: create a proper pull towards distract spell center for distract
+ sLog.outDebug("WORLD: Spell Effect DUMMY");
+}
+
+void Spell::EffectDistract(uint32 /*i*/)
+{
+ // Check for possible target
+ if (!unitTarget || unitTarget->isInCombat())
+ return;
+
+ // target must be OK to do this
+ 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);
+
+ if ( unitTarget->GetTypeId() == TYPEID_PLAYER )
+ {
+ // For players just turn them
+ WorldPacket data;
+ ((Player*)unitTarget)->BuildTeleportAckMsg(&data, unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), angle);
+ ((Player*)unitTarget)->GetSession()->SendPacket( &data );
+ ((Player*)unitTarget)->SetPosition(unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), angle, false);
+ }
+ else
+ {
+ // Set creature Distracted, Stop it, And turn it
+ unitTarget->SetOrientation(angle);
+ unitTarget->StopMoving();
+ unitTarget->GetMotionMaster()->MoveDistract(damage*1000);
+ }
+}
+
+void Spell::EffectPickPocket(uint32 /*i*/)
+{
+ if( m_caster->GetTypeId() != TYPEID_PLAYER )
+ return;
+
+ // victim must be creature and attackable
+ if( !unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->IsFriendlyTo(unitTarget) )
+ return;
+
+ // victim have to be alive and humanoid or undead
+ if( unitTarget->isAlive() && (unitTarget->GetCreatureTypeMask() &CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) != 0)
+ {
+ int32 chance = 10 + int32(m_caster->getLevel()) - int32(unitTarget->getLevel());
+
+ if (chance > irand(0, 19))
+ {
+ // Stealing successful
+ //sLog.outDebug("Sending loot from pickpocket");
+ ((Player*)m_caster)->SendLoot(unitTarget->GetGUID(),LOOT_PICKPOCKETING);
+ }
+ else
+ {
+ // Reveal action + get attack
+ m_caster->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ if (((Creature*)unitTarget)->AI())
+ ((Creature*)unitTarget)->AI()->AttackStart(m_caster);
+ }
+ }
+}
+
+void Spell::EffectAddFarsight(uint32 i)
+{
+ float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+ int32 duration = GetSpellDuration(m_spellInfo);
+ DynamicObject* dynObj = new DynamicObject;
+ if(!dynObj->Create(objmgr.GenerateLowGuid(HIGHGUID_DYNAMICOBJECT), m_caster, m_spellInfo->Id, i, m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, duration, radius))
+ {
+ delete dynObj;
+ return;
+ }
+ dynObj->SetUInt32Value(OBJECT_FIELD_TYPE, 65);
+ dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x80000002);
+ m_caster->AddDynObject(dynObj);
+ MapManager::Instance().GetMap(dynObj->GetMapId(), dynObj)->Add(dynObj);
+ m_caster->SetUInt64Value(PLAYER_FARSIGHT, dynObj->GetGUID());
+}
+
+void Spell::EffectSummonWild(uint32 i)
+{
+ uint32 creature_entry = m_spellInfo->EffectMiscValue[i];
+ if(!creature_entry)
+ return;
+
+ uint32 level = m_caster->getLevel();
+
+ // level of creature summoned using engineering item based at engineering skill level
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && m_CastItem)
+ {
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if(proto && proto->RequiredSkill == SKILL_ENGINERING)
+ {
+ uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINERING);
+ if(skill202)
+ {
+ level = skill202/5;
+ }
+ }
+ }
+
+ // select center of summon position
+ float center_x = m_targets.m_destX;
+ float center_y = m_targets.m_destY;
+ float center_z = m_targets.m_destZ;
+
+ float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
+ int32 amount = damage > 0 ? damage : 1;
+
+ for(int32 count = 0; count < amount; ++count)
+ {
+ float px, py, pz;
+ // If dest location if present
+ if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ // Summon 1 unit in dest location
+ if (count == 0)
+ {
+ px = m_targets.m_destX;
+ py = m_targets.m_destY;
+ pz = m_targets.m_destZ;
+ }
+ // Summon in random point all other units if location present
+ else
+ m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz);
+ }
+ // Summon if dest location not present near caster
+ else
+ m_caster->GetClosePoint(px,py,pz,3.0f);
+
+ int32 duration = GetSpellDuration(m_spellInfo);
+
+ TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_OR_DEAD_DESPAWN;
+
+ m_caster->SummonCreature(creature_entry,px,py,pz,m_caster->GetOrientation(),summonType,duration);
+ }
+}
+
+void Spell::EffectSummonGuardian(uint32 i)
+{
+ uint32 pet_entry = m_spellInfo->EffectMiscValue[i];
+ if(!pet_entry)
+ return;
+
+ // Jewelery statue case (totem like)
+ if(m_spellInfo->SpellIconID==2056)
+ {
+ EffectSummonTotem(i);
+ return;
+ }
+
+ // set timer for unsummon
+ int32 duration = GetSpellDuration(m_spellInfo);
+
+ // Search old Guardian only for players (if casted spell not have duration or cooldown)
+ // FIXME: some guardians have control spell applied and controlled by player and anyway player can't summon in this time
+ // so this code hack in fact
+ if( m_caster->GetTypeId() == TYPEID_PLAYER && (duration <= 0 || GetSpellRecoveryTime(m_spellInfo)==0) )
+ if(((Player*)m_caster)->HasGuardianWithEntry(pet_entry))
+ return; // find old guardian, ignore summon
+
+ // in another case summon new
+ uint32 level = m_caster->getLevel();
+
+ // level of pet summoned using engineering item based at engineering skill level
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && m_CastItem)
+ {
+ ItemPrototype const *proto = m_CastItem->GetProto();
+ if(proto && proto->RequiredSkill == SKILL_ENGINERING)
+ {
+ uint16 skill202 = ((Player*)m_caster)->GetSkillValue(SKILL_ENGINERING);
+ if(skill202)
+ {
+ level = skill202/5;
+ }
+ }
+ }
+
+ // select center of summon position
+ float center_x = m_targets.m_destX;
+ float center_y = m_targets.m_destY;
+ float center_z = m_targets.m_destZ;
+
+ float radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
+ int32 amount = damage > 0 ? damage : 1;
+
+ for(int32 count = 0; count < amount; ++count)
+ {
+ Pet* spawnCreature = new Pet(GUARDIAN_PET);
+
+ Map *map = m_caster->GetMap();
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!spawnCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map,m_spellInfo->EffectMiscValue[i], pet_number))
+ {
+ sLog.outError("no such creature entry %u",m_spellInfo->EffectMiscValue[i]);
+ delete spawnCreature;
+ return;
+ }
+
+ float px, py, pz;
+ // If dest location if present
+ if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ // Summon 1 unit in dest location
+ if (count == 0)
+ {
+ px = m_targets.m_destX;
+ py = m_targets.m_destY;
+ pz = m_targets.m_destZ;
+ }
+ // Summon in random point all other units if location present
+ else
+ m_caster->GetRandomPoint(center_x,center_y,center_z,radius,px,py,pz);
+ }
+ // Summon if dest location not present near caster
+ else
+ m_caster->GetClosePoint(px,py,pz,spawnCreature->GetObjectSize());
+
+ spawnCreature->Relocate(px,py,pz,m_caster->GetOrientation());
+
+ if(!spawnCreature->IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %d Y: ^%d)", spawnCreature->GetGUIDLow(), spawnCreature->GetEntry(), spawnCreature->GetPositionX(), spawnCreature->GetPositionY());
+ delete spawnCreature;
+ return;
+ }
+
+ if(duration > 0)
+ spawnCreature->SetDuration(duration);
+
+ spawnCreature->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID());
+ spawnCreature->setPowerType(POWER_MANA);
+ spawnCreature->SetUInt32Value(UNIT_NPC_FLAGS , 0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
+ spawnCreature->SetUInt32Value(UNIT_FIELD_FLAGS,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
+ spawnCreature->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
+ spawnCreature->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
+ spawnCreature->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
+ spawnCreature->InitStatsForLevel(level);
+ spawnCreature->GetCharmInfo()->SetPetNumber(pet_number, false);
+
+ spawnCreature->AIM_Initialize();
+
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)m_caster)->AddGuardian(spawnCreature);
+
+ map->Add((Creature*)spawnCreature);
+ }
+}
+
+void Spell::EffectTeleUnitsFaceCaster(uint32 i)
+{
+ if(!unitTarget)
+ return;
+
+ if(unitTarget->isInFlight())
+ return;
+
+ uint32 mapid = m_caster->GetMapId();
+ float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
+ float fx,fy,fz;
+ m_caster->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis);
+
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, -m_caster->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
+ else
+ MapManager::Instance().GetMap(mapid, m_caster)->CreatureRelocation((Creature*)m_caster, fx, fy, fz, -m_caster->GetOrientation());
+}
+
+void Spell::EffectLearnSkill(uint32 i)
+{
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(damage < 0)
+ return;
+
+ uint32 skillid = m_spellInfo->EffectMiscValue[i];
+ uint16 skillval = ((Player*)unitTarget)->GetPureSkillValue(skillid);
+ ((Player*)unitTarget)->SetSkill(skillid, skillval?skillval:1, damage*75);
+}
+
+void Spell::EffectAddHonor(uint32 /*i*/)
+{
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ sLog.outDebug("SpellEffect::AddHonor called for spell_id %u , that rewards %d honor points to player: %u", m_spellInfo->Id, this->damage, ((Player*)unitTarget)->GetGUIDLow());
+
+ // TODO: find formula for honor reward based on player's level!
+
+ // now fixed only for level 70 players:
+ if (((Player*)unitTarget)->getLevel() == 70)
+ ((Player*)unitTarget)->RewardHonor(NULL, 1, this->damage);
+}
+
+void Spell::EffectTradeSkill(uint32 /*i*/)
+{
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ // uint32 skillid = m_spellInfo->EffectMiscValue[i];
+ // uint16 skillmax = ((Player*)unitTarget)->(skillid);
+ // ((Player*)unitTarget)->SetSkill(skillid,skillval?skillval:1,skillmax+75);
+}
+
+void Spell::EffectEnchantItemPerm(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if (!itemTarget)
+ return;
+
+ Player* p_caster = (Player*)m_caster;
+
+ p_caster->UpdateCraftSkill(m_spellInfo->Id);
+
+ if (m_spellInfo->EffectMiscValue[i])
+ {
+ uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
+
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return;
+
+ // item can be in trade slot and have owner diff. from caster
+ Player* item_owner = itemTarget->GetOwner();
+ if(!item_owner)
+ return;
+
+ if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ sLog.outCommand("GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)",
+ p_caster->GetName(),p_caster->GetSession()->GetAccountId(),
+ itemTarget->GetProto()->Name1,itemTarget->GetEntry(),
+ item_owner->GetName(),item_owner->GetSession()->GetAccountId());
+
+ // remove old enchanting before applying new if equipped
+ item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,false);
+
+ itemTarget->SetEnchantment(PERM_ENCHANTMENT_SLOT, enchant_id, 0, 0);
+
+ // add new enchanting if equipped
+ item_owner->ApplyEnchantment(itemTarget,PERM_ENCHANTMENT_SLOT,true);
+ }
+}
+
+void Spell::EffectEnchantItemTmp(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* p_caster = (Player*)m_caster;
+
+ if(!itemTarget)
+ return;
+
+ uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
+
+ // Shaman Rockbiter Weapon
+ if(i==0 && m_spellInfo->Effect[1]==SPELL_EFFECT_DUMMY)
+ {
+ int32 enchnting_damage = m_currentBasePoints[1]+1;
+
+ // enchanting id selected by calculated damage-per-sec stored in Effect[1] base value
+ // with already applied percent bonus from Elemental Weapons talent
+ // Note: damage calculated (correctly) with rounding int32(float(v)) but
+ // RW enchantments applied damage int32(float(v)+0.5), this create 0..1 difference sometime
+ switch(enchnting_damage)
+ {
+ // Rank 1
+ case 2: enchant_id = 29; break; // 0% [ 7% == 2, 14% == 2, 20% == 2]
+ // Rank 2
+ case 4: enchant_id = 6; break; // 0% [ 7% == 4, 14% == 4]
+ case 5: enchant_id = 3025; break; // 20%
+ // Rank 3
+ case 6: enchant_id = 1; break; // 0% [ 7% == 6, 14% == 6]
+ case 7: enchant_id = 3027; break; // 20%
+ // Rank 4
+ case 9: enchant_id = 3032; break; // 0% [ 7% == 6]
+ case 10: enchant_id = 503; break; // 14%
+ case 11: enchant_id = 3031; break; // 20%
+ // Rank 5
+ case 15: enchant_id = 3035; break; // 0%
+ case 16: enchant_id = 1663; break; // 7%
+ case 17: enchant_id = 3033; break; // 14%
+ case 18: enchant_id = 3034; break; // 20%
+ // Rank 6
+ case 28: enchant_id = 3038; break; // 0%
+ case 29: enchant_id = 683; break; // 7%
+ case 31: enchant_id = 3036; break; // 14%
+ case 33: enchant_id = 3037; break; // 20%
+ // Rank 7
+ case 40: enchant_id = 3041; break; // 0%
+ case 42: enchant_id = 1664; break; // 7%
+ case 45: enchant_id = 3039; break; // 14%
+ case 48: enchant_id = 3040; break; // 20%
+ // Rank 8
+ case 49: enchant_id = 3044; break; // 0%
+ case 52: enchant_id = 2632; break; // 7%
+ case 55: enchant_id = 3042; break; // 14%
+ case 58: enchant_id = 3043; break; // 20%
+ // Rank 9
+ case 62: enchant_id = 2633; break; // 0%
+ case 66: enchant_id = 3018; break; // 7%
+ case 70: enchant_id = 3019; break; // 14%
+ case 74: enchant_id = 3020; break; // 20%
+ default:
+ sLog.outError("Spell::EffectEnchantItemTmp: Damage %u not handled in S'RW",enchnting_damage);
+ return;
+ }
+ }
+
+ if (!enchant_id)
+ {
+ sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have 0 as enchanting id",m_spellInfo->Id,i);
+ return;
+ }
+
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ {
+ sLog.outError("Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have not existed enchanting id %u ",m_spellInfo->Id,i,enchant_id);
+ return;
+ }
+
+ // select enchantment duration
+ uint32 duration;
+
+ // rogue family enchantments exception by duration
+ if(m_spellInfo->Id==38615)
+ duration = 1800; // 30 mins
+ // other rogue family enchantments always 1 hour (some have spell damage=0, but some have wrong data in EffBasePoints)
+ else if(m_spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE)
+ duration = 3600; // 1 hour
+ // shaman family enchantments
+ else if(m_spellInfo->SpellFamilyName==SPELLFAMILY_SHAMAN)
+ duration = 1800; // 30 mins
+ // other cases with this SpellVisual already selected
+ else if(m_spellInfo->SpellVisual==215)
+ duration = 1800; // 30 mins
+ // some fishing pole bonuses
+ else if(m_spellInfo->SpellVisual==563)
+ duration = 600; // 10 mins
+ // shaman rockbiter enchantments
+ else if(m_spellInfo->SpellVisual==0)
+ duration = 1800; // 30 mins
+ else if(m_spellInfo->Id==29702)
+ duration = 300; // 5 mins
+ else if(m_spellInfo->Id==37360)
+ duration = 300; // 5 mins
+ // default case
+ else
+ duration = 3600; // 1 hour
+
+ // item can be in trade slot and have owner diff. from caster
+ Player* item_owner = itemTarget->GetOwner();
+ if(!item_owner)
+ return;
+
+ if(item_owner!=p_caster && p_caster->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
+ sLog.outCommand("GM %s (Account: %u) enchanting(temp): %s (Entry: %d) for player: %s (Account: %u)",
+ p_caster->GetName(),p_caster->GetSession()->GetAccountId(),
+ itemTarget->GetProto()->Name1,itemTarget->GetEntry(),
+ item_owner->GetName(),item_owner->GetSession()->GetAccountId());
+
+ // remove old enchanting before applying new if equipped
+ item_owner->ApplyEnchantment(itemTarget,TEMP_ENCHANTMENT_SLOT,false);
+
+ itemTarget->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, duration*1000, 0);
+
+ // add new enchanting if equipped
+ item_owner->ApplyEnchantment(itemTarget,TEMP_ENCHANTMENT_SLOT,true);
+}
+
+void Spell::EffectTameCreature(uint32 /*i*/)
+{
+ if(m_caster->GetPetGUID())
+ return;
+
+ if(!unitTarget)
+ return;
+
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ return;
+
+ Creature* creatureTarget = (Creature*)unitTarget;
+
+ if(creatureTarget->isPet())
+ return;
+
+ if(m_caster->getClass() == CLASS_HUNTER)
+ {
+ // cast finish successfully
+ //SendChannelUpdate(0);
+ finish();
+
+ Pet* pet = new Pet(HUNTER_PET);
+
+ if(!pet->CreateBaseAtCreature(creatureTarget))
+ {
+ delete pet;
+ return;
+ }
+
+ creatureTarget->setDeathState(JUST_DIED);
+ creatureTarget->RemoveCorpse();
+ creatureTarget->SetHealth(0); // just for nice GM-mode view
+
+ pet->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, m_caster->GetGUID());
+ pet->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
+ pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
+ pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
+ if(!pet->InitStatsForLevel(creatureTarget->getLevel()))
+ {
+ sLog.outError("ERROR: InitStatsForLevel() in EffectTameCreature failed! Pet deleted.");
+ delete pet;
+ return;
+ }
+
+ // prepare visual effect for levelup
+ pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel()-1);
+
+ pet->GetCharmInfo()->SetPetNumber(objmgr.GeneratePetNumber(), true);
+ // this enables pet details window (Shift+P)
+ pet->AIM_Initialize();
+ pet->InitPetCreateSpells();
+ pet->SetHealth(pet->GetMaxHealth());
+
+ MapManager::Instance().GetMap(pet->GetMapId(), pet)->Add((Creature*)pet);
+
+ // visual effect for levelup
+ pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel());
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ m_caster->SetPet(pet);
+ pet->SavePetToDB(PET_SAVE_AS_CURRENT);
+ ((Player*)m_caster)->PetSpellInitialize();
+ }
+ }
+}
+
+void Spell::EffectSummonPet(uint32 i)
+{
+ uint32 petentry = m_spellInfo->EffectMiscValue[i];
+
+ Pet *OldSummon = m_caster->GetPet();
+
+ // if pet requested type already exist
+ if( OldSummon )
+ {
+ if(petentry == 0 || OldSummon->GetEntry() == petentry)
+ {
+ // pet in corpse state can't be summoned
+ if( OldSummon->isDead() )
+ return;
+
+ MapManager::Instance().GetMap(OldSummon->GetMapId(), OldSummon)->Remove((Creature*)OldSummon,false);
+ OldSummon->SetMapId(m_caster->GetMapId());
+
+ float px, py, pz;
+ m_caster->GetClosePoint(px, py, pz, OldSummon->GetObjectSize());
+
+ OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation());
+ MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)->Add((Creature*)OldSummon);
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled() )
+ {
+ ((Player*)m_caster)->PetSpellInitialize();
+ }
+ return;
+ }
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)m_caster)->RemovePet(OldSummon,(OldSummon->getPetType()==HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT),false);
+ else
+ return;
+ }
+
+ Pet* NewSummon = new Pet;
+
+ // petentry==0 for hunter "call pet" (current pet summoned if any)
+ if(NewSummon->LoadPetFromDB(m_caster,petentry))
+ {
+ if(NewSummon->getPetType()==SUMMON_PET)
+ {
+ // Remove Demonic Sacrifice auras (known pet)
+ Unit::AuraList const& auraClassScripts = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();)
+ {
+ if((*itr)->GetModifier()->m_miscvalue==2228)
+ {
+ m_caster->RemoveAurasDueToSpell((*itr)->GetId());
+ itr = auraClassScripts.begin();
+ }
+ else
+ ++itr;
+ }
+ }
+
+ return;
+ }
+
+ // not error in case fail hunter call pet
+ if(!petentry)
+ {
+ delete NewSummon;
+ return;
+ }
+
+ CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(petentry);
+
+ if(!cInfo)
+ {
+ sLog.outError("EffectSummonPet: creature entry %u not found.",petentry);
+ delete NewSummon;
+ return;
+ }
+
+ Map *map = m_caster->GetMap();
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!NewSummon->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map, petentry, pet_number))
+ {
+ delete NewSummon;
+ return;
+ }
+
+ float px, py, pz;
+ m_caster->GetClosePoint(px, py, pz, NewSummon->GetObjectSize());
+
+ NewSummon->Relocate(px, py, pz, m_caster->GetOrientation());
+
+ if(!NewSummon->IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", NewSummon->GetGUIDLow(), NewSummon->GetEntry(), NewSummon->GetPositionX(), NewSummon->GetPositionY());
+ delete NewSummon;
+ return;
+ }
+
+ uint32 petlevel = m_caster->getLevel();
+ NewSummon->setPetType(SUMMON_PET);
+
+ uint32 faction = m_caster->getFaction();
+ if(m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isTotem())
+ {
+ Unit* owner = ((Totem*)m_caster)->GetOwner();
+ if(owner)
+ faction = owner->getFaction();
+ NewSummon->GetCharmInfo()->SetReactState(REACT_AGGRESSIVE);
+ }
+
+ NewSummon->SetUInt64Value(UNIT_FIELD_SUMMONEDBY, m_caster->GetGUID());
+ NewSummon->SetUInt64Value(UNIT_FIELD_CREATEDBY, m_caster->GetGUID());
+ NewSummon->SetUInt32Value(UNIT_NPC_FLAGS , 0);
+ NewSummon->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, faction);
+ NewSummon->SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
+ NewSummon->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
+ NewSummon->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,time(NULL));
+ NewSummon->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
+ NewSummon->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
+ NewSummon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
+ NewSummon->GetCharmInfo()->SetPetNumber(pet_number, true);
+ // this enables pet details window (Shift+P)
+
+ // this enables popup window (pet dismiss, cancel), hunter pet additional flags set later
+ NewSummon->SetUInt32Value(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE);
+
+ NewSummon->InitStatsForLevel( petlevel);
+ NewSummon->InitPetCreateSpells();
+
+ if(NewSummon->getPetType()==SUMMON_PET)
+ {
+ // Remove Demonic Sacrifice auras (new pet)
+ Unit::AuraList const& auraClassScripts = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator itr = auraClassScripts.begin();itr!=auraClassScripts.end();)
+ {
+ if((*itr)->GetModifier()->m_miscvalue==2228)
+ {
+ m_caster->RemoveAurasDueToSpell((*itr)->GetId());
+ itr = auraClassScripts.begin();
+ }
+ else
+ ++itr;
+ }
+
+ // generate new name for summon pet
+ std::string new_name=objmgr.GeneratePetName(petentry);
+ if(!new_name.empty())
+ NewSummon->SetName(new_name);
+ }
+ else if(NewSummon->getPetType()==HUNTER_PET)
+ NewSummon->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED);
+
+ NewSummon->AIM_Initialize();
+ NewSummon->SetHealth(NewSummon->GetMaxHealth());
+ NewSummon->SetPower(POWER_MANA, NewSummon->GetMaxPower(POWER_MANA));
+
+ map->Add((Creature*)NewSummon);
+
+ m_caster->SetPet(NewSummon);
+ sLog.outDebug("New Pet has guid %u", NewSummon->GetGUIDLow());
+
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ NewSummon->SavePetToDB(PET_SAVE_AS_CURRENT);
+ ((Player*)m_caster)->PetSpellInitialize();
+ }
+}
+
+void Spell::EffectLearnPetSpell(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *_player = (Player*)m_caster;
+
+ Pet *pet = _player->GetPet();
+ if(!pet)
+ return;
+ if(!pet->isAlive())
+ return;
+
+ SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(m_spellInfo->EffectTriggerSpell[i]);
+ if(!learn_spellproto)
+ return;
+
+ pet->SetTP(pet->m_TrainingPoints - pet->GetTPForSpell(learn_spellproto->Id));
+ pet->learnSpell(learn_spellproto->Id);
+
+ pet->SavePetToDB(PET_SAVE_AS_CURRENT);
+ _player->PetSpellInitialize();
+}
+
+void Spell::EffectTaunt(uint32 /*i*/)
+{
+ // this effect use before aura Taunt apply for prevent taunt already attacking target
+ // for spell as marked "non effective at already attacking target"
+ if(unitTarget && unitTarget->GetTypeId() != TYPEID_PLAYER)
+ {
+ if(unitTarget->getVictim()==m_caster)
+ {
+ SendCastResult(SPELL_FAILED_DONT_REPORT);
+ return;
+ }
+ }
+
+ // Also use this effect to set the taunter's threat to the taunted creature's highest value
+ if(unitTarget->CanHaveThreatList() && unitTarget->getThreatManager().getCurrentVictim())
+ unitTarget->getThreatManager().addThreat(m_caster,unitTarget->getThreatManager().getCurrentVictim()->getThreat());
+}
+
+void Spell::EffectWeaponDmg(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ // multiple weapon dmg effect workaround
+ // execute only the last weapon damage
+ // and handle all effects at once
+ for (int j = 0; j < 3; j++)
+ {
+ switch(m_spellInfo->Effect[j])
+ {
+ case SPELL_EFFECT_WEAPON_DAMAGE:
+ case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
+ case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
+ case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
+ if (j < i) // we must calculate only at last weapon effect
+ return;
+ break;
+ }
+ }
+
+ // some spell specific modifiers
+ bool customBonusDamagePercentMod = false;
+ float bonusDamagePercentMod = 1.0f; // applied to fixed effect damage bonus if set customBonusDamagePercentMod
+ float weaponDamagePercentMod = 1.0f; // applied to weapon damage (and to fixed effect damage bonus if customBonusDamagePercentMod not set
+ float totalDamagePercentMod = 1.0f; // applied to final bonus+weapon damage
+ bool normalized = false;
+
+ int32 spell_bonus = 0; // bonus specific for spell
+ switch(m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Whirlwind, single only spell with 2 weapon white damage apply if have
+ if(m_caster->GetTypeId()==TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags & 0x00000400000000LL))
+ {
+ if(((Player*)m_caster)->GetWeaponForAttack(OFF_ATTACK,true))
+ spell_bonus += m_caster->CalculateDamage (OFF_ATTACK, normalized);
+ }
+ // Devastate bonus and sunder armor refresh
+ else if(m_spellInfo->SpellVisual == 671 && m_spellInfo->SpellIconID == 1508)
+ {
+ customBonusDamagePercentMod = true;
+ bonusDamagePercentMod = 0.0f; // only applied if auras found
+
+ Unit::AuraList const& list = unitTarget->GetAurasByType(SPELL_AURA_MOD_RESISTANCE);
+ for(Unit::AuraList::const_iterator itr=list.begin();itr!=list.end();++itr)
+ {
+ SpellEntry const *proto = (*itr)->GetSpellProto();
+ if(proto->SpellVisual == 406 && proto->SpellIconID == 565)
+ {
+ int32 duration = GetSpellDuration(proto);
+ (*itr)->SetAuraDuration(duration);
+ (*itr)->UpdateAuraDuration();
+ bonusDamagePercentMod += 1.0f; // +100%
+ }
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ // Ambush
+ if(m_spellInfo->SpellFamilyFlags & 0x00000200LL)
+ {
+ customBonusDamagePercentMod = true;
+ bonusDamagePercentMod = 2.5f; // 250%
+ }
+ // Mutilate (for each hand)
+ else if(m_spellInfo->SpellFamilyFlags & 0x600000000LL)
+ {
+ bool found = false;
+ // fast check
+ if(unitTarget->HasAuraState(AURA_STATE_DEADLY_POISON))
+ found = true;
+ // full aura scan
+ else
+ {
+ Unit::AuraMap const& auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr!=auras.end(); ++itr)
+ {
+ if(itr->second->GetSpellProto()->Dispel == DISPEL_POISON)
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if(found)
+ totalDamagePercentMod *= 1.5f; // 150% if poisoned
+ }
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Seal of Command - receive benefit from Spell Damage and Healing
+ if(m_spellInfo->SpellFamilyFlags & 0x00000002000000LL)
+ {
+ spell_bonus += int32(0.20f*m_caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellInfo)));
+ spell_bonus += int32(0.29f*m_caster->SpellBaseDamageBonusForVictim(GetSpellSchoolMask(m_spellInfo), unitTarget));
+ }
+ break;
+ }
+ case SPELLFAMILY_SHAMAN:
+ {
+ // Skyshatter Harness item set bonus
+ // Stormstrike
+ if(m_spellInfo->SpellFamilyFlags & 0x001000000000LL)
+ {
+ Unit::AuraList const& m_OverrideClassScript = m_caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(Unit::AuraList::const_iterator i = m_OverrideClassScript.begin(); i != m_OverrideClassScript.end(); ++i)
+ {
+ // Stormstrike AP Buff
+ if ( (*i)->GetModifier()->m_miscvalue == 5634 )
+ {
+ m_caster->CastSpell(m_caster,38430,true,NULL,*i);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ int32 fixed_bonus = 0;
+ for (int j = 0; j < 3; j++)
+ {
+ switch(m_spellInfo->Effect[j])
+ {
+ case SPELL_EFFECT_WEAPON_DAMAGE:
+ case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL:
+ fixed_bonus += CalculateDamage(j,unitTarget);
+ break;
+ case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
+ fixed_bonus += CalculateDamage(j,unitTarget);
+ normalized = true;
+ break;
+ case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
+ weaponDamagePercentMod *= float(CalculateDamage(j,unitTarget)) / 100.0f;
+
+ // applied only to prev.effects fixed damage
+ if(customBonusDamagePercentMod)
+ fixed_bonus = int32(fixed_bonus*bonusDamagePercentMod);
+ else
+ fixed_bonus = int32(fixed_bonus*weaponDamagePercentMod);
+ break;
+ default:
+ break; // not weapon damage effect, just skip
+ }
+ }
+
+ // non-weapon damage
+ int32 bonus = spell_bonus + fixed_bonus;
+
+ // apply to non-weapon bonus weapon total pct effect, weapon total flat effect included in weapon damage
+ if(bonus)
+ {
+ UnitMods unitMod;
+ switch(m_attackType)
+ {
+ default:
+ case BASE_ATTACK: unitMod = UNIT_MOD_DAMAGE_MAINHAND; break;
+ case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break;
+ case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break;
+ }
+
+ float weapon_total_pct = m_caster->GetModifierValue(unitMod, TOTAL_PCT);
+ bonus = int32(bonus*weapon_total_pct);
+ }
+
+ // + weapon damage with applied weapon% dmg to base weapon damage in call
+ bonus += int32(m_caster->CalculateDamage(m_attackType, normalized)*weaponDamagePercentMod);
+
+ // total damage
+ bonus = int32(bonus*totalDamagePercentMod);
+
+ // prevent negative damage
+ uint32 eff_damage = uint32(bonus > 0 ? bonus : 0);
+
+ const uint32 nohitMask = HITINFO_ABSORB | HITINFO_RESIST | HITINFO_MISS;
+
+ uint32 hitInfo = 0;
+ VictimState victimState = VICTIMSTATE_NORMAL;
+ uint32 blocked_dmg = 0;
+ uint32 absorbed_dmg = 0;
+ uint32 resisted_dmg = 0;
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
+
+ m_caster->DoAttackDamage(unitTarget, &eff_damage, &cleanDamage, &blocked_dmg, m_spellSchoolMask, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, m_attackType, m_spellInfo, m_IsTriggeredSpell);
+
+ if ((hitInfo & nohitMask) && m_attackType != RANGED_ATTACK) // not send ranged miss/etc
+ m_caster->SendAttackStateUpdate(hitInfo & nohitMask, unitTarget, 1, m_spellSchoolMask, eff_damage, absorbed_dmg, resisted_dmg, VICTIMSTATE_NORMAL, blocked_dmg);
+
+ bool criticalhit = (hitInfo & HITINFO_CRITICALHIT);
+ m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, eff_damage, m_spellSchoolMask, absorbed_dmg, resisted_dmg, false, blocked_dmg, criticalhit);
+
+ if (eff_damage > (absorbed_dmg + resisted_dmg + blocked_dmg))
+ {
+ eff_damage -= (absorbed_dmg + resisted_dmg + blocked_dmg);
+ }
+ else
+ {
+ cleanDamage.damage += eff_damage;
+ eff_damage = 0;
+ }
+
+ // SPELL_SCHOOL_NORMAL use for weapon-like threat and rage calculation
+ m_caster->DealDamage(unitTarget, eff_damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, true);
+
+ // Hemorrhage
+ if(m_spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & 0x2000000))
+ {
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)m_caster)->AddComboPoints(unitTarget, 1);
+ }
+ // Mangle (Cat): CP
+ if(m_spellInfo->SpellFamilyName==SPELLFAMILY_DRUID && (m_spellInfo->SpellFamilyFlags==0x0000040000000000LL))
+ {
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)m_caster)->AddComboPoints(unitTarget,1);
+ }
+
+
+ // take ammo
+ if(m_attackType == RANGED_ATTACK && m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ Item *pItem = ((Player*)m_caster)->GetWeaponForAttack( RANGED_ATTACK );
+
+ // wands don't have ammo
+ if(!pItem || pItem->IsBroken() || pItem->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_WAND)
+ return;
+
+ if( pItem->GetProto()->InventoryType == INVTYPE_THROWN )
+ {
+ if(pItem->GetMaxStackCount()==1)
+ {
+ // decrease durability for non-stackable throw weapon
+ ((Player*)m_caster)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_RANGED);
+ }
+ else
+ {
+ // decrease items amount for stackable throw weapon
+ uint32 count = 1;
+ ((Player*)m_caster)->DestroyItemCount( pItem, count, true);
+ }
+ }
+ else if(uint32 ammo = ((Player*)m_caster)->GetUInt32Value(PLAYER_AMMO_ID))
+ ((Player*)m_caster)->DestroyItemCount(ammo, 1, true);
+ }
+}
+
+void Spell::EffectThreat(uint32 /*i*/)
+{
+ if(!unitTarget || !unitTarget->isAlive() || !m_caster->isAlive())
+ return;
+
+ if(!unitTarget->CanHaveThreatList())
+ return;
+
+ unitTarget->AddThreat(m_caster, float(damage));
+}
+
+void Spell::EffectHealMaxHealth(uint32 /*i*/)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ uint32 heal = m_caster->GetMaxHealth();
+
+ int32 gain = unitTarget->ModifyHealth(heal);
+ unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
+
+ m_caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, heal);
+}
+
+void Spell::EffectInterruptCast(uint32 /*i*/)
+{
+ if(!unitTarget)
+ return;
+ if(!unitTarget->isAlive())
+ return;
+
+ // TODO: not all spells that used this effect apply cooldown at school spells
+ // also exist case: apply cooldown to interrupted cast only and to all spells
+ for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
+ {
+ if (unitTarget->m_currentSpells[i])
+ {
+ // check if we can interrupt spell
+ if ( unitTarget->m_currentSpells[i]->m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT && unitTarget->m_currentSpells[i]->m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE )
+ {
+ unitTarget->ProhibitSpellScholl(GetSpellSchoolMask(unitTarget->m_currentSpells[i]->m_spellInfo), GetSpellDuration(m_spellInfo));
+ unitTarget->InterruptSpell(i,false);
+ }
+ }
+ }
+}
+
+void Spell::EffectSummonObjectWild(uint32 i)
+{
+ uint32 gameobject_id = m_spellInfo->EffectMiscValue[i];
+
+ GameObject* pGameObj = new GameObject;
+
+ WorldObject* target = focusObject;
+ if( !target )
+ target = m_caster;
+
+ float x,y,z;
+ if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ x = m_targets.m_destX;
+ y = m_targets.m_destY;
+ z = m_targets.m_destZ;
+ }
+ else
+ m_caster->GetClosePoint(x,y,z,DEFAULT_WORLD_OBJECT_SIZE);
+
+ Map *map = target->GetMap();
+
+ if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map,
+ x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1))
+ {
+ delete pGameObj;
+ return;
+ }
+
+ int32 duration = GetSpellDuration(m_spellInfo);
+ pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+ pGameObj->SetSpellId(m_spellInfo->Id);
+
+ if(pGameObj->GetGoType() != GAMEOBJECT_TYPE_FLAGDROP) // make dropped flag clickable for other players (not set owner guid (created by) for this)...
+ m_caster->AddGameObject(pGameObj);
+ map->Add(pGameObj);
+
+ if(pGameObj->GetMapId() == 489 && pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP) //WS
+ {
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ Player *pl = (Player*)m_caster;
+ BattleGround* bg = ((Player *)m_caster)->GetBattleGround();
+ if(bg && bg->GetTypeID()==BATTLEGROUND_WS && bg->GetStatus() == STATUS_IN_PROGRESS)
+ {
+ uint32 team = ALLIANCE;
+
+ if(pl->GetTeam() == team)
+ team = HORDE;
+
+ ((BattleGroundWS*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID(),team);
+ }
+ }
+ }
+
+ if(pGameObj->GetMapId() == 566 && pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP) //EY
+ {
+ if(m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ BattleGround* bg = ((Player *)m_caster)->GetBattleGround();
+ if(bg && bg->GetTypeID()==BATTLEGROUND_EY && bg->GetStatus() == STATUS_IN_PROGRESS)
+ {
+ ((BattleGroundEY*)bg)->SetDroppedFlagGUID(pGameObj->GetGUID());
+ }
+ }
+ }
+
+ if(uint32 linkedEntry = pGameObj->GetLinkedGameObjectEntry())
+ {
+ GameObject* linkedGO = new GameObject;
+ if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, map,
+ x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1))
+ {
+ linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+ linkedGO->SetSpellId(m_spellInfo->Id);
+
+ m_caster->AddGameObject(linkedGO);
+ map->Add(linkedGO);
+ }
+ else
+ {
+ delete linkedGO;
+ linkedGO = NULL;
+ return;
+ }
+ }
+}
+
+void Spell::EffectScriptEffect(uint32 effIndex)
+{
+ // TODO: we must implement hunter pet summon at login there (spell 6962)
+
+ // by spell id
+ switch(m_spellInfo->Id)
+ {
+ // Bending Shinbone
+ case 8856:
+ {
+ if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ uint32 spell_id = 0;
+ switch(urand(1,5))
+ {
+ case 1: spell_id = 8854; break;
+ default: spell_id = 8855; break;
+ }
+
+ m_caster->CastSpell(m_caster,spell_id,true,NULL);
+ return;
+ }
+
+ // Healthstone creating spells
+ case 6201:
+ case 6202:
+ case 5699:
+ case 11729:
+ case 11730:
+ case 27230:
+ {
+ uint32 itemtype;
+ uint32 rank = 0;
+ Unit::AuraList const& mDummyAuras = unitTarget->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
+ {
+ if((*i)->GetId() == 18692)
+ {
+ rank = 1;
+ break;
+ }
+ else if((*i)->GetId() == 18693)
+ {
+ rank = 2;
+ break;
+ }
+ }
+
+ static uint32 const itypes[6][3] = {
+ { 5512,19004,19005}, // Minor Healthstone
+ { 5511,19006,19007}, // Lesser Healthstone
+ { 5509,19008,19009}, // Healthstone
+ { 5510,19010,19011}, // Greater Healthstone
+ { 9421,19012,19013}, // Major Healthstone
+ {22103,22104,22105} // Master Healthstone
+ };
+
+ switch(m_spellInfo->Id)
+ {
+ case 6201: itemtype=itypes[0][rank];break; // Minor Healthstone
+ case 6202: itemtype=itypes[1][rank];break; // Lesser Healthstone
+ case 5699: itemtype=itypes[2][rank];break; // Healthstone
+ case 11729: itemtype=itypes[3][rank];break; // Greater Healthstone
+ case 11730: itemtype=itypes[4][rank];break; // Major Healthstone
+ case 27230: itemtype=itypes[5][rank];break; // Master Healthstone
+ default:
+ return;
+ }
+ DoCreateItem( effIndex, itemtype );
+ return;
+ }
+ // Brittle Armor - need remove one 24575 Brittle Armor aura
+ case 24590:
+ unitTarget->RemoveSingleAuraFromStack(24575, 0);
+ unitTarget->RemoveSingleAuraFromStack(24575, 1);
+ return;
+ // Mercurial Shield - need remove one 26464 Mercurial Shield aura
+ case 26465:
+ unitTarget->RemoveSingleAuraFromStack(26464, 0);
+ return;
+ // Orb teleport spells
+ case 25140:
+ case 25143:
+ case 25650:
+ case 25652:
+ case 29128:
+ case 29129:
+ case 35376:
+ case 35727:
+ {
+ if(!unitTarget)
+ return;
+
+ uint32 spellid;
+ switch(m_spellInfo->Id)
+ {
+ case 25140: spellid = 32571; break;
+ case 25143: spellid = 32572; break;
+ case 25650: spellid = 30140; break;
+ case 25652: spellid = 30141; break;
+ case 29128: spellid = 32568; break;
+ case 29129: spellid = 32569; break;
+ case 35376: spellid = 25649; break;
+ case 35727: spellid = 35730; break;
+ default:
+ return;
+ }
+
+ unitTarget->CastSpell(unitTarget,spellid,false);
+ return;
+ }
+
+ // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell)
+ case 22539:
+ case 22972:
+ case 22975:
+ case 22976:
+ case 22977:
+ case 22978:
+ case 22979:
+ case 22980:
+ case 22981:
+ case 22982:
+ case 22983:
+ case 22984:
+ case 22985:
+ {
+ if(!unitTarget || !unitTarget->isAlive())
+ return;
+
+ // Onyxia Scale Cloak
+ if(unitTarget->GetDummyAura(22683))
+ return;
+
+ // Shadow Flame
+ m_caster->CastSpell(unitTarget, 22682, true);
+ return;
+ }
+ break;
+
+ // Summon Black Qiraji Battle Tank
+ case 26656:
+ {
+ if(!unitTarget)
+ return;
+
+ // Prevent stacking of mounts
+ unitTarget->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED);
+
+ // Two separate mounts depending on area id (allows use both in and out of specific instance)
+ if (unitTarget->GetAreaId() == 3428)
+ unitTarget->CastSpell(unitTarget, 25863, false);
+ else
+ unitTarget->CastSpell(unitTarget, 26655, false);
+ break;
+ }
+ // Piccolo of the Flaming Fire
+ case 17512:
+ {
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE);
+ break;
+ }
+
+ // Dreaming Glory
+ case 28698:
+ {
+ if(!unitTarget)
+ return;
+ unitTarget->CastSpell(unitTarget, 28694, true);
+ break;
+ }
+
+ // Netherbloom
+ case 28702:
+ {
+ if(!unitTarget)
+ return;
+ // 25% chance of casting a random buff
+ if(roll_chance_i(75))
+ return;
+
+ // triggered spells are 28703 to 28707
+ // Note: some sources say, that there was the possibility of
+ // receiving a debuff. However, this seems to be removed by a patch.
+ const uint32 spellid = 28703;
+
+ // don't overwrite an existing aura
+ for(uint8 i=0; i<5; i++)
+ if(unitTarget->HasAura(spellid+i, 0))
+ return;
+ unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true);
+ break;
+ }
+
+ // Nightmare Vine
+ case 28720:
+ {
+ if(!unitTarget)
+ return;
+ // 25% chance of casting Nightmare Pollen
+ if(roll_chance_i(75))
+ return;
+ unitTarget->CastSpell(unitTarget, 28721, true);
+ break;
+ }
+
+ // Mirren's Drinking Hat
+ case 29830:
+ {
+ uint32 item = 0;
+ switch ( urand(1,6) )
+ {
+ case 1: case 2: case 3: item = 23584; break;// Loch Modan Lager
+ case 4: case 5: item = 23585; break;// Stouthammer Lite
+ case 6: item = 23586; break;// Aerie Peak Pale Ale
+ }
+ if (item)
+ DoCreateItem(effIndex,item);
+ break;
+ }
+ // Improved Sprint
+ case 30918:
+ {
+ // Removes snares and roots.
+ uint32 mechanic_mask = (1<<MECHANIC_ROOT) | (1<<MECHANIC_SNARE);
+ Unit::AuraMap& Auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next)
+ {
+ next = iter;
+ ++next;
+ Aura *aur = iter->second;
+ if (!aur->IsPositive()) //only remove negative spells
+ {
+ // check for mechanic mask
+ if(GetSpellMechanicMask(aur->GetSpellProto(), aur->GetEffIndex()) & mechanic_mask)
+ {
+ unitTarget->RemoveAurasDueToSpell(aur->GetId());
+ if(Auras.empty())
+ break;
+ else
+ next = Auras.begin();
+ }
+ }
+ }
+ break;
+ }
+ case 41126: // Flame Crash
+ {
+ if(!unitTarget)
+ return;
+
+ unitTarget->CastSpell(unitTarget, 41131, true);
+ break;
+ }
+ case 44876: // Force Cast - Portal Effect: Sunwell Isle
+ {
+ if(!unitTarget)
+ return;
+
+ unitTarget->CastSpell(unitTarget, 44870, true);
+ break;
+ }
+
+ // Goblin Weather Machine
+ case 46203:
+ {
+ if(!unitTarget)
+ return;
+
+ uint32 spellId;
+ switch(rand()%4)
+ {
+ case 0:
+ spellId=46740;
+ break;
+ case 1:
+ spellId=46739;
+ break;
+ case 2:
+ spellId=46738;
+ break;
+ case 3:
+ spellId=46736;
+ break;
+ }
+ unitTarget->CastSpell(unitTarget, spellId, true);
+ break;
+ }
+ //5,000 Gold
+ case 46642:
+ {
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)unitTarget)->ModifyMoney(50000000);
+
+ break;
+ }
+ }
+
+ if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN )
+ {
+ switch(m_spellInfo->SpellFamilyFlags)
+ {
+ // Judgement
+ case 0x800000:
+ {
+ if(!unitTarget || !unitTarget->isAlive())
+ return;
+ uint32 spellId2 = 0;
+
+ // all seals have aura dummy
+ Unit::AuraList const& m_dummyAuras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = m_dummyAuras.begin(); itr != m_dummyAuras.end(); ++itr)
+ {
+ SpellEntry const *spellInfo = (*itr)->GetSpellProto();
+
+ // search seal (all seals have judgement's aura dummy spell id in 2 effect
+ if ( !spellInfo || !IsSealSpell((*itr)->GetSpellProto()) || (*itr)->GetEffIndex() != 2 )
+ continue;
+
+ // must be calculated base at raw base points in spell proto, GetModifier()->m_value for S.Righteousness modified by SPELLMOD_DAMAGE
+ spellId2 = (*itr)->GetSpellProto()->EffectBasePoints[2]+1;
+
+ if(spellId2 <= 1)
+ continue;
+
+ // found, remove seal
+ m_caster->RemoveAurasDueToSpell((*itr)->GetId());
+
+ // Sanctified Judgement
+ Unit::AuraList const& m_auras = m_caster->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator i = m_auras.begin(); i != m_auras.end(); ++i)
+ {
+ if ((*i)->GetSpellProto()->SpellIconID == 205 && (*i)->GetSpellProto()->Attributes == 0x01D0LL)
+ {
+ int32 chance = (*i)->GetModifier()->m_amount;
+ if ( roll_chance_i(chance) )
+ {
+ int32 mana = spellInfo->manaCost;
+ if ( Player* modOwner = m_caster->GetSpellModOwner() )
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, mana);
+ mana = int32(mana* 0.8f);
+ m_caster->CastCustomSpell(m_caster,31930,&mana,NULL,NULL,true,NULL,*i);
+ }
+ break;
+ }
+ }
+
+ break;
+ }
+
+ m_caster->CastSpell(unitTarget,spellId2,true);
+ return;
+ }
+ }
+ }
+
+ // normal DB scripted effect
+ if(!unitTarget)
+ return;
+
+ sLog.outDebug("Spell ScriptStart spellid %u in EffectScriptEffect ", m_spellInfo->Id);
+ sWorld.ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget);
+}
+
+void Spell::EffectSanctuary(uint32 /*i*/)
+{
+ if(!unitTarget)
+ return;
+ //unitTarget->CombatStop();
+
+ unitTarget->CombatStop();
+ unitTarget->getHostilRefManager().deleteReferences(); // stop all fighting
+ // Vanish allows to remove all threat and cast regular stealth so other spells can be used
+ if(m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE_VANISH))
+ {
+ ((Player *)m_caster)->RemoveSpellsCausingAura(SPELL_AURA_MOD_ROOT);
+ }
+}
+
+void Spell::EffectAddComboPoints(uint32 /*i*/)
+{
+ if(!unitTarget)
+ return;
+
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(damage <= 0)
+ return;
+
+ ((Player*)m_caster)->AddComboPoints(unitTarget, damage);
+}
+
+void Spell::EffectDuel(uint32 i)
+{
+ if(!m_caster || !unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *caster = (Player*)m_caster;
+ Player *target = (Player*)unitTarget;
+
+ // caster or target already have requested duel
+ if( caster->duel || target->duel || target->GetSocial()->HasIgnore(caster->GetGUIDLow()) )
+ return;
+
+ // Players can only fight a duel with each other outside (=not inside dungeons and not in capital cities)
+ // Don't have to check the target's map since you cannot challenge someone across maps
+ if( caster->GetMapId() != 0 && caster->GetMapId() != 1 && caster->GetMapId() != 530)
+ {
+ SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
+ return;
+ }
+
+ AreaTableEntry const* casterAreaEntry = GetAreaEntryByAreaID(caster->GetZoneId());
+ if(casterAreaEntry && (casterAreaEntry->flags & AREA_FLAG_CAPITAL) )
+ {
+ SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
+ return;
+ }
+
+ AreaTableEntry const* targetAreaEntry = GetAreaEntryByAreaID(target->GetZoneId());
+ if(targetAreaEntry && (targetAreaEntry->flags & AREA_FLAG_CAPITAL) )
+ {
+ SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here
+ return;
+ }
+
+ //CREATE DUEL FLAG OBJECT
+ GameObject* pGameObj = new GameObject;
+
+ uint32 gameobject_id = m_spellInfo->EffectMiscValue[i];
+
+ Map *map = m_caster->GetMap();
+ if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), gameobject_id, map,
+ m_caster->GetPositionX()+(unitTarget->GetPositionX()-m_caster->GetPositionX())/2 ,
+ m_caster->GetPositionY()+(unitTarget->GetPositionY()-m_caster->GetPositionY())/2 ,
+ m_caster->GetPositionZ(),
+ m_caster->GetOrientation(), 0, 0, 0, 0, 0, 1))
+ {
+ delete pGameObj;
+ return;
+ }
+
+ pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, m_caster->getFaction() );
+ pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()+1 );
+ int32 duration = GetSpellDuration(m_spellInfo);
+ pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+ pGameObj->SetSpellId(m_spellInfo->Id);
+
+ m_caster->AddGameObject(pGameObj);
+ map->Add(pGameObj);
+ //END
+
+ // Send request
+ WorldPacket data(SMSG_DUEL_REQUESTED, 16);
+ data << pGameObj->GetGUID();
+ data << caster->GetGUID();
+ caster->GetSession()->SendPacket(&data);
+ target->GetSession()->SendPacket(&data);
+
+ // create duel-info
+ DuelInfo *duel = new DuelInfo;
+ duel->initiator = caster;
+ duel->opponent = target;
+ duel->startTime = 0;
+ duel->startTimer = 0;
+ caster->duel = duel;
+
+ DuelInfo *duel2 = new DuelInfo;
+ duel2->initiator = caster;
+ duel2->opponent = caster;
+ duel2->startTime = 0;
+ duel2->startTimer = 0;
+ target->duel = duel2;
+
+ caster->SetUInt64Value(PLAYER_DUEL_ARBITER,pGameObj->GetGUID());
+ target->SetUInt64Value(PLAYER_DUEL_ARBITER,pGameObj->GetGUID());
+}
+
+void Spell::EffectStuck(uint32 /*i*/)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(!sWorld.getConfig(CONFIG_CAST_UNSTUCK))
+ return;
+
+ Player* pTarget = (Player*)unitTarget;
+
+ sLog.outDebug("Spell Effect: Stuck");
+ sLog.outDetail("Player %s (guid %u) used auto-unstuck future at map %u (%f, %f, %f)", pTarget->GetName(), pTarget->GetGUIDLow(), m_caster->GetMapId(), m_caster->GetPositionX(), pTarget->GetPositionY(), pTarget->GetPositionZ());
+
+ if(pTarget->isInFlight())
+ return;
+
+ // homebind location is loaded always
+ pTarget->TeleportTo(pTarget->m_homebindMapId,pTarget->m_homebindX,pTarget->m_homebindY,pTarget->m_homebindZ,pTarget->GetOrientation(), (unitTarget==m_caster ? TELE_TO_SPELL : 0));
+
+ // Stuck spell trigger Hearthstone cooldown
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(8690);
+ if(!spellInfo)
+ return;
+ Spell spell(pTarget,spellInfo,true,0);
+ spell.SendSpellCooldown();
+}
+
+void Spell::EffectSummonPlayer(uint32 /*i*/)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ // Evil Twin (ignore player summon, but hide this for summoner)
+ if(unitTarget->GetDummyAura(23445))
+ return;
+
+ float x,y,z;
+ m_caster->GetClosePoint(x,y,z,unitTarget->GetObjectSize());
+
+ ((Player*)unitTarget)->SetSummonPoint(m_caster->GetMapId(),x,y,z);
+
+ WorldPacket data(SMSG_SUMMON_REQUEST, 8+4+4);
+ data << uint64(m_caster->GetGUID()); // summoner guid
+ data << uint32(m_caster->GetZoneId()); // summoner zone
+ data << uint32(MAX_PLAYER_SUMMON_DELAY*1000); // auto decline after msecs
+ ((Player*)unitTarget)->GetSession()->SendPacket(&data);
+}
+
+static ScriptInfo generateActivateCommand()
+{
+ ScriptInfo si;
+ si.command = SCRIPT_COMMAND_ACTIVATE_OBJECT;
+ return si;
+}
+
+void Spell::EffectActivateObject(uint32 effect_idx)
+{
+ if(!gameObjTarget)
+ return;
+
+ static ScriptInfo activateCommand = generateActivateCommand();
+
+ int32 delay_secs = m_spellInfo->EffectMiscValue[effect_idx];
+
+ sWorld.ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget);
+}
+
+void Spell::EffectSummonTotem(uint32 i)
+{
+ uint8 slot = 0;
+ switch(m_spellInfo->EffectMiscValueB[i])
+ {
+ case SUMMON_TYPE_TOTEM_SLOT1: slot = 0; break;
+ case SUMMON_TYPE_TOTEM_SLOT2: slot = 1; break;
+ case SUMMON_TYPE_TOTEM_SLOT3: slot = 2; break;
+ case SUMMON_TYPE_TOTEM_SLOT4: slot = 3; break;
+ // Battle standard case
+ case SUMMON_TYPE_TOTEM: slot = 254; break;
+ // jewelery statue case, like totem without slot
+ case SUMMON_TYPE_GUARDIAN: slot = 255; break;
+ default: return;
+ }
+
+ if(slot < MAX_TOTEM)
+ {
+ uint64 guid = m_caster->m_TotemSlot[slot];
+ if(guid != 0)
+ {
+ Creature *OldTotem = ObjectAccessor::GetCreature(*m_caster, guid);
+ if(OldTotem && OldTotem->isTotem())
+ ((Totem*)OldTotem)->UnSummon();
+ }
+ }
+
+ uint32 team = 0;
+ if (m_caster->GetTypeId()==TYPEID_PLAYER)
+ team = ((Player*)m_caster)->GetTeam();
+
+ Totem* pTotem = new Totem;
+
+ if(!pTotem->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), m_caster->GetMap(), m_spellInfo->EffectMiscValue[i], team ))
+ {
+ delete pTotem;
+ return;
+ }
+
+ float angle = slot < MAX_TOTEM ? M_PI/MAX_TOTEM - (slot*2*M_PI/MAX_TOTEM) : 0;
+
+ float x,y,z;
+ m_caster->GetClosePoint(x,y,z,pTotem->GetObjectSize(),2.0f,angle);
+
+ // totem must be at same Z in case swimming caster and etc.
+ if( fabs( z - m_caster->GetPositionZ() ) > 5 )
+ z = m_caster->GetPositionZ();
+
+ pTotem->Relocate(x, y, z, m_caster->GetOrientation());
+
+ if(slot < MAX_TOTEM)
+ m_caster->m_TotemSlot[slot] = pTotem->GetGUID();
+
+ pTotem->SetOwner(m_caster->GetGUID());
+ pTotem->SetTypeBySummonSpell(m_spellInfo); // must be after Create call where m_spells initilized
+
+ int32 duration=GetSpellDuration(m_spellInfo);
+ if(Player* modOwner = m_caster->GetSpellModOwner())
+ modOwner->ApplySpellMod(m_spellInfo->Id,SPELLMOD_DURATION, duration);
+ pTotem->SetDuration(duration);
+
+ if (damage) // if not spell info, DB values used
+ {
+ pTotem->SetMaxHealth(damage);
+ pTotem->SetHealth(damage);
+ }
+
+ pTotem->SetUInt32Value(UNIT_CREATED_BY_SPELL,m_spellInfo->Id);
+ pTotem->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_PVP_ATTACKABLE);
+
+ pTotem->ApplySpellImmune(m_spellInfo->Id,IMMUNITY_STATE,SPELL_AURA_MOD_FEAR,true);
+ pTotem->ApplySpellImmune(m_spellInfo->Id,IMMUNITY_STATE,SPELL_AURA_TRANSFORM,true);
+
+ pTotem->Summon(m_caster);
+
+ if(slot < MAX_TOTEM && m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_TOTEM_CREATED, 1+8+4+4);
+ data << uint8(slot);
+ data << uint64(pTotem->GetGUID());
+ data << uint32(duration);
+ data << uint32(m_spellInfo->Id);
+ ((Player*)m_caster)->SendDirectMessage(&data);
+ }
+}
+
+void Spell::EffectEnchantHeldItem(uint32 i)
+{
+ // this is only item spell effect applied to main-hand weapon of target player (players in area)
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* item_owner = (Player*)unitTarget;
+ Item* item = item_owner->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
+
+ if(!item )
+ return;
+
+ // must be equipped
+ if(!item ->IsEquipped())
+ return;
+
+ if (m_spellInfo->EffectMiscValue[i])
+ {
+ uint32 enchant_id = m_spellInfo->EffectMiscValue[i];
+ int32 duration = GetSpellDuration(m_spellInfo); //Try duration index first ..
+ if(!duration)
+ duration = m_currentBasePoints[i]+1; //Base points after ..
+ if(!duration)
+ duration = 10; //10 seconds for enchants which don't have listed duration
+
+ SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
+ if(!pEnchant)
+ return;
+
+ // Always go to temp enchantment slot
+ EnchantmentSlot slot = TEMP_ENCHANTMENT_SLOT;
+
+ // Enchantment will not be applied if a different one already exists
+ if(item->GetEnchantmentId(slot) && item->GetEnchantmentId(slot) != enchant_id)
+ return;
+
+ // Apply the temporary enchantment
+ item->SetEnchantment(slot, enchant_id, duration*1000, 0);
+ item_owner->ApplyEnchantment(item,slot,true);
+ }
+}
+
+void Spell::EffectDisEnchant(uint32 /*i*/)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* p_caster = (Player*)m_caster;
+ if(!itemTarget || !itemTarget->GetProto()->DisenchantID)
+ return;
+
+ p_caster->UpdateCraftSkill(m_spellInfo->Id);
+
+ ((Player*)m_caster)->SendLoot(itemTarget->GetGUID(),LOOT_DISENCHANTING);
+
+ // item will be removed at disenchanting end
+}
+
+void Spell::EffectInebriate(uint32 /*i*/)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *player = (Player*)unitTarget;
+ uint16 currentDrunk = player->GetDrunkValue();
+ uint16 drunkMod = damage * 256;
+ if (currentDrunk + drunkMod > 0xFFFF)
+ currentDrunk = 0xFFFF;
+ else
+ currentDrunk += drunkMod;
+ player->SetDrunkValue(currentDrunk, m_CastItem?m_CastItem->GetEntry():0);
+}
+
+void Spell::EffectFeedPet(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *_player = (Player*)m_caster;
+
+ if(!itemTarget)
+ return;
+
+ Pet *pet = _player->GetPet();
+ if(!pet)
+ return;
+
+ if(!pet->isAlive())
+ return;
+
+ int32 benefit = pet->GetCurrentFoodBenefitLevel(itemTarget->GetProto()->ItemLevel);
+ if(benefit <= 0)
+ return;
+
+ uint32 count = 1;
+ _player->DestroyItemCount(itemTarget,count,true);
+ // TODO: fix crash when a spell has two effects, both pointed at the same item target
+
+ m_caster->CastCustomSpell(m_caster,m_spellInfo->EffectTriggerSpell[i],&benefit,NULL,NULL,true);
+}
+
+void Spell::EffectDismissPet(uint32 /*i*/)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Pet* pet = m_caster->GetPet();
+
+ // not let dismiss dead pet
+ if(!pet||!pet->isAlive())
+ return;
+
+ ((Player*)m_caster)->RemovePet(pet,PET_SAVE_NOT_IN_SLOT);
+}
+
+void Spell::EffectSummonObject(uint32 i)
+{
+ uint32 go_id = m_spellInfo->EffectMiscValue[i];
+
+ uint8 slot = 0;
+ switch(m_spellInfo->Effect[i])
+ {
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: slot = 0; break;
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: slot = 1; break;
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: slot = 2; break;
+ case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: slot = 3; break;
+ default: return;
+ }
+
+ uint64 guid = m_caster->m_ObjectSlot[slot];
+ if(guid != 0)
+ {
+ GameObject* obj = NULL;
+ if( m_caster )
+ obj = ObjectAccessor::GetGameObject(*m_caster, guid);
+
+ if(obj) obj->Delete();
+ m_caster->m_ObjectSlot[slot] = 0;
+ }
+
+ GameObject* pGameObj = new GameObject;
+
+ float rot2 = sin(m_caster->GetOrientation()/2);
+ float rot3 = cos(m_caster->GetOrientation()/2);
+
+ float x,y,z;
+ // If dest location if present
+ if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ x = m_targets.m_destX;
+ y = m_targets.m_destY;
+ z = m_targets.m_destZ;
+ }
+ // Summon in random point all other units if location present
+ else
+ m_caster->GetClosePoint(x,y,z,DEFAULT_WORLD_OBJECT_SIZE);
+
+ Map *map = m_caster->GetMap();
+ if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), go_id, map, x, y, z, m_caster->GetOrientation(), 0, 0, rot2, rot3, 0, 1))
+ {
+ delete pGameObj;
+ return;
+ }
+
+ pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL,m_caster->getLevel());
+ int32 duration = GetSpellDuration(m_spellInfo);
+ pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+ pGameObj->SetSpellId(m_spellInfo->Id);
+ m_caster->AddGameObject(pGameObj);
+
+ map->Add(pGameObj);
+ WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
+ data << pGameObj->GetGUID();
+ m_caster->SendMessageToSet(&data,true);
+
+ m_caster->m_ObjectSlot[slot] = pGameObj->GetGUID();
+}
+
+void Spell::EffectResurrect(uint32 i)
+{
+ if(!unitTarget)
+ return;
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if(unitTarget->isAlive())
+ return;
+ if(!unitTarget->IsInWorld())
+ return;
+
+ switch (m_spellInfo->Id)
+ {
+ // Defibrillate (Goblin Jumper Cables) have 33% chance on success
+ case 8342:
+ if (roll_chance_i(67))
+ {
+ m_caster->CastSpell(m_caster, 8338, true, m_CastItem);
+ return;
+ }
+ break;
+ // Defibrillate (Goblin Jumper Cables XL) have 50% chance on success
+ case 22999:
+ if (roll_chance_i(50))
+ {
+ m_caster->CastSpell(m_caster, 23055, true, m_CastItem);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ Player* pTarget = ((Player*)unitTarget);
+
+ if(pTarget->isRessurectRequested()) // already have one active request
+ return;
+
+ uint32 health = pTarget->GetMaxHealth() * damage / 100;
+ uint32 mana = pTarget->GetMaxPower(POWER_MANA) * damage / 100;
+
+ pTarget->setResurrectRequestData(m_caster->GetGUID(), m_caster->GetMapId(), m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), health, mana);
+ SendResurrectRequest(pTarget);
+}
+
+void Spell::EffectAddExtraAttacks(uint32 /*i*/)
+{
+ if(!unitTarget || !unitTarget->isAlive())
+ return;
+
+ if( unitTarget->m_extraAttacks )
+ return;
+
+ unitTarget->m_extraAttacks = damage;
+}
+
+void Spell::EffectParry(uint32 /*i*/)
+{
+ if (unitTarget->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)unitTarget)->SetCanParry(true);
+ }
+}
+
+void Spell::EffectBlock(uint32 /*i*/)
+{
+ if (unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)unitTarget)->SetCanBlock(true);
+}
+
+void Spell::EffectMomentMove(uint32 i)
+{
+ if(unitTarget->isInFlight())
+ return;
+
+ if( m_spellInfo->rangeIndex== 1) //self range
+ {
+ uint32 mapid = m_caster->GetMapId();
+ float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
+ // before caster
+ float fx,fy,fz;
+ unitTarget->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis);
+ float ox,oy,oz;
+ unitTarget->GetPosition(ox,oy,oz);
+
+ float fx2,fy2,fz2; // getObjectHitPos overwrite last args in any result case
+ if(VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(mapid, ox,oy,oz+0.5, fx,fy,oz+0.5,fx2,fy2,fz2, -0.5))
+ {
+ fx = fx2;
+ fy = fy2;
+ fz = fz2;
+ unitTarget->UpdateGroundPositionZ(fx,fy,fz);
+ }
+
+ if(unitTarget->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, unitTarget->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0));
+ else
+ MapManager::Instance().GetMap(mapid, unitTarget)->CreatureRelocation((Creature*)unitTarget, fx, fy, fz, unitTarget->GetOrientation());
+ }
+}
+
+void Spell::EffectReputation(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *_player = (Player*)unitTarget;
+
+ int32 rep_change = m_currentBasePoints[i]+1; // field store reputation change -1
+
+ uint32 faction_id = m_spellInfo->EffectMiscValue[i];
+
+ FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
+
+ if(!factionEntry)
+ return;
+
+ _player->ModifyFactionReputation(factionEntry,rep_change);
+}
+
+void Spell::EffectQuestComplete(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player *_player = (Player*)m_caster;
+
+ uint32 quest_id = m_spellInfo->EffectMiscValue[i];
+ _player->AreaExploredOrEventHappens(quest_id);
+}
+
+void Spell::EffectSelfResurrect(uint32 i)
+{
+ if(!unitTarget || unitTarget->isAlive())
+ return;
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if(!unitTarget->IsInWorld())
+ return;
+
+ uint32 health = 0;
+ uint32 mana = 0;
+
+ // flat case
+ if(damage < 0)
+ {
+ health = uint32(-damage);
+ mana = m_spellInfo->EffectMiscValue[i];
+ }
+ // percent case
+ else
+ {
+ health = uint32(damage/100.0f*unitTarget->GetMaxHealth());
+ if(unitTarget->GetMaxPower(POWER_MANA) > 0)
+ mana = uint32(damage/100.0f*unitTarget->GetMaxPower(POWER_MANA));
+ }
+
+ Player *plr = ((Player*)unitTarget);
+ plr->ResurrectPlayer(0.0f);
+
+ plr->SetHealth( health );
+ plr->SetPower(POWER_MANA, mana );
+ plr->SetPower(POWER_RAGE, 0 );
+ plr->SetPower(POWER_ENERGY, plr->GetMaxPower(POWER_ENERGY) );
+
+ plr->SpawnCorpseBones();
+
+ plr->SaveToDB();
+}
+
+void Spell::EffectSkinning(uint32 /*i*/)
+{
+ if(unitTarget->GetTypeId() != TYPEID_UNIT )
+ return;
+ if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Creature* creature = (Creature*) unitTarget;
+ int32 targetLevel = creature->getLevel();
+
+ uint32 skill;
+ if(creature->GetCreatureInfo()->flag1 & 256)
+ skill = SKILL_HERBALISM; // special case
+ else if(creature->GetCreatureInfo()->flag1 & 512)
+ skill = SKILL_MINING; // special case
+ else
+ skill = SKILL_SKINNING; // normal case
+
+ ((Player*)m_caster)->SendLoot(creature->GetGUID(),LOOT_SKINNING);
+ creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+
+ int32 reqValue = targetLevel < 10 ? 0 : targetLevel < 20 ? (targetLevel-10)*10 : targetLevel*5;
+
+ int32 skillValue = ((Player*)m_caster)->GetPureSkillValue(skill);
+
+ // Double chances for elites
+ ((Player*)m_caster)->UpdateGatherSkill(skill, skillValue, reqValue, creature->isElite() ? 2 : 1 );
+}
+
+void Spell::EffectCharge(uint32 /*i*/)
+{
+ if(!unitTarget || !m_caster)
+ return;
+
+ float x, y, z;
+ unitTarget->GetContactPoint(m_caster, x, y, z);
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ ((Creature *)unitTarget)->StopMoving();
+
+ // Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags
+ m_caster->SendMonsterMove(x, y, z, 0, MOVEMENTFLAG_WALK_MODE, 1);
+
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)->CreatureRelocation((Creature*)m_caster,x,y,z,m_caster->GetOrientation());
+
+ // not all charge effects used in negative spells
+ if ( !IsPositiveSpell(m_spellInfo->Id))
+ m_caster->Attack(unitTarget,true);
+}
+
+void Spell::EffectSummonCritter(uint32 i)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+ Player* player = (Player*)m_caster;
+
+ uint32 pet_entry = m_spellInfo->EffectMiscValue[i];
+ if(!pet_entry)
+ return;
+
+ Pet* old_critter = player->GetMiniPet();
+
+ // for same pet just despawn
+ if(old_critter && old_critter->GetEntry() == pet_entry)
+ {
+ player->RemoveMiniPet();
+ return;
+ }
+
+ // despawn old pet before summon new
+ if(old_critter)
+ player->RemoveMiniPet();
+
+ // summon new pet
+ Pet* critter = new Pet(MINI_PET);
+
+ Map *map = m_caster->GetMap();
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!critter->Create(objmgr.GenerateLowGuid(HIGHGUID_PET),
+ map, pet_entry, pet_number))
+ {
+ sLog.outError("Spell::EffectSummonCritter, spellid %u: no such creature entry %u", m_spellInfo->Id, pet_entry);
+ delete critter;
+ return;
+ }
+
+ float x,y,z;
+ // If dest location if present
+ if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ x = m_targets.m_destX;
+ y = m_targets.m_destY;
+ z = m_targets.m_destZ;
+ }
+ // Summon if dest location not present near caster
+ else
+ m_caster->GetClosePoint(x,y,z,critter->GetObjectSize());
+
+ critter->Relocate(x,y,z,m_caster->GetOrientation());
+
+ if(!critter->IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %d Y: ^%d)", critter->GetGUIDLow(), critter->GetEntry(), critter->GetPositionX(), critter->GetPositionY());
+ delete critter;
+ return;
+ }
+
+ critter->SetUInt64Value(UNIT_FIELD_SUMMONEDBY,m_caster->GetGUID());
+ critter->SetUInt64Value(UNIT_FIELD_CREATEDBY,m_caster->GetGUID());
+ critter->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,m_caster->getFaction());
+ critter->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
+ critter->AIM_Initialize();
+ critter->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as critter...
+ critter->SetMaxHealth(1);
+ critter->SetHealth(1);
+ critter->SetLevel(1);
+
+ // set timer for unsummon
+ int32 duration = GetSpellDuration(m_spellInfo);
+ if(duration > 0)
+ critter->SetDuration(duration);
+
+ std::string name = player->GetName();
+ name.append(petTypeSuffix[critter->getPetType()]);
+ critter->SetName( name );
+ player->SetMiniPet(critter);
+
+ map->Add((Creature*)critter);
+}
+
+void Spell::EffectKnockBack(uint32 i)
+{
+ if(!unitTarget || !m_caster)
+ return;
+
+ // Effect only works on players
+ if(unitTarget->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ float vsin = sin(m_caster->GetAngle(unitTarget));
+ float vcos = cos(m_caster->GetAngle(unitTarget));
+
+ WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4));
+ data.append(unitTarget->GetPackGUID());
+ data << uint32(0); // Sequence
+ data << float(vcos); // x direction
+ data << float(vsin); // y direction
+ data << float(m_spellInfo->EffectMiscValue[i])/10; // Horizontal speed
+ data << float(damage/-10); // Z Movement speed (vertical)
+
+ ((Player*)unitTarget)->GetSession()->SendPacket(&data);
+}
+
+void Spell::EffectSendTaxi(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(m_spellInfo->EffectMiscValue[i]);
+ if(!entry)
+ return;
+
+ std::vector<uint32> nodes;
+
+ nodes.resize(2);
+ nodes[0] = entry->from;
+ nodes[1] = entry->to;
+
+ uint32 mountid = 0;
+ switch(m_spellInfo->Id)
+ {
+ case 31606: //Stormcrow Amulet
+ mountid = 17447;
+ break;
+ case 45071: //Quest - Sunwell Daily - Dead Scar Bombing Run
+ case 45113: //Quest - Sunwell Daily - Ship Bombing Run
+ case 45353: //Quest - Sunwell Daily - Ship Bombing Run Return
+ mountid = 22840;
+ break;
+ case 34905: //Stealth Flight
+ mountid = 6851;
+ break;
+ }
+
+ ((Player*)unitTarget)->ActivateTaxiPathTo(nodes,mountid);
+
+}
+
+void Spell::EffectPlayerPull(uint32 i)
+{
+ if(!unitTarget || !m_caster)
+ return;
+
+ // Effect only works on players
+ if(unitTarget->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ float vsin = sin(unitTarget->GetAngle(m_caster));
+ float vcos = cos(unitTarget->GetAngle(m_caster));
+
+ WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4));
+ data.append(unitTarget->GetPackGUID());
+ data << uint32(0); // Sequence
+ data << float(vcos); // x direction
+ data << float(vsin); // y direction
+ // Horizontal speed
+ data << float(damage ? damage : unitTarget->GetDistance2d(m_caster));
+ data << float(m_spellInfo->EffectMiscValue[i])/-10; // Z Movement speed
+
+ ((Player*)unitTarget)->GetSession()->SendPacket(&data);
+}
+
+void Spell::EffectDispelMechanic(uint32 i)
+{
+ if(!unitTarget)
+ return;
+
+ uint32 mechanic = m_spellInfo->EffectMiscValue[i];
+
+ Unit::AuraMap& Auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::iterator iter = Auras.begin(), next; iter != Auras.end(); iter = next)
+ {
+ next = iter;
+ ++next;
+ SpellEntry const *spell = sSpellStore.LookupEntry(iter->second->GetSpellProto()->Id);
+ if(spell->Mechanic == mechanic || spell->EffectMechanic[iter->second->GetEffIndex()] == mechanic)
+ {
+ unitTarget->RemoveAurasDueToSpell(spell->Id);
+ if(Auras.empty())
+ break;
+ else
+ next = Auras.begin();
+ }
+ }
+ return;
+}
+
+void Spell::EffectSummonDeadPet(uint32 /*i*/)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+ Player *_player = (Player*)m_caster;
+ Pet *pet = _player->GetPet();
+ if(!pet)
+ return;
+ if(pet->isAlive())
+ return;
+ if(damage < 0)
+ return;
+ pet->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0);
+ pet->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+ pet->setDeathState( ALIVE );
+ pet->clearUnitState(UNIT_STAT_ALL_STATE);
+ pet->SetHealth( uint32(pet->GetMaxHealth()*(float(damage)/100)));
+
+ pet->AIM_Initialize();
+
+ _player->PetSpellInitialize();
+ pet->SavePetToDB(PET_SAVE_AS_CURRENT);
+}
+
+void Spell::EffectDestroyAllTotems(uint32 /*i*/)
+{
+ float mana = 0;
+ for(int slot = 0; slot < MAX_TOTEM; ++slot)
+ {
+ if(!m_caster->m_TotemSlot[slot])
+ continue;
+
+ Creature* totem = ObjectAccessor::GetCreature(*m_caster,m_caster->m_TotemSlot[slot]);
+ if(totem && totem->isTotem())
+ {
+ uint32 spell_id = totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id);
+ if(spellInfo)
+ mana += spellInfo->manaCost * damage / 100;
+ ((Totem*)totem)->UnSummon();
+ }
+ }
+
+ int32 gain = m_caster->ModifyPower(POWER_MANA,int32(mana));
+ m_caster->SendEnergizeSpellLog(m_caster, m_spellInfo->Id, gain, POWER_MANA);
+}
+
+void Spell::EffectDurabilityDamage(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ int32 slot = m_spellInfo->EffectMiscValue[i];
+
+ // FIXME: some spells effects have value -1/-2
+ // Possibly its mean -1 all player equipped items and -2 all items
+ if(slot < 0)
+ {
+ ((Player*)unitTarget)->DurabilityPointsLossAll(damage,(slot < -1));
+ return;
+ }
+
+ // invalid slot value
+ if(slot >= INVENTORY_SLOT_BAG_END)
+ return;
+
+ if(Item* item = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0,slot))
+ ((Player*)unitTarget)->DurabilityPointsLoss(item,damage);
+}
+
+void Spell::EffectDurabilityDamagePCT(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ int32 slot = m_spellInfo->EffectMiscValue[i];
+
+ // FIXME: some spells effects have value -1/-2
+ // Possibly its mean -1 all player equipped items and -2 all items
+ if(slot < 0)
+ {
+ ((Player*)unitTarget)->DurabilityLossAll(double(damage)/100.0f,(slot < -1));
+ return;
+ }
+
+ // invalid slot value
+ if(slot >= INVENTORY_SLOT_BAG_END)
+ return;
+
+ if(damage <= 0)
+ return;
+
+ if(Item* item = ((Player*)unitTarget)->GetItemByPos(INVENTORY_SLOT_BAG_0,slot))
+ ((Player*)unitTarget)->DurabilityLoss(item,double(damage)/100.0f);
+}
+
+void Spell::EffectModifyThreatPercent(uint32 /*effIndex*/)
+{
+ if(!unitTarget)
+ return;
+
+ unitTarget->getThreatManager().modifyThreatPercent(m_caster, damage);
+}
+
+void Spell::EffectTransmitted(uint32 effIndex)
+{
+ uint32 name_id = m_spellInfo->EffectMiscValue[effIndex];
+
+ GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id);
+
+ if (!goinfo)
+ {
+ sLog.outErrorDb("Gameobject (Entry: %u) not exist and not created at spell (ID: %u) cast",name_id, m_spellInfo->Id);
+ return;
+ }
+
+ float fx,fy,fz;
+
+ if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
+ {
+ fx = m_targets.m_destX;
+ fy = m_targets.m_destY;
+ fz = m_targets.m_destZ;
+ }
+ //FIXME: this can be better check for most objects but still hack
+ else if(m_spellInfo->EffectRadiusIndex[effIndex] && m_spellInfo->speed==0)
+ {
+ float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[effIndex]));
+ m_caster->GetClosePoint(fx,fy,fz,DEFAULT_WORLD_OBJECT_SIZE, dis);
+ }
+ else
+ {
+ float min_dis = GetSpellMinRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
+ float max_dis = GetSpellMaxRange(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
+ float dis = rand_norm() * (max_dis - min_dis) + min_dis;
+
+ m_caster->GetClosePoint(fx,fy,fz,DEFAULT_WORLD_OBJECT_SIZE, dis);
+ }
+
+ Map *cMap = m_caster->GetMap();
+
+ if(goinfo->type==GAMEOBJECT_TYPE_FISHINGNODE)
+ {
+ if ( !cMap->IsInWater(fx,fy,fz-0.5f)) // Hack to prevent fishing bobber from failing to land on fishing hole
+ { // but this is not proper, we really need to ignore not materialized objects
+ SendCastResult(SPELL_FAILED_NOT_HERE);
+ SendChannelUpdate(0);
+ return;
+ }
+
+ // replace by water level in this case
+ fz = cMap->GetWaterLevel(fx,fy);
+ }
+ // if gameobject is summoning object, it should be spawned right on caster's position
+ else if(goinfo->type==GAMEOBJECT_TYPE_SUMMONING_RITUAL)
+ {
+ m_caster->GetPosition(fx,fy,fz);
+ }
+
+ GameObject* pGameObj = new GameObject;
+
+ if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), name_id, cMap,
+ fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1))
+ {
+ delete pGameObj;
+ return;
+ }
+
+ int32 duration = GetSpellDuration(m_spellInfo);
+
+ switch(goinfo->type)
+ {
+ case GAMEOBJECT_TYPE_FISHINGNODE:
+ {
+ m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,pGameObj->GetGUID());
+ // Orientation3
+ pGameObj->SetFloatValue(GAMEOBJECT_ROTATION + 2, 0.88431775569915771 );
+ // Orientation4
+ pGameObj->SetFloatValue(GAMEOBJECT_ROTATION + 3, -0.4668855369091033 );
+ m_caster->AddGameObject(pGameObj); // will removed at spell cancel
+
+ // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo))
+ // start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME)
+ int32 lastSec;
+ switch(urand(0, 3))
+ {
+ case 0: lastSec = 3; break;
+ case 1: lastSec = 7; break;
+ case 2: lastSec = 13; break;
+ case 3: lastSec = 17; break;
+ }
+
+ duration = duration - lastSec*1000 + FISHING_BOBBER_READY_TIME*1000;
+ break;
+ }
+ case GAMEOBJECT_TYPE_SUMMONING_RITUAL:
+ {
+ if(m_caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ pGameObj->AddUniqueUse((Player*)m_caster);
+ m_caster->AddGameObject(pGameObj); // will removed at spell cancel
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_FISHINGHOLE:
+ case GAMEOBJECT_TYPE_CHEST:
+ default:
+ {
+ break;
+ }
+ }
+
+ pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+
+ pGameObj->SetOwnerGUID(m_caster->GetGUID() );
+
+ pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() );
+ pGameObj->SetSpellId(m_spellInfo->Id);
+
+ DEBUG_LOG("AddObject at SpellEfects.cpp EffectTransmitted\n");
+ //m_caster->AddGameObject(pGameObj);
+ //m_ObjToDel.push_back(pGameObj);
+
+ cMap->Add(pGameObj);
+
+ WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
+ data << uint64(pGameObj->GetGUID());
+ m_caster->SendMessageToSet(&data,true);
+
+ if(uint32 linkedEntry = pGameObj->GetLinkedGameObjectEntry())
+ {
+ GameObject* linkedGO = new GameObject;
+ if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, cMap,
+ fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1))
+ {
+ linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0);
+ linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() );
+ linkedGO->SetSpellId(m_spellInfo->Id);
+ linkedGO->SetOwnerGUID(m_caster->GetGUID() );
+
+ MapManager::Instance().GetMap(linkedGO->GetMapId(), linkedGO)->Add(linkedGO);
+ }
+ else
+ {
+ delete linkedGO;
+ linkedGO = NULL;
+ return;
+ }
+ }
+}
+
+void Spell::EffectProspecting(uint32 /*i*/)
+{
+ if(m_caster->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ Player* p_caster = (Player*)m_caster;
+ if(!itemTarget || !(itemTarget->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP))
+ return;
+
+ if(itemTarget->GetCount() < 5)
+ return;
+
+ if( sWorld.getConfig(CONFIG_SKILL_PROSPECTING))
+ {
+ uint32 SkillValue = p_caster->GetPureSkillValue(SKILL_JEWELCRAFTING);
+ uint32 reqSkillValue = itemTarget->GetProto()->RequiredSkillRank;
+ p_caster->UpdateGatherSkill(SKILL_JEWELCRAFTING, SkillValue, reqSkillValue);
+ }
+
+ ((Player*)m_caster)->SendLoot(itemTarget->GetGUID(), LOOT_PROSPECTING);
+}
+
+void Spell::EffectSkill(uint32 /*i*/)
+{
+ sLog.outDebug("WORLD: SkillEFFECT");
+}
+
+void Spell::EffectSummonDemon(uint32 i)
+{
+ float px = m_targets.m_destX;
+ float py = m_targets.m_destY;
+ float pz = m_targets.m_destZ;
+
+ Creature* Charmed = m_caster->SummonCreature(m_spellInfo->EffectMiscValue[i], px, py, pz, m_caster->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,3600000);
+ if (!Charmed)
+ return;
+
+ // might not always work correctly, maybe the creature that dies from CoD casts the effect on itself and is therefore the caster?
+ Charmed->SetLevel(m_caster->getLevel());
+
+ // TODO: Add damage/mana/hp according to level
+
+ if (m_spellInfo->EffectMiscValue[i] == 89) // Inferno summon
+ {
+ // Enslave demon effect, without mana cost and cooldown
+ m_caster->CastSpell(Charmed, 20882, true); // FIXME: enslave does not scale with level, level 62+ minions cannot be enslaved
+
+ // Inferno effect
+ Charmed->CastSpell(Charmed, 22703, true, 0);
+ }
+}
+
+/* There is currently no need for this effect. We handle it in BattleGround.cpp
+ If we would handle the resurrection here, the spiritguide would instantly disappear as the
+ player revives, and so we wouldn't see the spirit heal visual effect on the npc.
+ This is why we use a half sec delay between the visual effect and the resurrection itself */
+void Spell::EffectSpiritHeal(uint32 /*i*/)
+{
+ /*
+ if(!unitTarget || unitTarget->isAlive())
+ return;
+ if(unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+ if(!unitTarget->IsInWorld())
+ return;
+
+ //m_spellInfo->EffectBasePoints[i]; == 99 (percent?)
+ //((Player*)unitTarget)->setResurrect(m_caster->GetGUID(), unitTarget->GetPositionX(), unitTarget->GetPositionY(), unitTarget->GetPositionZ(), unitTarget->GetMaxHealth(), unitTarget->GetMaxPower(POWER_MANA));
+ ((Player*)unitTarget)->ResurrectPlayer(1.0f);
+ ((Player*)unitTarget)->SpawnCorpseBones();
+ */
+}
+
+// remove insignia spell effect
+void Spell::EffectSkinPlayerCorpse(uint32 /*i*/)
+{
+ sLog.outDebug("Effect: SkinPlayerCorpse");
+ if ( (m_caster->GetTypeId() != TYPEID_PLAYER) || (unitTarget->GetTypeId() != TYPEID_PLAYER) || (unitTarget->isAlive()) )
+ return;
+
+ ((Player*)unitTarget)->RemovedInsignia( (Player*)m_caster );
+}
+
+void Spell::EffectStealBeneficialBuff(uint32 i)
+{
+ sLog.outDebug("Effect: StealBeneficialBuff");
+
+ if(!unitTarget || unitTarget==m_caster) // can't steal from self
+ return;
+
+ std::vector <Aura *> steal_list;
+ // Create dispel mask by dispel type
+ uint32 dispelMask = GetDispellMask( DispelType(m_spellInfo->EffectMiscValue[i]) );
+ Unit::AuraMap const& auras = unitTarget->GetAuras();
+ for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ Aura *aur = (*itr).second;
+ if (aur && (1<<aur->GetSpellProto()->Dispel) & dispelMask)
+ {
+ // Need check for passive? this
+ if (aur->IsPositive() && !aur->IsPassive())
+ steal_list.push_back(aur);
+ }
+ }
+ // Ok if exist some buffs for dispel try dispel it
+ if (!steal_list.empty())
+ {
+ std::list < std::pair<uint32,uint64> > success_list;
+ int32 list_size = steal_list.size();
+ // Dispell N = damage buffs (or while exist buffs for dispel)
+ for (int32 count=0; count < damage && list_size > 0; ++count)
+ {
+ // Random select buff for dispel
+ Aura *aur = steal_list[urand(0, list_size-1)];
+ // Not use chance for steal
+ // TODO possible need do it
+ success_list.push_back( std::pair<uint32,uint64>(aur->GetId(),aur->GetCasterGUID()));
+
+ // Remove buff from list for prevent doubles
+ for (std::vector<Aura *>::iterator j = steal_list.begin(); j != steal_list.end(); )
+ {
+ Aura *stealed = *j;
+ if (stealed->GetId() == aur->GetId() && stealed->GetCasterGUID() == aur->GetCasterGUID())
+ {
+ j = steal_list.erase(j);
+ --list_size;
+ }
+ else
+ ++j;
+ }
+ }
+ // Really try steal and send log
+ if (!success_list.empty())
+ {
+ int32 count = success_list.size();
+ WorldPacket data(SMSG_SPELLSTEALLOG, 8+8+4+1+4+count*5);
+ data.append(unitTarget->GetPackGUID()); // Victim GUID
+ data.append(m_caster->GetPackGUID()); // Caster GUID
+ data << uint32(m_spellInfo->Id); // Dispell spell id
+ data << uint8(0); // not used
+ data << uint32(count); // count
+ for (std::list<std::pair<uint32,uint64> >::iterator j = success_list.begin(); j != success_list.end(); ++j)
+ {
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(j->first);
+ data << uint32(spellInfo->Id); // Spell Id
+ data << uint8(0); // 0 - steals !=0 transfers
+ unitTarget->RemoveAurasDueToSpellBySteal(spellInfo->Id, j->second, m_caster);
+ }
+ m_caster->SendMessageToSet(&data, true);
+ }
+ }
+}
+
+void Spell::EffectKillCredit(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)unitTarget)->KilledMonster(m_spellInfo->EffectMiscValue[i], 0);
+}
+
+void Spell::EffectQuestFail(uint32 i)
+{
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)unitTarget)->FailQuest(m_spellInfo->EffectMiscValue[i]);
+}
diff --git a/src/game/StatSystem.cpp b/src/game/StatSystem.cpp
index 951a92cb5ad..eda78f95033 100644
--- a/src/game/StatSystem.cpp
+++ b/src/game/StatSystem.cpp
@@ -1,965 +1,965 @@
-/*
- * 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 "Unit.h"
-#include "Player.h"
-#include "Pet.h"
-#include "Creature.h"
-#include "SharedDefines.h"
-#include "SpellAuras.h"
-
-/*#######################################
-######## ########
-######## PLAYERS STAT SYSTEM ########
-######## ########
-#######################################*/
-
-bool Player::UpdateStats(Stats stat)
-{
- if(stat > STAT_SPIRIT)
- return false;
-
- // value = ((base_value * base_pct) + total_value) * total_pct
- float value = GetTotalStatValue(stat);
-
- SetStat(stat, int32(value));
-
- if(stat == STAT_STAMINA || stat == STAT_INTELLECT)
- {
- Pet *pet = GetPet();
- if(pet)
- pet->UpdateStats(stat);
- }
-
- switch(stat)
- {
- case STAT_STRENGTH:
- UpdateAttackPowerAndDamage();
- UpdateShieldBlockValue();
- break;
- case STAT_AGILITY:
- UpdateArmor();
- UpdateAttackPowerAndDamage(true);
- if(getClass() == CLASS_ROGUE || getClass() == CLASS_HUNTER || getClass() == CLASS_DRUID && m_form==FORM_CAT)
- UpdateAttackPowerAndDamage();
-
- UpdateAllCritPercentages();
- UpdateDodgePercentage();
- break;
-
- case STAT_STAMINA: UpdateMaxHealth(); break;
- case STAT_INTELLECT:
- UpdateMaxPower(POWER_MANA);
- UpdateAllSpellCritChances();
- UpdateAttackPowerAndDamage(true); //SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT, only intelect currently
- UpdateArmor(); //SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently
- break;
-
- case STAT_SPIRIT:
- break;
-
- default:
- break;
- }
- UpdateSpellDamageAndHealingBonus();
- UpdateManaRegen();
- return true;
-}
-
-void Player::UpdateSpellDamageAndHealingBonus()
-{
- // Magic damage modifiers implemented in Unit::SpellDamageBonus
- // This information for client side use only
- // Get healing bonus for all schools
- SetStatInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, SpellBaseHealingBonus(SPELL_SCHOOL_MASK_ALL));
- // Get damage bonus for all schools
- for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
- SetStatInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, SpellBaseDamageBonus(SpellSchoolMask(1 << i)));
-}
-
-bool Player::UpdateAllStats()
-{
- for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
- {
- float value = GetTotalStatValue(Stats(i));
- SetStat(Stats(i), (int32)value);
- }
-
- UpdateAttackPowerAndDamage();
- UpdateAttackPowerAndDamage(true);
- UpdateArmor();
- UpdateMaxHealth();
-
- for(int i = POWER_MANA; i < MAX_POWERS; i++)
- UpdateMaxPower(Powers(i));
-
- UpdateAllCritPercentages();
- UpdateAllSpellCritChances();
- UpdateDefenseBonusesMod();
- UpdateShieldBlockValue();
- UpdateSpellDamageAndHealingBonus();
- UpdateManaRegen();
- UpdateExpertise(BASE_ATTACK);
- UpdateExpertise(OFF_ATTACK);
- for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
- UpdateResistances(i);
-
- return true;
-}
-
-void Player::UpdateResistances(uint32 school)
-{
- if(school > SPELL_SCHOOL_NORMAL)
- {
- float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
- SetResistance(SpellSchools(school), int32(value));
-
- Pet *pet = GetPet();
- if(pet)
- pet->UpdateResistances(school);
- }
- else
- UpdateArmor();
-}
-
-void Player::UpdateArmor()
-{
- float value = 0.0f;
- UnitMods unitMod = UNIT_MOD_ARMOR;
-
- value = GetModifierValue(unitMod, BASE_VALUE); // base armor (from items)
- value *= GetModifierValue(unitMod, BASE_PCT); // armor percent from items
- value += GetStat(STAT_AGILITY) * 2.0f; // armor bonus from stats
- value += GetModifierValue(unitMod, TOTAL_VALUE);
-
- //add dynamic flat mods
- AuraList const& mResbyIntellect = GetAurasByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT);
- for(AuraList::const_iterator i = mResbyIntellect.begin();i != mResbyIntellect.end(); ++i)
- {
- Modifier* mod = (*i)->GetModifier();
- if(mod->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
- value += int32(GetStat(Stats((*i)->GetMiscBValue())) * mod->m_amount / 100.0f);
- }
-
- value *= GetModifierValue(unitMod, TOTAL_PCT);
-
- SetArmor(int32(value));
-
- Pet *pet = GetPet();
- if(pet)
- pet->UpdateArmor();
-}
-
-float Player::GetHealthBonusFromStamina()
-{
- float stamina = GetStat(STAT_STAMINA);
-
- float baseStam = stamina < 20 ? stamina : 20;
- float moreStam = stamina - baseStam;
-
- return baseStam + (moreStam*10.0f);
-}
-
-float Player::GetManaBonusFromIntellect()
-{
- float intellect = GetStat(STAT_INTELLECT);
-
- float baseInt = intellect < 20 ? intellect : 20;
- float moreInt = intellect - baseInt;
-
- return baseInt + (moreInt*15.0f);
-}
-
-void Player::UpdateMaxHealth()
-{
- UnitMods unitMod = UNIT_MOD_HEALTH;
-
- float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
- value *= GetModifierValue(unitMod, BASE_PCT);
- value += GetModifierValue(unitMod, TOTAL_VALUE) + GetHealthBonusFromStamina();
- value *= GetModifierValue(unitMod, TOTAL_PCT);
-
- SetMaxHealth((uint32)value);
-}
-
-void Player::UpdateMaxPower(Powers power)
-{
- UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
-
- float bonusPower = (power == POWER_MANA) ? GetManaBonusFromIntellect() : 0;
-
- float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
- value *= GetModifierValue(unitMod, BASE_PCT);
- value += GetModifierValue(unitMod, TOTAL_VALUE) + bonusPower;
- value *= GetModifierValue(unitMod, TOTAL_PCT);
-
- SetMaxPower(power, uint32(value));
-}
-
-void Player::UpdateAttackPowerAndDamage(bool ranged )
-{
- float val2 = 0.0f;
- float level = float(getLevel());
-
- UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
-
- uint16 index = UNIT_FIELD_ATTACK_POWER;
- uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS;
- uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
-
- if(ranged)
- {
- index = UNIT_FIELD_RANGED_ATTACK_POWER;
- index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS;
- index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER;
-
- switch(getClass())
- {
- case CLASS_HUNTER: val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f; break;
- case CLASS_ROGUE: val2 = level + GetStat(STAT_AGILITY) - 10.0f; break;
- case CLASS_WARRIOR:val2 = level + GetStat(STAT_AGILITY) - 10.0f; break;
- case CLASS_DRUID:
- switch(m_form)
- {
- case FORM_CAT:
- case FORM_BEAR:
- case FORM_DIREBEAR:
- val2 = 0.0f; break;
- default:
- val2 = GetStat(STAT_AGILITY) - 10.0f; break;
- }
- break;
- default: val2 = GetStat(STAT_AGILITY) - 10.0f; break;
- }
- }
- else
- {
- switch(getClass())
- {
- case CLASS_WARRIOR: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
- case CLASS_PALADIN: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
- case CLASS_ROGUE: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
- case CLASS_HUNTER: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
- case CLASS_SHAMAN: val2 = level*2.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
- case CLASS_DRUID:
- {
- //Check if Predatory Strikes is skilled
- float mLevelMult = 0.0;
- switch(m_form)
- {
- case FORM_CAT:
- case FORM_BEAR:
- case FORM_DIREBEAR:
- case FORM_MOONKIN:
- {
- Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY);
- for(Unit::AuraList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr)
- {
- // Predatory Strikes
- if ((*itr)->GetSpellProto()->SpellIconID == 1563)
- {
- mLevelMult = (*itr)->GetModifier()->m_amount / 100.0f;
- break;
- }
- }
- break;
- }
- }
-
- switch(m_form)
- {
- case FORM_CAT:
- val2 = getLevel()*(mLevelMult+2.0f) + GetStat(STAT_STRENGTH)*2.0f + GetStat(STAT_AGILITY) - 20.0f; break;
- case FORM_BEAR:
- case FORM_DIREBEAR:
- val2 = getLevel()*(mLevelMult+3.0f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
- case FORM_MOONKIN:
- val2 = getLevel()*(mLevelMult+1.5f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
- default:
- val2 = GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
- }
- break;
- }
- case CLASS_MAGE: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
- case CLASS_PRIEST: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
- case CLASS_WARLOCK: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
- }
- }
-
- SetModifierValue(unitMod, BASE_VALUE, val2);
-
- float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
- float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
-
- //add dynamic flat mods
- if( ranged && (getClassMask() & CLASSMASK_WAND_USERS)==0)
- {
- AuraList const& mRAPbyIntellect = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT);
- for(AuraList::const_iterator i = mRAPbyIntellect.begin();i != mRAPbyIntellect.end(); ++i)
- attPowerMod += int32(GetStat(Stats((*i)->GetModifier()->m_miscvalue)) * (*i)->GetModifier()->m_amount / 100.0f);
- }
-
- float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
-
- SetUInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field
- SetUInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
- SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
-
- //automatically update weapon damage after attack power modification
- if(ranged)
- {
- UpdateDamagePhysical(RANGED_ATTACK);
-
- Pet *pet = GetPet(); //update pet's AP
- if(pet)
- pet->UpdateAttackPowerAndDamage();
- }
- else
- {
- UpdateDamagePhysical(BASE_ATTACK);
- if(CanDualWield() && haveOffhandWeapon()) //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon
- UpdateDamagePhysical(OFF_ATTACK);
- }
-}
-
-void Player::UpdateShieldBlockValue()
-{
- SetUInt32Value(PLAYER_SHIELD_BLOCK, GetShieldBlockValue());
-}
-
-void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage)
-{
- UnitMods unitMod;
- UnitMods attPower;
-
- switch(attType)
- {
- case BASE_ATTACK:
- default:
- unitMod = UNIT_MOD_DAMAGE_MAINHAND;
- attPower = UNIT_MOD_ATTACK_POWER;
- break;
- case OFF_ATTACK:
- unitMod = UNIT_MOD_DAMAGE_OFFHAND;
- attPower = UNIT_MOD_ATTACK_POWER;
- break;
- case RANGED_ATTACK:
- unitMod = UNIT_MOD_DAMAGE_RANGED;
- attPower = UNIT_MOD_ATTACK_POWER_RANGED;
- break;
- }
-
- float att_speed = GetAPMultiplier(attType,normalized);
-
- float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
- float base_pct = GetModifierValue(unitMod, BASE_PCT);
- float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
- float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
-
- float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
- float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
-
- if (IsInFeralForm()) //check if player is druid and in cat or bear forms
- {
- uint32 lvl = getLevel();
- if ( lvl > 60 ) lvl = 60;
-
- weapon_mindamage = lvl*0.85*att_speed;
- weapon_maxdamage = lvl*1.25*att_speed;
- }
- else if(!IsUseEquipedWeapon(attType==BASE_ATTACK)) //check if player not in form but still can't use weapon (broken/etc)
- {
- weapon_mindamage = BASE_MINDAMAGE;
- weapon_maxdamage = BASE_MAXDAMAGE;
- }
- else if(attType == RANGED_ATTACK) //add ammo DPS to ranged damage
- {
- weapon_mindamage += GetAmmoDPS() * att_speed;
- weapon_maxdamage += GetAmmoDPS() * att_speed;
- }
-
- min_damage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
- max_damage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
-}
-
-void Player::UpdateDamagePhysical(WeaponAttackType attType)
-{
- float mindamage;
- float maxdamage;
-
- CalculateMinMaxDamage(attType,false,mindamage,maxdamage);
-
- switch(attType)
- {
- case BASE_ATTACK:
- default:
- SetStatFloatValue(UNIT_FIELD_MINDAMAGE,mindamage);
- SetStatFloatValue(UNIT_FIELD_MAXDAMAGE,maxdamage);
- break;
- case OFF_ATTACK:
- SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE,mindamage);
- SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE,maxdamage);
- break;
- case RANGED_ATTACK:
- SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,mindamage);
- SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,maxdamage);
- break;
- }
-}
-
-void Player::UpdateDefenseBonusesMod()
-{
- UpdateBlockPercentage();
- UpdateParryPercentage();
- UpdateDodgePercentage();
-}
-
-void Player::UpdateBlockPercentage()
-{
- // No block
- float value = 0.0f;
- if(CanBlock())
- {
- // Base value
- value = 5.0f;
- // Modify value from defense skill
- value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
- // Increase from SPELL_AURA_MOD_BLOCK_PERCENT aura
- value += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);
- // Increase from rating
- value += GetRatingBonusValue(CR_BLOCK);
- value = value < 0.0f ? 0.0f : value;
- }
- SetStatFloatValue(PLAYER_BLOCK_PERCENTAGE, value);
-}
-
-void Player::UpdateCritPercentage(WeaponAttackType attType)
-{
- BaseModGroup modGroup;
- uint16 index;
- CombatRating cr;
-
- switch(attType)
- {
- case OFF_ATTACK:
- modGroup = OFFHAND_CRIT_PERCENTAGE;
- index = PLAYER_OFFHAND_CRIT_PERCENTAGE;
- cr = CR_CRIT_MELEE;
- break;
- case RANGED_ATTACK:
- modGroup = RANGED_CRIT_PERCENTAGE;
- index = PLAYER_RANGED_CRIT_PERCENTAGE;
- cr = CR_CRIT_RANGED;
- break;
- case BASE_ATTACK:
- default:
- modGroup = CRIT_PERCENTAGE;
- index = PLAYER_CRIT_PERCENTAGE;
- cr = CR_CRIT_MELEE;
- break;
- }
-
- float value = GetTotalPercentageModValue(modGroup) + GetRatingBonusValue(cr);
- // Modify crit from weapon skill and maximized defense skill of same level victim difference
- value += (int32(GetWeaponSkillValue(attType)) - int32(GetMaxSkillValueForLevel())) * 0.04f;
- value = value < 0.0f ? 0.0f : value;
- SetStatFloatValue(index, value);
-}
-
-void Player::UpdateAllCritPercentages()
-{
- float value = GetMeleeCritFromAgility();
-
- SetBaseModValue(CRIT_PERCENTAGE, PCT_MOD, value);
- SetBaseModValue(OFFHAND_CRIT_PERCENTAGE, PCT_MOD, value);
- SetBaseModValue(RANGED_CRIT_PERCENTAGE, PCT_MOD, value);
-
- UpdateCritPercentage(BASE_ATTACK);
- UpdateCritPercentage(OFF_ATTACK);
- UpdateCritPercentage(RANGED_ATTACK);
-}
-
-void Player::UpdateParryPercentage()
-{
- // No parry
- float value = 0.0f;
- if (CanParry())
- {
- // Base parry
- value = 5.0f;
- // Modify value from defense skill
- value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
- // Parry from SPELL_AURA_MOD_PARRY_PERCENT aura
- value += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
- // Parry from rating
- value += GetRatingBonusValue(CR_PARRY);
- value = value < 0.0f ? 0.0f : value;
- }
- SetStatFloatValue(PLAYER_PARRY_PERCENTAGE, value);
-}
-
-void Player::UpdateDodgePercentage()
-{
- // Dodge from agility
- float value = GetDodgeFromAgility();
- // Modify value from defense skill
- value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
- // Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura
- value += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
- // Dodge from rating
- value += GetRatingBonusValue(CR_DODGE);
- value = value < 0.0f ? 0.0f : value;
- SetStatFloatValue(PLAYER_DODGE_PERCENTAGE, value);
-}
-
-void Player::UpdateSpellCritChance(uint32 school)
-{
- // For normal school set zero crit chance
- if(school == SPELL_SCHOOL_NORMAL)
- {
- SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1, 0.0f);
- return;
- }
- // For others recalculate it from:
- float crit = 0.0f;
- // Crit from Intellect
- crit += GetSpellCritFromIntellect();
- // Increase crit from SPELL_AURA_MOD_SPELL_CRIT_CHANCE
- crit += GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE);
- // Increase crit by school from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
- crit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, 1<<school);
- // Increase crit from spell crit ratings
- crit += GetRatingBonusValue(CR_CRIT_SPELL);
-
- // Store crit value
- SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + school, crit);
-}
-
-void Player::UpdateAllSpellCritChances()
-{
- for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
- UpdateSpellCritChance(i);
-}
-
-void Player::UpdateExpertise(WeaponAttackType attack)
-{
- if(attack==RANGED_ATTACK)
- return;
-
- int32 expertise = int32(GetRatingBonusValue(CR_EXPERTISE));
-
- Item *weapon = GetWeaponForAttack(attack);
-
- AuraList const& expAuras = GetAurasByType(SPELL_AURA_MOD_EXPERTISE);
- for(AuraList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr)
- {
- // item neutral spell
- if((*itr)->GetSpellProto()->EquippedItemClass == -1)
- expertise += (*itr)->GetModifier()->m_amount;
- // item dependent spell
- else if(weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto()))
- expertise += (*itr)->GetModifier()->m_amount;
- }
-
- if(expertise < 0)
- expertise = 0;
-
- switch(attack)
- {
- case BASE_ATTACK: SetUInt32Value(PLAYER_EXPERTISE, expertise); break;
- case OFF_ATTACK: SetUInt32Value(PLAYER_OFFHAND_EXPERTISE, expertise); break;
- default: break;
- }
-}
-
-void Player::UpdateManaRegen()
-{
- float Intellect = GetStat(STAT_INTELLECT);
- // Mana regen from spirit and intellect
- float power_regen = sqrt(Intellect) * OCTRegenMPPerSpirit();
- // Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT aura on spirit base regen
- power_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, POWER_MANA);
-
- // Mana regen from SPELL_AURA_MOD_POWER_REGEN aura
- float power_regen_mp5 = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) / 5.0f;
-
- // Get bonus from SPELL_AURA_MOD_MANA_REGEN_FROM_STAT aura
- AuraList const& regenAura = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT);
- for(AuraList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i)
- {
- Modifier* mod = (*i)->GetModifier();
- power_regen_mp5 += GetStat(Stats(mod->m_miscvalue)) * mod->m_amount / 500.0f;
- }
-
- // Bonus from some dummy auras
- AuraList const& mDummyAuras = GetAurasByType(SPELL_AURA_PERIODIC_DUMMY);
- for(AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
- if((*i)->GetId() == 34074) // Aspect of the Viper
- {
- power_regen_mp5 += (*i)->GetModifier()->m_amount * Intellect / 500.0f;
- // Add regen bonus from level in this dummy
- power_regen_mp5 += getLevel() * 35 / 100;
- }
-
- // Set regen rate in cast state apply only on spirit based regen
- int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
- if (modManaRegenInterrupt > 100)
- modManaRegenInterrupt = 100;
- SetStatFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT, power_regen_mp5 + power_regen * modManaRegenInterrupt / 100.0f);
-
- SetStatFloatValue(PLAYER_FIELD_MOD_MANA_REGEN, power_regen_mp5 + power_regen);
-}
-
-void Player::_ApplyAllStatBonuses()
-{
- SetCanModifyStats(false);
-
- _ApplyAllAuraMods();
- _ApplyAllItemMods();
-
- SetCanModifyStats(true);
-
- UpdateAllStats();
-}
-
-void Player::_RemoveAllStatBonuses()
-{
- SetCanModifyStats(false);
-
- _RemoveAllItemMods();
- _RemoveAllAuraMods();
-
- SetCanModifyStats(true);
-
- UpdateAllStats();
-}
-
-/*#######################################
-######## ########
-######## MOBS STAT SYSTEM ########
-######## ########
-#######################################*/
-
-bool Creature::UpdateStats(Stats /*stat*/)
-{
- return true;
-}
-
-bool Creature::UpdateAllStats()
-{
- UpdateMaxHealth();
- UpdateAttackPowerAndDamage();
-
- for(int i = POWER_MANA; i < MAX_POWERS; ++i)
- UpdateMaxPower(Powers(i));
-
- for(int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
- UpdateResistances(i);
-
- return true;
-}
-
-void Creature::UpdateResistances(uint32 school)
-{
- if(school > SPELL_SCHOOL_NORMAL)
- {
- float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
- SetResistance(SpellSchools(school), int32(value));
- }
- else
- UpdateArmor();
-}
-
-void Creature::UpdateArmor()
-{
- float value = GetTotalAuraModValue(UNIT_MOD_ARMOR);
- SetArmor(int32(value));
-}
-
-void Creature::UpdateMaxHealth()
-{
- float value = GetTotalAuraModValue(UNIT_MOD_HEALTH);
- SetMaxHealth((uint32)value);
-}
-
-void Creature::UpdateMaxPower(Powers power)
-{
- UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
-
- float value = GetTotalAuraModValue(unitMod);
- SetMaxPower(power, uint32(value));
-}
-
-void Creature::UpdateAttackPowerAndDamage(bool ranged)
-{
- if(ranged)
- return;
-
- //automatically update weapon damage after attack power modification
- UpdateDamagePhysical(BASE_ATTACK);
-}
-
-void Creature::UpdateDamagePhysical(WeaponAttackType attType)
-{
- if(attType > BASE_ATTACK)
- return;
-
- UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
-
- float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
-
- float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
- float base_pct = GetModifierValue(unitMod, BASE_PCT);
- float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
- float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
-
- float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
- float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
-
- float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct ;
- float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct ;
-
- SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
- SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
-}
-
-/*#######################################
-######## ########
-######## PETS STAT SYSTEM ########
-######## ########
-#######################################*/
-
-bool Pet::UpdateStats(Stats stat)
-{
- if(stat > STAT_SPIRIT)
- return false;
-
- // value = ((base_value * base_pct) + total_value) * total_pct
- float value = GetTotalStatValue(stat);
-
- Unit *owner = GetOwner();
- if ( stat == STAT_STAMINA )
- {
- if(owner)
- value += float(owner->GetStat(stat)) * 0.3f;
- }
- //warlock's and mage's pets gain 30% of owner's intellect
- else if ( stat == STAT_INTELLECT && getPetType() == SUMMON_PET )
- {
- if(owner && (owner->getClass() == CLASS_WARLOCK || owner->getClass() == CLASS_MAGE) )
- value += float(owner->GetStat(stat)) * 0.3f;
- }
-
- SetStat(stat, int32(value));
-
- switch(stat)
- {
- case STAT_STRENGTH: UpdateAttackPowerAndDamage(); break;
- case STAT_AGILITY: UpdateArmor(); break;
- case STAT_STAMINA: UpdateMaxHealth(); break;
- case STAT_INTELLECT: UpdateMaxPower(POWER_MANA); break;
- case STAT_SPIRIT:
- default:
- break;
- }
-
- return true;
-}
-
-bool Pet::UpdateAllStats()
-{
- for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
- UpdateStats(Stats(i));
-
- for(int i = POWER_MANA; i < MAX_POWERS; i++)
- UpdateMaxPower(Powers(i));
-
- for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
- UpdateResistances(i);
-
- return true;
-}
-
-void Pet::UpdateResistances(uint32 school)
-{
- if(school > SPELL_SCHOOL_NORMAL)
- {
- float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
-
- Unit *owner = GetOwner();
- // hunter and warlock pets gain 40% of owner's resistance
- if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
- value += float(owner->GetResistance(SpellSchools(school))) * 0.4f;
-
- SetResistance(SpellSchools(school), int32(value));
- }
- else
- UpdateArmor();
-}
-
-void Pet::UpdateArmor()
-{
- float value = 0.0f;
- float bonus_armor = 0.0f;
- UnitMods unitMod = UNIT_MOD_ARMOR;
-
- Unit *owner = GetOwner();
- // hunter and warlock pets gain 35% of owner's armor value
- if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
- bonus_armor = 0.35f * float(owner->GetArmor());
-
- value = GetModifierValue(unitMod, BASE_VALUE);
- value *= GetModifierValue(unitMod, BASE_PCT);
- value += GetStat(STAT_AGILITY) * 2.0f;
- value += GetModifierValue(unitMod, TOTAL_VALUE) + bonus_armor;
- value *= GetModifierValue(unitMod, TOTAL_PCT);
-
- SetArmor(int32(value));
-}
-
-void Pet::UpdateMaxHealth()
-{
- UnitMods unitMod = UNIT_MOD_HEALTH;
- float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA);
-
- float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
- value *= GetModifierValue(unitMod, BASE_PCT);
- value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * 10.0f;
- value *= GetModifierValue(unitMod, TOTAL_PCT);
-
- SetMaxHealth((uint32)value);
-}
-
-void Pet::UpdateMaxPower(Powers power)
-{
- UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
- float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f;
-
- float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
- value *= GetModifierValue(unitMod, BASE_PCT);
- value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * 15.0f;
- value *= GetModifierValue(unitMod, TOTAL_PCT);
-
- SetMaxPower(power, uint32(value));
-}
-
-void Pet::UpdateAttackPowerAndDamage(bool ranged)
-{
- if(ranged)
- return;
-
- float val = 0.0f;
- float bonusAP = 0.0f;
- UnitMods unitMod = UNIT_MOD_ATTACK_POWER;
-
- if(GetEntry() == 416) // imp's attack power
- val = GetStat(STAT_STRENGTH) - 10.0f;
- else
- val = 2 * GetStat(STAT_STRENGTH) - 20.0f;
-
- Unit* owner = GetOwner();
- if( owner && owner->GetTypeId()==TYPEID_PLAYER)
- {
- if(getPetType() == HUNTER_PET) //hunter pets benefit from owner's attack power
- {
- bonusAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.22f;
- SetBonusDamage( int32(owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.125f));
- }
- //demons benefit from warlocks shadow or fire damage
- else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK)
- {
- int32 fire = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_FIRE);
- int32 shadow = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_SHADOW);
- int32 maximum = (fire > shadow) ? fire : shadow;
- if(maximum < 0)
- maximum = 0;
- SetBonusDamage( int32(maximum * 0.15f));
- bonusAP = maximum * 0.57f;
- }
- //water elementals benefit from mage's frost damage
- else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_MAGE)
- {
- int32 frost = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_FROST);
- if(frost < 0)
- frost = 0;
- SetBonusDamage( int32(frost * 0.4f));
- }
- }
-
- SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val + bonusAP);
-
- //in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB
- float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
- float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
- float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
-
- //UNIT_FIELD_(RANGED)_ATTACK_POWER field
- SetUInt32Value(UNIT_FIELD_ATTACK_POWER, (uint32)base_attPower);
- //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
- SetUInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (uint32)attPowerMod);
- //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
- SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier);
-
- //automatically update weapon damage after attack power modification
- UpdateDamagePhysical(BASE_ATTACK);
-}
-
-void Pet::UpdateDamagePhysical(WeaponAttackType attType)
-{
- if(attType > BASE_ATTACK)
- return;
-
- UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
-
- float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
-
- float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
- float base_pct = GetModifierValue(unitMod, BASE_PCT);
- float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
- float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
-
- float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
- float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
-
- float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
- float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
-
- // Pet's base damage changes depending on happiness
- if (getPetType() == HUNTER_PET && attType == BASE_ATTACK)
- {
- switch(GetHappinessState())
- {
- case HAPPY:
- // 125% of normal damage
- mindamage = mindamage * 1.25;
- maxdamage = maxdamage * 1.25;
- break;
- case CONTENT:
- // 100% of normal damage, nothing to modify
- break;
- case UNHAPPY:
- // 75% of normal damage
- mindamage = mindamage * 0.75;
- maxdamage = maxdamage * 0.75;
- break;
- }
- }
-
- SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
- SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
-}
+/*
+ * 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 "Unit.h"
+#include "Player.h"
+#include "Pet.h"
+#include "Creature.h"
+#include "SharedDefines.h"
+#include "SpellAuras.h"
+
+/*#######################################
+######## ########
+######## PLAYERS STAT SYSTEM ########
+######## ########
+#######################################*/
+
+bool Player::UpdateStats(Stats stat)
+{
+ if(stat > STAT_SPIRIT)
+ return false;
+
+ // value = ((base_value * base_pct) + total_value) * total_pct
+ float value = GetTotalStatValue(stat);
+
+ SetStat(stat, int32(value));
+
+ if(stat == STAT_STAMINA || stat == STAT_INTELLECT)
+ {
+ Pet *pet = GetPet();
+ if(pet)
+ pet->UpdateStats(stat);
+ }
+
+ switch(stat)
+ {
+ case STAT_STRENGTH:
+ UpdateAttackPowerAndDamage();
+ UpdateShieldBlockValue();
+ break;
+ case STAT_AGILITY:
+ UpdateArmor();
+ UpdateAttackPowerAndDamage(true);
+ if(getClass() == CLASS_ROGUE || getClass() == CLASS_HUNTER || getClass() == CLASS_DRUID && m_form==FORM_CAT)
+ UpdateAttackPowerAndDamage();
+
+ UpdateAllCritPercentages();
+ UpdateDodgePercentage();
+ break;
+
+ case STAT_STAMINA: UpdateMaxHealth(); break;
+ case STAT_INTELLECT:
+ UpdateMaxPower(POWER_MANA);
+ UpdateAllSpellCritChances();
+ UpdateAttackPowerAndDamage(true); //SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT, only intelect currently
+ UpdateArmor(); //SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently
+ break;
+
+ case STAT_SPIRIT:
+ break;
+
+ default:
+ break;
+ }
+ UpdateSpellDamageAndHealingBonus();
+ UpdateManaRegen();
+ return true;
+}
+
+void Player::UpdateSpellDamageAndHealingBonus()
+{
+ // Magic damage modifiers implemented in Unit::SpellDamageBonus
+ // This information for client side use only
+ // Get healing bonus for all schools
+ SetStatInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, SpellBaseHealingBonus(SPELL_SCHOOL_MASK_ALL));
+ // Get damage bonus for all schools
+ for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; i++)
+ SetStatInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, SpellBaseDamageBonus(SpellSchoolMask(1 << i)));
+}
+
+bool Player::UpdateAllStats()
+{
+ for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
+ {
+ float value = GetTotalStatValue(Stats(i));
+ SetStat(Stats(i), (int32)value);
+ }
+
+ UpdateAttackPowerAndDamage();
+ UpdateAttackPowerAndDamage(true);
+ UpdateArmor();
+ UpdateMaxHealth();
+
+ for(int i = POWER_MANA; i < MAX_POWERS; i++)
+ UpdateMaxPower(Powers(i));
+
+ UpdateAllCritPercentages();
+ UpdateAllSpellCritChances();
+ UpdateDefenseBonusesMod();
+ UpdateShieldBlockValue();
+ UpdateSpellDamageAndHealingBonus();
+ UpdateManaRegen();
+ UpdateExpertise(BASE_ATTACK);
+ UpdateExpertise(OFF_ATTACK);
+ for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
+ UpdateResistances(i);
+
+ return true;
+}
+
+void Player::UpdateResistances(uint32 school)
+{
+ if(school > SPELL_SCHOOL_NORMAL)
+ {
+ float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
+ SetResistance(SpellSchools(school), int32(value));
+
+ Pet *pet = GetPet();
+ if(pet)
+ pet->UpdateResistances(school);
+ }
+ else
+ UpdateArmor();
+}
+
+void Player::UpdateArmor()
+{
+ float value = 0.0f;
+ UnitMods unitMod = UNIT_MOD_ARMOR;
+
+ value = GetModifierValue(unitMod, BASE_VALUE); // base armor (from items)
+ value *= GetModifierValue(unitMod, BASE_PCT); // armor percent from items
+ value += GetStat(STAT_AGILITY) * 2.0f; // armor bonus from stats
+ value += GetModifierValue(unitMod, TOTAL_VALUE);
+
+ //add dynamic flat mods
+ AuraList const& mResbyIntellect = GetAurasByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT);
+ for(AuraList::const_iterator i = mResbyIntellect.begin();i != mResbyIntellect.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if(mod->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
+ value += int32(GetStat(Stats((*i)->GetMiscBValue())) * mod->m_amount / 100.0f);
+ }
+
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetArmor(int32(value));
+
+ Pet *pet = GetPet();
+ if(pet)
+ pet->UpdateArmor();
+}
+
+float Player::GetHealthBonusFromStamina()
+{
+ float stamina = GetStat(STAT_STAMINA);
+
+ float baseStam = stamina < 20 ? stamina : 20;
+ float moreStam = stamina - baseStam;
+
+ return baseStam + (moreStam*10.0f);
+}
+
+float Player::GetManaBonusFromIntellect()
+{
+ float intellect = GetStat(STAT_INTELLECT);
+
+ float baseInt = intellect < 20 ? intellect : 20;
+ float moreInt = intellect - baseInt;
+
+ return baseInt + (moreInt*15.0f);
+}
+
+void Player::UpdateMaxHealth()
+{
+ UnitMods unitMod = UNIT_MOD_HEALTH;
+
+ float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
+ value *= GetModifierValue(unitMod, BASE_PCT);
+ value += GetModifierValue(unitMod, TOTAL_VALUE) + GetHealthBonusFromStamina();
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetMaxHealth((uint32)value);
+}
+
+void Player::UpdateMaxPower(Powers power)
+{
+ UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
+
+ float bonusPower = (power == POWER_MANA) ? GetManaBonusFromIntellect() : 0;
+
+ float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
+ value *= GetModifierValue(unitMod, BASE_PCT);
+ value += GetModifierValue(unitMod, TOTAL_VALUE) + bonusPower;
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetMaxPower(power, uint32(value));
+}
+
+void Player::UpdateAttackPowerAndDamage(bool ranged )
+{
+ float val2 = 0.0f;
+ float level = float(getLevel());
+
+ UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
+
+ uint16 index = UNIT_FIELD_ATTACK_POWER;
+ uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS;
+ uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
+
+ if(ranged)
+ {
+ index = UNIT_FIELD_RANGED_ATTACK_POWER;
+ index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS;
+ index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER;
+
+ switch(getClass())
+ {
+ case CLASS_HUNTER: val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f; break;
+ case CLASS_ROGUE: val2 = level + GetStat(STAT_AGILITY) - 10.0f; break;
+ case CLASS_WARRIOR:val2 = level + GetStat(STAT_AGILITY) - 10.0f; break;
+ case CLASS_DRUID:
+ switch(m_form)
+ {
+ case FORM_CAT:
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ val2 = 0.0f; break;
+ default:
+ val2 = GetStat(STAT_AGILITY) - 10.0f; break;
+ }
+ break;
+ default: val2 = GetStat(STAT_AGILITY) - 10.0f; break;
+ }
+ }
+ else
+ {
+ switch(getClass())
+ {
+ case CLASS_WARRIOR: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ case CLASS_PALADIN: val2 = level*3.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ case CLASS_ROGUE: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
+ case CLASS_HUNTER: val2 = level*2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; break;
+ case CLASS_SHAMAN: val2 = level*2.0f + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ case CLASS_DRUID:
+ {
+ //Check if Predatory Strikes is skilled
+ float mLevelMult = 0.0;
+ switch(m_form)
+ {
+ case FORM_CAT:
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ case FORM_MOONKIN:
+ {
+ Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr)
+ {
+ // Predatory Strikes
+ if ((*itr)->GetSpellProto()->SpellIconID == 1563)
+ {
+ mLevelMult = (*itr)->GetModifier()->m_amount / 100.0f;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ switch(m_form)
+ {
+ case FORM_CAT:
+ val2 = getLevel()*(mLevelMult+2.0f) + GetStat(STAT_STRENGTH)*2.0f + GetStat(STAT_AGILITY) - 20.0f; break;
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ val2 = getLevel()*(mLevelMult+3.0f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ case FORM_MOONKIN:
+ val2 = getLevel()*(mLevelMult+1.5f) + GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ default:
+ val2 = GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+ }
+ break;
+ }
+ case CLASS_MAGE: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
+ case CLASS_PRIEST: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
+ case CLASS_WARLOCK: val2 = GetStat(STAT_STRENGTH) - 10.0f; break;
+ }
+ }
+
+ SetModifierValue(unitMod, BASE_VALUE, val2);
+
+ float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
+ float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
+
+ //add dynamic flat mods
+ if( ranged && (getClassMask() & CLASSMASK_WAND_USERS)==0)
+ {
+ AuraList const& mRAPbyIntellect = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT);
+ for(AuraList::const_iterator i = mRAPbyIntellect.begin();i != mRAPbyIntellect.end(); ++i)
+ attPowerMod += int32(GetStat(Stats((*i)->GetModifier()->m_miscvalue)) * (*i)->GetModifier()->m_amount / 100.0f);
+ }
+
+ float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
+
+ SetUInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field
+ SetUInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
+ SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
+
+ //automatically update weapon damage after attack power modification
+ if(ranged)
+ {
+ UpdateDamagePhysical(RANGED_ATTACK);
+
+ Pet *pet = GetPet(); //update pet's AP
+ if(pet)
+ pet->UpdateAttackPowerAndDamage();
+ }
+ else
+ {
+ UpdateDamagePhysical(BASE_ATTACK);
+ if(CanDualWield() && haveOffhandWeapon()) //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon
+ UpdateDamagePhysical(OFF_ATTACK);
+ }
+}
+
+void Player::UpdateShieldBlockValue()
+{
+ SetUInt32Value(PLAYER_SHIELD_BLOCK, GetShieldBlockValue());
+}
+
+void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, float& min_damage, float& max_damage)
+{
+ UnitMods unitMod;
+ UnitMods attPower;
+
+ switch(attType)
+ {
+ case BASE_ATTACK:
+ default:
+ unitMod = UNIT_MOD_DAMAGE_MAINHAND;
+ attPower = UNIT_MOD_ATTACK_POWER;
+ break;
+ case OFF_ATTACK:
+ unitMod = UNIT_MOD_DAMAGE_OFFHAND;
+ attPower = UNIT_MOD_ATTACK_POWER;
+ break;
+ case RANGED_ATTACK:
+ unitMod = UNIT_MOD_DAMAGE_RANGED;
+ attPower = UNIT_MOD_ATTACK_POWER_RANGED;
+ break;
+ }
+
+ float att_speed = GetAPMultiplier(attType,normalized);
+
+ float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
+ float base_pct = GetModifierValue(unitMod, BASE_PCT);
+ float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
+ float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
+
+ float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
+ float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
+
+ if (IsInFeralForm()) //check if player is druid and in cat or bear forms
+ {
+ uint32 lvl = getLevel();
+ if ( lvl > 60 ) lvl = 60;
+
+ weapon_mindamage = lvl*0.85*att_speed;
+ weapon_maxdamage = lvl*1.25*att_speed;
+ }
+ else if(!IsUseEquipedWeapon(attType==BASE_ATTACK)) //check if player not in form but still can't use weapon (broken/etc)
+ {
+ weapon_mindamage = BASE_MINDAMAGE;
+ weapon_maxdamage = BASE_MAXDAMAGE;
+ }
+ else if(attType == RANGED_ATTACK) //add ammo DPS to ranged damage
+ {
+ weapon_mindamage += GetAmmoDPS() * att_speed;
+ weapon_maxdamage += GetAmmoDPS() * att_speed;
+ }
+
+ min_damage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
+ max_damage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
+}
+
+void Player::UpdateDamagePhysical(WeaponAttackType attType)
+{
+ float mindamage;
+ float maxdamage;
+
+ CalculateMinMaxDamage(attType,false,mindamage,maxdamage);
+
+ switch(attType)
+ {
+ case BASE_ATTACK:
+ default:
+ SetStatFloatValue(UNIT_FIELD_MINDAMAGE,mindamage);
+ SetStatFloatValue(UNIT_FIELD_MAXDAMAGE,maxdamage);
+ break;
+ case OFF_ATTACK:
+ SetStatFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE,mindamage);
+ SetStatFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE,maxdamage);
+ break;
+ case RANGED_ATTACK:
+ SetStatFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,mindamage);
+ SetStatFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,maxdamage);
+ break;
+ }
+}
+
+void Player::UpdateDefenseBonusesMod()
+{
+ UpdateBlockPercentage();
+ UpdateParryPercentage();
+ UpdateDodgePercentage();
+}
+
+void Player::UpdateBlockPercentage()
+{
+ // No block
+ float value = 0.0f;
+ if(CanBlock())
+ {
+ // Base value
+ value = 5.0f;
+ // Modify value from defense skill
+ value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
+ // Increase from SPELL_AURA_MOD_BLOCK_PERCENT aura
+ value += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);
+ // Increase from rating
+ value += GetRatingBonusValue(CR_BLOCK);
+ value = value < 0.0f ? 0.0f : value;
+ }
+ SetStatFloatValue(PLAYER_BLOCK_PERCENTAGE, value);
+}
+
+void Player::UpdateCritPercentage(WeaponAttackType attType)
+{
+ BaseModGroup modGroup;
+ uint16 index;
+ CombatRating cr;
+
+ switch(attType)
+ {
+ case OFF_ATTACK:
+ modGroup = OFFHAND_CRIT_PERCENTAGE;
+ index = PLAYER_OFFHAND_CRIT_PERCENTAGE;
+ cr = CR_CRIT_MELEE;
+ break;
+ case RANGED_ATTACK:
+ modGroup = RANGED_CRIT_PERCENTAGE;
+ index = PLAYER_RANGED_CRIT_PERCENTAGE;
+ cr = CR_CRIT_RANGED;
+ break;
+ case BASE_ATTACK:
+ default:
+ modGroup = CRIT_PERCENTAGE;
+ index = PLAYER_CRIT_PERCENTAGE;
+ cr = CR_CRIT_MELEE;
+ break;
+ }
+
+ float value = GetTotalPercentageModValue(modGroup) + GetRatingBonusValue(cr);
+ // Modify crit from weapon skill and maximized defense skill of same level victim difference
+ value += (int32(GetWeaponSkillValue(attType)) - int32(GetMaxSkillValueForLevel())) * 0.04f;
+ value = value < 0.0f ? 0.0f : value;
+ SetStatFloatValue(index, value);
+}
+
+void Player::UpdateAllCritPercentages()
+{
+ float value = GetMeleeCritFromAgility();
+
+ SetBaseModValue(CRIT_PERCENTAGE, PCT_MOD, value);
+ SetBaseModValue(OFFHAND_CRIT_PERCENTAGE, PCT_MOD, value);
+ SetBaseModValue(RANGED_CRIT_PERCENTAGE, PCT_MOD, value);
+
+ UpdateCritPercentage(BASE_ATTACK);
+ UpdateCritPercentage(OFF_ATTACK);
+ UpdateCritPercentage(RANGED_ATTACK);
+}
+
+void Player::UpdateParryPercentage()
+{
+ // No parry
+ float value = 0.0f;
+ if (CanParry())
+ {
+ // Base parry
+ value = 5.0f;
+ // Modify value from defense skill
+ value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
+ // Parry from SPELL_AURA_MOD_PARRY_PERCENT aura
+ value += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
+ // Parry from rating
+ value += GetRatingBonusValue(CR_PARRY);
+ value = value < 0.0f ? 0.0f : value;
+ }
+ SetStatFloatValue(PLAYER_PARRY_PERCENTAGE, value);
+}
+
+void Player::UpdateDodgePercentage()
+{
+ // Dodge from agility
+ float value = GetDodgeFromAgility();
+ // Modify value from defense skill
+ value += (int32(GetDefenseSkillValue()) - int32(GetMaxSkillValueForLevel())) * 0.04f;
+ // Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura
+ value += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
+ // Dodge from rating
+ value += GetRatingBonusValue(CR_DODGE);
+ value = value < 0.0f ? 0.0f : value;
+ SetStatFloatValue(PLAYER_DODGE_PERCENTAGE, value);
+}
+
+void Player::UpdateSpellCritChance(uint32 school)
+{
+ // For normal school set zero crit chance
+ if(school == SPELL_SCHOOL_NORMAL)
+ {
+ SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1, 0.0f);
+ return;
+ }
+ // For others recalculate it from:
+ float crit = 0.0f;
+ // Crit from Intellect
+ crit += GetSpellCritFromIntellect();
+ // Increase crit from SPELL_AURA_MOD_SPELL_CRIT_CHANCE
+ crit += GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE);
+ // Increase crit by school from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL
+ crit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, 1<<school);
+ // Increase crit from spell crit ratings
+ crit += GetRatingBonusValue(CR_CRIT_SPELL);
+
+ // Store crit value
+ SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + school, crit);
+}
+
+void Player::UpdateAllSpellCritChances()
+{
+ for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
+ UpdateSpellCritChance(i);
+}
+
+void Player::UpdateExpertise(WeaponAttackType attack)
+{
+ if(attack==RANGED_ATTACK)
+ return;
+
+ int32 expertise = int32(GetRatingBonusValue(CR_EXPERTISE));
+
+ Item *weapon = GetWeaponForAttack(attack);
+
+ AuraList const& expAuras = GetAurasByType(SPELL_AURA_MOD_EXPERTISE);
+ for(AuraList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr)
+ {
+ // item neutral spell
+ if((*itr)->GetSpellProto()->EquippedItemClass == -1)
+ expertise += (*itr)->GetModifier()->m_amount;
+ // item dependent spell
+ else if(weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto()))
+ expertise += (*itr)->GetModifier()->m_amount;
+ }
+
+ if(expertise < 0)
+ expertise = 0;
+
+ switch(attack)
+ {
+ case BASE_ATTACK: SetUInt32Value(PLAYER_EXPERTISE, expertise); break;
+ case OFF_ATTACK: SetUInt32Value(PLAYER_OFFHAND_EXPERTISE, expertise); break;
+ default: break;
+ }
+}
+
+void Player::UpdateManaRegen()
+{
+ float Intellect = GetStat(STAT_INTELLECT);
+ // Mana regen from spirit and intellect
+ float power_regen = sqrt(Intellect) * OCTRegenMPPerSpirit();
+ // Apply PCT bonus from SPELL_AURA_MOD_POWER_REGEN_PERCENT aura on spirit base regen
+ power_regen *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, POWER_MANA);
+
+ // Mana regen from SPELL_AURA_MOD_POWER_REGEN aura
+ float power_regen_mp5 = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) / 5.0f;
+
+ // Get bonus from SPELL_AURA_MOD_MANA_REGEN_FROM_STAT aura
+ AuraList const& regenAura = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT);
+ for(AuraList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ power_regen_mp5 += GetStat(Stats(mod->m_miscvalue)) * mod->m_amount / 500.0f;
+ }
+
+ // Bonus from some dummy auras
+ AuraList const& mDummyAuras = GetAurasByType(SPELL_AURA_PERIODIC_DUMMY);
+ for(AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
+ if((*i)->GetId() == 34074) // Aspect of the Viper
+ {
+ power_regen_mp5 += (*i)->GetModifier()->m_amount * Intellect / 500.0f;
+ // Add regen bonus from level in this dummy
+ power_regen_mp5 += getLevel() * 35 / 100;
+ }
+
+ // Set regen rate in cast state apply only on spirit based regen
+ int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
+ if (modManaRegenInterrupt > 100)
+ modManaRegenInterrupt = 100;
+ SetStatFloatValue(PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT, power_regen_mp5 + power_regen * modManaRegenInterrupt / 100.0f);
+
+ SetStatFloatValue(PLAYER_FIELD_MOD_MANA_REGEN, power_regen_mp5 + power_regen);
+}
+
+void Player::_ApplyAllStatBonuses()
+{
+ SetCanModifyStats(false);
+
+ _ApplyAllAuraMods();
+ _ApplyAllItemMods();
+
+ SetCanModifyStats(true);
+
+ UpdateAllStats();
+}
+
+void Player::_RemoveAllStatBonuses()
+{
+ SetCanModifyStats(false);
+
+ _RemoveAllItemMods();
+ _RemoveAllAuraMods();
+
+ SetCanModifyStats(true);
+
+ UpdateAllStats();
+}
+
+/*#######################################
+######## ########
+######## MOBS STAT SYSTEM ########
+######## ########
+#######################################*/
+
+bool Creature::UpdateStats(Stats /*stat*/)
+{
+ return true;
+}
+
+bool Creature::UpdateAllStats()
+{
+ UpdateMaxHealth();
+ UpdateAttackPowerAndDamage();
+
+ for(int i = POWER_MANA; i < MAX_POWERS; ++i)
+ UpdateMaxPower(Powers(i));
+
+ for(int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
+ UpdateResistances(i);
+
+ return true;
+}
+
+void Creature::UpdateResistances(uint32 school)
+{
+ if(school > SPELL_SCHOOL_NORMAL)
+ {
+ float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
+ SetResistance(SpellSchools(school), int32(value));
+ }
+ else
+ UpdateArmor();
+}
+
+void Creature::UpdateArmor()
+{
+ float value = GetTotalAuraModValue(UNIT_MOD_ARMOR);
+ SetArmor(int32(value));
+}
+
+void Creature::UpdateMaxHealth()
+{
+ float value = GetTotalAuraModValue(UNIT_MOD_HEALTH);
+ SetMaxHealth((uint32)value);
+}
+
+void Creature::UpdateMaxPower(Powers power)
+{
+ UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
+
+ float value = GetTotalAuraModValue(unitMod);
+ SetMaxPower(power, uint32(value));
+}
+
+void Creature::UpdateAttackPowerAndDamage(bool ranged)
+{
+ if(ranged)
+ return;
+
+ //automatically update weapon damage after attack power modification
+ UpdateDamagePhysical(BASE_ATTACK);
+}
+
+void Creature::UpdateDamagePhysical(WeaponAttackType attType)
+{
+ if(attType > BASE_ATTACK)
+ return;
+
+ UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
+
+ float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
+
+ float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
+ float base_pct = GetModifierValue(unitMod, BASE_PCT);
+ float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
+ float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
+
+ float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
+ float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
+
+ float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct ;
+ float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct ;
+
+ SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
+ SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
+}
+
+/*#######################################
+######## ########
+######## PETS STAT SYSTEM ########
+######## ########
+#######################################*/
+
+bool Pet::UpdateStats(Stats stat)
+{
+ if(stat > STAT_SPIRIT)
+ return false;
+
+ // value = ((base_value * base_pct) + total_value) * total_pct
+ float value = GetTotalStatValue(stat);
+
+ Unit *owner = GetOwner();
+ if ( stat == STAT_STAMINA )
+ {
+ if(owner)
+ value += float(owner->GetStat(stat)) * 0.3f;
+ }
+ //warlock's and mage's pets gain 30% of owner's intellect
+ else if ( stat == STAT_INTELLECT && getPetType() == SUMMON_PET )
+ {
+ if(owner && (owner->getClass() == CLASS_WARLOCK || owner->getClass() == CLASS_MAGE) )
+ value += float(owner->GetStat(stat)) * 0.3f;
+ }
+
+ SetStat(stat, int32(value));
+
+ switch(stat)
+ {
+ case STAT_STRENGTH: UpdateAttackPowerAndDamage(); break;
+ case STAT_AGILITY: UpdateArmor(); break;
+ case STAT_STAMINA: UpdateMaxHealth(); break;
+ case STAT_INTELLECT: UpdateMaxPower(POWER_MANA); break;
+ case STAT_SPIRIT:
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool Pet::UpdateAllStats()
+{
+ for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
+ UpdateStats(Stats(i));
+
+ for(int i = POWER_MANA; i < MAX_POWERS; i++)
+ UpdateMaxPower(Powers(i));
+
+ for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
+ UpdateResistances(i);
+
+ return true;
+}
+
+void Pet::UpdateResistances(uint32 school)
+{
+ if(school > SPELL_SCHOOL_NORMAL)
+ {
+ float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
+
+ Unit *owner = GetOwner();
+ // hunter and warlock pets gain 40% of owner's resistance
+ if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
+ value += float(owner->GetResistance(SpellSchools(school))) * 0.4f;
+
+ SetResistance(SpellSchools(school), int32(value));
+ }
+ else
+ UpdateArmor();
+}
+
+void Pet::UpdateArmor()
+{
+ float value = 0.0f;
+ float bonus_armor = 0.0f;
+ UnitMods unitMod = UNIT_MOD_ARMOR;
+
+ Unit *owner = GetOwner();
+ // hunter and warlock pets gain 35% of owner's armor value
+ if(owner && (getPetType() == HUNTER_PET || getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK))
+ bonus_armor = 0.35f * float(owner->GetArmor());
+
+ value = GetModifierValue(unitMod, BASE_VALUE);
+ value *= GetModifierValue(unitMod, BASE_PCT);
+ value += GetStat(STAT_AGILITY) * 2.0f;
+ value += GetModifierValue(unitMod, TOTAL_VALUE) + bonus_armor;
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetArmor(int32(value));
+}
+
+void Pet::UpdateMaxHealth()
+{
+ UnitMods unitMod = UNIT_MOD_HEALTH;
+ float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA);
+
+ float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
+ value *= GetModifierValue(unitMod, BASE_PCT);
+ value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * 10.0f;
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetMaxHealth((uint32)value);
+}
+
+void Pet::UpdateMaxPower(Powers power)
+{
+ UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);
+ float addValue = (power == POWER_MANA) ? GetStat(STAT_INTELLECT) - GetCreateStat(STAT_INTELLECT) : 0.0f;
+
+ float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
+ value *= GetModifierValue(unitMod, BASE_PCT);
+ value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * 15.0f;
+ value *= GetModifierValue(unitMod, TOTAL_PCT);
+
+ SetMaxPower(power, uint32(value));
+}
+
+void Pet::UpdateAttackPowerAndDamage(bool ranged)
+{
+ if(ranged)
+ return;
+
+ float val = 0.0f;
+ float bonusAP = 0.0f;
+ UnitMods unitMod = UNIT_MOD_ATTACK_POWER;
+
+ if(GetEntry() == 416) // imp's attack power
+ val = GetStat(STAT_STRENGTH) - 10.0f;
+ else
+ val = 2 * GetStat(STAT_STRENGTH) - 20.0f;
+
+ Unit* owner = GetOwner();
+ if( owner && owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(getPetType() == HUNTER_PET) //hunter pets benefit from owner's attack power
+ {
+ bonusAP = owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.22f;
+ SetBonusDamage( int32(owner->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.125f));
+ }
+ //demons benefit from warlocks shadow or fire damage
+ else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK)
+ {
+ int32 fire = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_FIRE);
+ int32 shadow = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_SHADOW);
+ int32 maximum = (fire > shadow) ? fire : shadow;
+ if(maximum < 0)
+ maximum = 0;
+ SetBonusDamage( int32(maximum * 0.15f));
+ bonusAP = maximum * 0.57f;
+ }
+ //water elementals benefit from mage's frost damage
+ else if(getPetType() == SUMMON_PET && owner->getClass() == CLASS_MAGE)
+ {
+ int32 frost = int32(owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST)) - owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG + SPELL_SCHOOL_FROST);
+ if(frost < 0)
+ frost = 0;
+ SetBonusDamage( int32(frost * 0.4f));
+ }
+ }
+
+ SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val + bonusAP);
+
+ //in BASE_VALUE of UNIT_MOD_ATTACK_POWER for creatures we store data of meleeattackpower field in DB
+ float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
+ float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);
+ float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
+
+ //UNIT_FIELD_(RANGED)_ATTACK_POWER field
+ SetUInt32Value(UNIT_FIELD_ATTACK_POWER, (uint32)base_attPower);
+ //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
+ SetUInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (uint32)attPowerMod);
+ //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
+ SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier);
+
+ //automatically update weapon damage after attack power modification
+ UpdateDamagePhysical(BASE_ATTACK);
+}
+
+void Pet::UpdateDamagePhysical(WeaponAttackType attType)
+{
+ if(attType > BASE_ATTACK)
+ return;
+
+ UnitMods unitMod = UNIT_MOD_DAMAGE_MAINHAND;
+
+ float att_speed = float(GetAttackTime(BASE_ATTACK))/1000.0f;
+
+ float base_value = GetModifierValue(unitMod, BASE_VALUE) + GetTotalAttackPowerValue(attType)/ 14.0f * att_speed;
+ float base_pct = GetModifierValue(unitMod, BASE_PCT);
+ float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
+ float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
+
+ float weapon_mindamage = GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
+ float weapon_maxdamage = GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE);
+
+ float mindamage = ((base_value + weapon_mindamage) * base_pct + total_value) * total_pct;
+ float maxdamage = ((base_value + weapon_maxdamage) * base_pct + total_value) * total_pct;
+
+ // Pet's base damage changes depending on happiness
+ if (getPetType() == HUNTER_PET && attType == BASE_ATTACK)
+ {
+ switch(GetHappinessState())
+ {
+ case HAPPY:
+ // 125% of normal damage
+ mindamage = mindamage * 1.25;
+ maxdamage = maxdamage * 1.25;
+ break;
+ case CONTENT:
+ // 100% of normal damage, nothing to modify
+ break;
+ case UNHAPPY:
+ // 75% of normal damage
+ mindamage = mindamage * 0.75;
+ maxdamage = maxdamage * 0.75;
+ break;
+ }
+ }
+
+ SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
+ SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
+}
diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp
index 8687de479f2..cc4f7b7b869 100644
--- a/src/game/TargetedMovementGenerator.cpp
+++ b/src/game/TargetedMovementGenerator.cpp
@@ -1,211 +1,211 @@
-/*
- * 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 "ByteBuffer.h"
-#include "TargetedMovementGenerator.h"
-#include "Errors.h"
-#include "Creature.h"
-#include "MapManager.h"
-#include "DestinationHolderImp.h"
-#include "World.h"
-
-#define SMALL_ALPHA 0.05f
-
-#include <cmath>
-/*
-struct StackCleaner
-{
- Creature &i_creature;
- StackCleaner(Creature &creature) : i_creature(creature) {}
- void Done(void) { i_creature.StopMoving(); }
- ~StackCleaner()
- {
- i_creature->Clear();
- }
-};
-*/
-
-template<class T>
-void
-TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
-{
- if( !i_target.isValid() || !&owner )
- return;
-
- if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED) )
- return;
-
- // prevent redundant micro-movement for pets, other followers.
- if(i_offset && i_target->IsWithinDistInMap(&owner,2*i_offset))
- return;
-
- float x, y, z;
- if(!i_offset)
- {
- // to nearest contact position
- i_target->GetContactPoint( &owner, x, y, z );
- }
- else
- {
- // to at i_offset distance from target and i_angle from target facing
- i_target->GetClosePoint(x,y,z,owner.GetObjectSize(),i_offset,i_angle);
- }
-
- /*
- We MUST not check the distance difference and avoid setting the new location for smaller distances.
- By that we risk having far too many GetContactPoint() calls freezing the whole system.
- In TargetedMovementGenerator<T>::Update() we check the distance to the target and at
- some range we calculate a new position. The calculation takes some processor cycles due to vmaps.
- If the distance to the target it too large to ignore,
- but the distance to the new contact point is short enough to be ignored,
- we will calculate a new contact point each update loop, but will never move to it.
- The system will freeze.
- ralf
-
- //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize
- float bothObjectSize = i_target->GetObjectSize() + owner.GetObjectSize() + CONTACT_DISTANCE;
- if( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize )
- return;
- */
- Traveller<T> traveller(owner);
- i_destinationHolder.SetDestination(traveller, x, y, z);
- owner.addUnitState(UNIT_STAT_CHASE);
- if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
- owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
-}
-
-template<class T>
-void
-TargetedMovementGenerator<T>::Initialize(T &owner)
-{
- if(!&owner)
- return;
- owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
-
- if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
- owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
-
- _setTargetLocation(owner);
-}
-
-template<class T>
-void
-TargetedMovementGenerator<T>::Finalize(T &owner)
-{
- owner.clearUnitState(UNIT_STAT_CHASE);
-}
-
-template<class T>
-void
-TargetedMovementGenerator<T>::Reset(T &owner)
-{
- Initialize(owner);
-}
-
-template<class T>
-bool
-TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
-{
- if(!i_target.isValid())
- return false;
-
- if( !&owner || !owner.isAlive())
- return true;
-
- 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
- if ( owner.IsNonMeleeSpellCasted(false, false, true))
- {
- if (!owner.IsStopped())
- owner.StopMoving();
- return true;
- }
-
- // prevent crash after creature killed pet
- if (!owner.hasUnitState(UNIT_STAT_FOLLOW) && owner.getVictim() != i_target.getTarget())
- return true;
-
- Traveller<T> traveller(owner);
-
- if( !i_destinationHolder.HasDestination() )
- _setTargetLocation(owner);
- if( owner.IsStopped() && !i_destinationHolder.HasArrived() )
- {
- owner.addUnitState(UNIT_STAT_CHASE);
- if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
- owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
-
- i_destinationHolder.StartTravel(traveller);
- return true;
- }
-
- if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false))
- {
- // put targeted movement generators on a higher priority
- if (owner.GetObjectSize())
- i_destinationHolder.ResetUpdate(50);
-
- float dist = i_target->GetObjectSize() + owner.GetObjectSize() + sWorld.getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
-
- //More distance let have better performance, less distance let have more sensitive reaction at target move.
-
- // try to counter precision differences
- if( i_destinationHolder.GetDistance2dFromDestSq(*i_target.getTarget()) >= dist * dist)
- {
- owner.SetInFront(i_target.getTarget()); // Set new Angle For Map::
- _setTargetLocation(owner); //Calculate New Dest and Send data To Player
- }
- // Update the Angle of the target only for Map::, no need to send packet for player
- else if ( !i_angle && !owner.HasInArc( 0.01f, i_target.getTarget() ) )
- owner.SetInFront(i_target.getTarget());
-
- if(( owner.IsStopped() && !i_destinationHolder.HasArrived() ) || i_recalculateTravel )
- {
- i_recalculateTravel = false;
- //Angle update will take place into owner.StopMoving()
- owner.SetInFront(i_target.getTarget());
-
- owner.StopMoving();
- if(owner.canReachWithAttack(i_target.getTarget()) && !owner.hasUnitState(UNIT_STAT_FOLLOW))
- owner.Attack(i_target.getTarget(),true);
- }
- }
- return true;
-}
-
-template<class T>
-Unit*
-TargetedMovementGenerator<T>::GetTarget() const
-{
- return i_target.getTarget();
-}
-
-template void TargetedMovementGenerator<Player>::_setTargetLocation(Player &);
-template void TargetedMovementGenerator<Creature>::_setTargetLocation(Creature &);
-template void TargetedMovementGenerator<Player>::Initialize(Player &);
-template void TargetedMovementGenerator<Creature>::Initialize(Creature &);
-template void TargetedMovementGenerator<Player>::Finalize(Player &);
-template void TargetedMovementGenerator<Creature>::Finalize(Creature &);
-template void TargetedMovementGenerator<Player>::Reset(Player &);
-template void TargetedMovementGenerator<Creature>::Reset(Creature &);
-template bool TargetedMovementGenerator<Player>::Update(Player &, const uint32 &);
-template bool TargetedMovementGenerator<Creature>::Update(Creature &, const uint32 &);
-template Unit* TargetedMovementGenerator<Player>::GetTarget() const;
-template Unit* TargetedMovementGenerator<Creature>::GetTarget() const;
+/*
+ * 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 "ByteBuffer.h"
+#include "TargetedMovementGenerator.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "MapManager.h"
+#include "DestinationHolderImp.h"
+#include "World.h"
+
+#define SMALL_ALPHA 0.05f
+
+#include <cmath>
+/*
+struct StackCleaner
+{
+ Creature &i_creature;
+ StackCleaner(Creature &creature) : i_creature(creature) {}
+ void Done(void) { i_creature.StopMoving(); }
+ ~StackCleaner()
+ {
+ i_creature->Clear();
+ }
+};
+*/
+
+template<class T>
+void
+TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
+{
+ if( !i_target.isValid() || !&owner )
+ return;
+
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED) )
+ return;
+
+ // prevent redundant micro-movement for pets, other followers.
+ if(i_offset && i_target->IsWithinDistInMap(&owner,2*i_offset))
+ return;
+
+ float x, y, z;
+ if(!i_offset)
+ {
+ // to nearest contact position
+ i_target->GetContactPoint( &owner, x, y, z );
+ }
+ else
+ {
+ // to at i_offset distance from target and i_angle from target facing
+ i_target->GetClosePoint(x,y,z,owner.GetObjectSize(),i_offset,i_angle);
+ }
+
+ /*
+ We MUST not check the distance difference and avoid setting the new location for smaller distances.
+ By that we risk having far too many GetContactPoint() calls freezing the whole system.
+ In TargetedMovementGenerator<T>::Update() we check the distance to the target and at
+ some range we calculate a new position. The calculation takes some processor cycles due to vmaps.
+ If the distance to the target it too large to ignore,
+ but the distance to the new contact point is short enough to be ignored,
+ we will calculate a new contact point each update loop, but will never move to it.
+ The system will freeze.
+ ralf
+
+ //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize
+ float bothObjectSize = i_target->GetObjectSize() + owner.GetObjectSize() + CONTACT_DISTANCE;
+ if( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize )
+ return;
+ */
+ Traveller<T> traveller(owner);
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+ owner.addUnitState(UNIT_STAT_CHASE);
+ if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Initialize(T &owner)
+{
+ if(!&owner)
+ return;
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
+ if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+
+ _setTargetLocation(owner);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Finalize(T &owner)
+{
+ owner.clearUnitState(UNIT_STAT_CHASE);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Reset(T &owner)
+{
+ Initialize(owner);
+}
+
+template<class T>
+bool
+TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
+{
+ if(!i_target.isValid())
+ return false;
+
+ if( !&owner || !owner.isAlive())
+ return true;
+
+ 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
+ if ( owner.IsNonMeleeSpellCasted(false, false, true))
+ {
+ if (!owner.IsStopped())
+ owner.StopMoving();
+ return true;
+ }
+
+ // prevent crash after creature killed pet
+ if (!owner.hasUnitState(UNIT_STAT_FOLLOW) && owner.getVictim() != i_target.getTarget())
+ return true;
+
+ Traveller<T> traveller(owner);
+
+ if( !i_destinationHolder.HasDestination() )
+ _setTargetLocation(owner);
+ if( owner.IsStopped() && !i_destinationHolder.HasArrived() )
+ {
+ owner.addUnitState(UNIT_STAT_CHASE);
+ if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+
+ i_destinationHolder.StartTravel(traveller);
+ return true;
+ }
+
+ if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false))
+ {
+ // put targeted movement generators on a higher priority
+ if (owner.GetObjectSize())
+ i_destinationHolder.ResetUpdate(50);
+
+ float dist = i_target->GetObjectSize() + owner.GetObjectSize() + sWorld.getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
+
+ //More distance let have better performance, less distance let have more sensitive reaction at target move.
+
+ // try to counter precision differences
+ if( i_destinationHolder.GetDistance2dFromDestSq(*i_target.getTarget()) >= dist * dist)
+ {
+ owner.SetInFront(i_target.getTarget()); // Set new Angle For Map::
+ _setTargetLocation(owner); //Calculate New Dest and Send data To Player
+ }
+ // Update the Angle of the target only for Map::, no need to send packet for player
+ else if ( !i_angle && !owner.HasInArc( 0.01f, i_target.getTarget() ) )
+ owner.SetInFront(i_target.getTarget());
+
+ if(( owner.IsStopped() && !i_destinationHolder.HasArrived() ) || i_recalculateTravel )
+ {
+ i_recalculateTravel = false;
+ //Angle update will take place into owner.StopMoving()
+ owner.SetInFront(i_target.getTarget());
+
+ owner.StopMoving();
+ if(owner.canReachWithAttack(i_target.getTarget()) && !owner.hasUnitState(UNIT_STAT_FOLLOW))
+ owner.Attack(i_target.getTarget(),true);
+ }
+ }
+ return true;
+}
+
+template<class T>
+Unit*
+TargetedMovementGenerator<T>::GetTarget() const
+{
+ return i_target.getTarget();
+}
+
+template void TargetedMovementGenerator<Player>::_setTargetLocation(Player &);
+template void TargetedMovementGenerator<Creature>::_setTargetLocation(Creature &);
+template void TargetedMovementGenerator<Player>::Initialize(Player &);
+template void TargetedMovementGenerator<Creature>::Initialize(Creature &);
+template void TargetedMovementGenerator<Player>::Finalize(Player &);
+template void TargetedMovementGenerator<Creature>::Finalize(Creature &);
+template void TargetedMovementGenerator<Player>::Reset(Player &);
+template void TargetedMovementGenerator<Creature>::Reset(Creature &);
+template bool TargetedMovementGenerator<Player>::Update(Player &, const uint32 &);
+template bool TargetedMovementGenerator<Creature>::Update(Creature &, const uint32 &);
+template Unit* TargetedMovementGenerator<Player>::GetTarget() const;
+template Unit* TargetedMovementGenerator<Creature>::GetTarget() const;
diff --git a/src/game/TradeHandler.cpp b/src/game/TradeHandler.cpp
index 4a4273c8750..a8abe3a8cf2 100644
--- a/src/game/TradeHandler.cpp
+++ b/src/game/TradeHandler.cpp
@@ -1,634 +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"
-#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();
-}
+/*
+ * 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 bf4e98d427e..c72d88c24ad 100644
--- a/src/game/Unit.cpp
+++ b/src/game/Unit.cpp
@@ -1,10828 +1,10828 @@
-/*
- * 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 "Opcodes.h"
-#include "WorldPacket.h"
-#include "WorldSession.h"
-#include "World.h"
-#include "ObjectMgr.h"
-#include "SpellMgr.h"
-#include "Unit.h"
-#include "QuestDef.h"
-#include "Player.h"
-#include "Creature.h"
-#include "Spell.h"
-#include "Group.h"
-#include "SpellAuras.h"
-#include "MapManager.h"
-#include "ObjectAccessor.h"
-#include "CreatureAI.h"
-#include "Formulas.h"
-#include "Pet.h"
-#include "Util.h"
-#include "Totem.h"
-#include "BattleGround.h"
-#include "InstanceSaveMgr.h"
-#include "GridNotifiersImpl.h"
-#include "CellImpl.h"
-#include "Path.h"
-
-#include <math.h>
-
-float baseMoveSpeed[MAX_MOVE_TYPE] =
-{
- 2.5f, // MOVE_WALK
- 7.0f, // MOVE_RUN
- 1.25f, // MOVE_WALKBACK
- 4.722222f, // MOVE_SWIM
- 4.5f, // MOVE_SWIMBACK
- 3.141594f, // MOVE_TURN
- 7.0f, // MOVE_FLY
- 4.5f, // MOVE_FLYBACK
-};
-
-// auraTypes contains attacker auras capable of proc'ing cast auras
-static Unit::AuraTypeSet GenerateAttakerProcCastAuraTypes()
-{
- static Unit::AuraTypeSet auraTypes;
- auraTypes.insert(SPELL_AURA_DUMMY);
- auraTypes.insert(SPELL_AURA_PROC_TRIGGER_SPELL);
- auraTypes.insert(SPELL_AURA_MOD_HASTE);
- auraTypes.insert(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
- return auraTypes;
-}
-
-// auraTypes contains victim auras capable of proc'ing cast auras
-static Unit::AuraTypeSet GenerateVictimProcCastAuraTypes()
-{
- static Unit::AuraTypeSet auraTypes;
- auraTypes.insert(SPELL_AURA_DUMMY);
- auraTypes.insert(SPELL_AURA_PRAYER_OF_MENDING);
- auraTypes.insert(SPELL_AURA_PROC_TRIGGER_SPELL);
- return auraTypes;
-}
-
-// auraTypes contains auras capable of proc effect/damage (but not cast) for attacker
-static Unit::AuraTypeSet GenerateAttakerProcEffectAuraTypes()
-{
- static Unit::AuraTypeSet auraTypes;
- auraTypes.insert(SPELL_AURA_MOD_DAMAGE_DONE);
- auraTypes.insert(SPELL_AURA_PROC_TRIGGER_DAMAGE);
- auraTypes.insert(SPELL_AURA_MOD_CASTING_SPEED);
- auraTypes.insert(SPELL_AURA_MOD_RATING);
- return auraTypes;
-}
-
-// auraTypes contains auras capable of proc effect/damage (but not cast) for victim
-static Unit::AuraTypeSet GenerateVictimProcEffectAuraTypes()
-{
- static Unit::AuraTypeSet auraTypes;
- auraTypes.insert(SPELL_AURA_MOD_RESISTANCE);
- auraTypes.insert(SPELL_AURA_PROC_TRIGGER_DAMAGE);
- auraTypes.insert(SPELL_AURA_MOD_PARRY_PERCENT);
- auraTypes.insert(SPELL_AURA_MOD_BLOCK_PERCENT);
- auraTypes.insert(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
- return auraTypes;
-}
-
-static Unit::AuraTypeSet attackerProcCastAuraTypes = GenerateAttakerProcCastAuraTypes();
-static Unit::AuraTypeSet attackerProcEffectAuraTypes = GenerateAttakerProcEffectAuraTypes();
-
-static Unit::AuraTypeSet victimProcCastAuraTypes = GenerateVictimProcCastAuraTypes();
-static Unit::AuraTypeSet victimProcEffectAuraTypes = GenerateVictimProcEffectAuraTypes();
-
-// auraTypes contains auras capable of proc'ing for attacker and victim
-static Unit::AuraTypeSet GenerateProcAuraTypes()
-{
- Unit::AuraTypeSet auraTypes;
- auraTypes.insert(attackerProcCastAuraTypes.begin(),attackerProcCastAuraTypes.end());
- auraTypes.insert(attackerProcEffectAuraTypes.begin(),attackerProcEffectAuraTypes.end());
- auraTypes.insert(victimProcCastAuraTypes.begin(),victimProcCastAuraTypes.end());
- auraTypes.insert(victimProcEffectAuraTypes.begin(),victimProcEffectAuraTypes.end());
- return auraTypes;
-}
-
-static Unit::AuraTypeSet procAuraTypes = GenerateProcAuraTypes();
-
-bool IsPassiveStackableSpell( uint32 spellId )
-{
- if(!IsPassiveSpell(spellId))
- return false;
-
- SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId);
- if(!spellProto)
- return false;
-
- for(int j = 0; j < 3; ++j)
- {
- if(std::find(procAuraTypes.begin(),procAuraTypes.end(),spellProto->EffectApplyAuraName[j])!=procAuraTypes.end())
- return false;
- }
-
- return true;
-}
-
-Unit::Unit()
-: WorldObject(), i_motionMaster(this), m_ThreatManager(this), m_HostilRefManager(this)
-{
- m_objectType |= TYPEMASK_UNIT;
- m_objectTypeId = TYPEID_UNIT;
- // 2.3.2 - 0x70
- m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_LIVING | UPDATEFLAG_HASPOSITION);
-
- m_attackTimer[BASE_ATTACK] = 0;
- m_attackTimer[OFF_ATTACK] = 0;
- m_attackTimer[RANGED_ATTACK] = 0;
- m_modAttackSpeedPct[BASE_ATTACK] = 1.0f;
- m_modAttackSpeedPct[OFF_ATTACK] = 1.0f;
- m_modAttackSpeedPct[RANGED_ATTACK] = 1.0f;
-
- m_extraAttacks = 0;
-
- m_state = 0;
- m_form = FORM_NONE;
- m_deathState = ALIVE;
-
- for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
- m_currentSpells[i] = NULL;
-
- m_addDmgOnce = 0;
-
- for(int i = 0; i < MAX_TOTEM; ++i)
- m_TotemSlot[i] = 0;
-
- m_ObjectSlot[0] = m_ObjectSlot[1] = m_ObjectSlot[2] = m_ObjectSlot[3] = 0;
- //m_Aura = NULL;
- //m_AurasCheck = 2000;
- //m_removeAuraTimer = 4;
- //tmpAura = NULL;
- waterbreath = false;
-
- m_Visibility = VISIBILITY_ON;
-
- m_detectInvisibilityMask = 0;
- m_invisibilityMask = 0;
- m_transform = 0;
- m_ShapeShiftFormSpellId = 0;
- m_canModifyStats = false;
-
- for (int i = 0; i < MAX_SPELL_IMMUNITY; i++)
- m_spellImmune[i].clear();
- for (int i = 0; i < UNIT_MOD_END; i++)
- {
- m_auraModifiersGroup[i][BASE_VALUE] = 0.0f;
- m_auraModifiersGroup[i][BASE_PCT] = 1.0f;
- m_auraModifiersGroup[i][TOTAL_VALUE] = 0.0f;
- m_auraModifiersGroup[i][TOTAL_PCT] = 1.0f;
- }
- // implement 50% base damage from offhand
- m_auraModifiersGroup[UNIT_MOD_DAMAGE_OFFHAND][TOTAL_PCT] = 0.5f;
-
- for (int i = 0; i < 3; i++)
- {
- m_weaponDamage[i][MINDAMAGE] = BASE_MINDAMAGE;
- m_weaponDamage[i][MAXDAMAGE] = BASE_MAXDAMAGE;
- }
- for (int i = 0; i < MAX_STATS; i++)
- m_createStats[i] = 0.0f;
-
- m_attacking = NULL;
- m_modMeleeHitChance = 0.0f;
- m_modRangedHitChance = 0.0f;
- m_modSpellHitChance = 0.0f;
- m_baseSpellCritChance = 5;
-
- m_CombatTimer = 0;
- m_lastManaUse = 0;
-
- //m_victimThreat = 0.0f;
- for (int i = 0; i < MAX_SPELL_SCHOOL; ++i)
- m_threatModifier[i] = 1.0f;
- m_isSorted = true;
- for (int i = 0; i < MAX_MOVE_TYPE; ++i)
- m_speed_rate[i] = 1.0f;
-
- m_removedAuras = 0;
- m_charmInfo = NULL;
- m_unit_movement_flags = 0;
-
- // remove aurastates allowing special moves
- for(int i=0; i < MAX_REACTIVE; ++i)
- m_reactiveTimer[i] = 0;
-}
-
-Unit::~Unit()
-{
- // set current spells as deletable
- for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
- {
- // spell may be safely deleted now
- if (m_currentSpells[i]) m_currentSpells[i]->SetDeletable(true);
- m_currentSpells[i] = NULL;
- }
-
- RemoveAllGameObjects();
- RemoveAllDynObjects();
-
- if(m_charmInfo) delete m_charmInfo;
-}
-
-void Unit::Update( uint32 p_time )
-{
- /*if(p_time > m_AurasCheck)
- {
- m_AurasCheck = 2000;
- _UpdateAura();
- }else
- m_AurasCheck -= p_time;*/
-
- // WARNING! Order of execution here is important, do not change.
- // Spells must be processed with event system BEFORE they go to _UpdateSpells.
- // Or else we may have some SPELL_STATE_FINISHED spells stalled in pointers, that is bad.
- m_Events.Update( p_time );
- _UpdateSpells( p_time );
-
- // update combat timer only for players and pets
- if (isInCombat() && (GetTypeId() == TYPEID_PLAYER || ((Creature*)this)->isPet() || ((Creature*)this)->isCharmed()))
- {
- // Check UNIT_STAT_MELEE_ATTACKING or UNIT_STAT_CHASE (without UNIT_STAT_FOLLOW in this case) so pets can reach far away
- // targets without stopping half way there and running off.
- // These flags are reset after target dies or another command is given.
- if( m_HostilRefManager.isEmpty() )
- {
- // m_CombatTimer set at aura start and it will be freeze until aura removing
- if ( m_CombatTimer <= p_time )
- ClearInCombat();
- else
- m_CombatTimer -= p_time;
- }
- }
-
- if(uint32 base_att = getAttackTimer(BASE_ATTACK))
- {
- setAttackTimer(BASE_ATTACK, (p_time >= base_att ? 0 : base_att - p_time) );
- }
-
- // update abilities available only for fraction of time
- UpdateReactives( p_time );
-
- ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, GetHealth() < GetMaxHealth()*0.20f);
- ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, GetHealth() < GetMaxHealth()*0.35f);
-
- i_motionMaster.UpdateMotion(p_time);
-}
-
-bool Unit::haveOffhandWeapon() const
-{
- if(GetTypeId() == TYPEID_PLAYER)
- return ((Player*)this)->GetWeaponForAttack(OFF_ATTACK,true);
- else
- return false;
-}
-
-void Unit::SendMonsterMoveWithSpeedToCurrentDestination(Player* player)
-{
- float x, y, z;
- if(GetMotionMaster()->GetDestination(x, y, z))
- SendMonsterMoveWithSpeed(x, y, z, GetUnitMovementFlags(), 0, player);
-}
-
-void Unit::SendMonsterMoveWithSpeed(float x, float y, float z, uint32 MovementFlags, uint32 transitTime, Player* player)
-{
- if (!transitTime)
- {
- float dx = x - GetPositionX();
- float dy = y - GetPositionY();
- float dz = z - GetPositionZ();
-
- float dist = ((dx*dx) + (dy*dy) + (dz*dz));
- if(dist<0)
- dist = 0;
- else
- dist = sqrt(dist);
-
- double speed = GetSpeed((MovementFlags & MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN);
- if(speed<=0)
- speed = 2.5f;
- speed *= 0.001f;
- transitTime = static_cast<uint32>(dist / speed + 0.5);
- }
- //float orientation = (float)atan2((double)dy, (double)dx);
- SendMonsterMove(x, y, z, 0, MovementFlags, transitTime, player);
-}
-
-void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player)
-{
- WorldPacket data( SMSG_MONSTER_MOVE, (41 + GetPackGUID().size()) );
- data.append(GetPackGUID());
-
- // Point A, starting location
- data << GetPositionX() << GetPositionY() << GetPositionZ();
- // unknown field - unrelated to orientation
- // seems to increment about 1000 for every 1.7 seconds
- // for now, we'll just use mstime
- data << getMSTime();
-
- data << uint8(type); // unknown
- switch(type)
- {
- case 0: // normal packet
- break;
- case 1: // stop packet
- SendMessageToSet( &data, true );
- return;
- case 3: // not used currently
- data << uint64(0); // probably target guid
- break;
- case 4: // not used currently
- data << float(0); // probably orientation
- break;
- }
-
- //Movement Flags (0x0 = walk, 0x100 = run, 0x200 = fly/swim)
- data << uint32(MovementFlags);
-
- data << Time; // Time in between points
- data << uint32(1); // 1 single waypoint
- data << NewPosX << NewPosY << NewPosZ; // the single waypoint Point B
-
- if(player)
- player->GetSession()->SendPacket(&data);
- else
- SendMessageToSet( &data, true );
-}
-
-void Unit::SendMonsterMoveByPath(Path const& path, uint32 start, uint32 end, uint32 MovementFlags)
-{
- uint32 traveltime = uint32(path.GetTotalLength(start, end) * 32);
-
- uint32 pathSize = end-start;
-
- WorldPacket data( SMSG_MONSTER_MOVE, (GetPackGUID().size()+4+4+4+4+1+4+4+4+pathSize*4*3) );
- data.append(GetPackGUID());
- data << GetPositionX( )
- << GetPositionY( )
- << GetPositionZ( );
- data << GetOrientation( );
- data << uint8( 0 );
- data << uint32( MovementFlags );
- data << uint32( traveltime );
- data << uint32( pathSize );
- data.append( (char*)path.GetNodes(start), pathSize * 4 * 3 );
-
- //WPAssert( data.size() == 37 + pathnodes.Size( ) * 4 * 3 );
- SendMessageToSet(&data, true);
-}
-
-void Unit::resetAttackTimer(WeaponAttackType type)
-{
- m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]);
-}
-
-bool Unit::canReachWithAttack(Unit *pVictim) const
-{
- assert(pVictim);
- float reach = GetFloatValue(UNIT_FIELD_COMBATREACH);
- if( reach <= 0.0f )
- reach = 1.0f;
- return IsWithinDistInMap(pVictim, reach);
-}
-
-void Unit::RemoveSpellsCausingAura(AuraType auraType)
-{
- if (auraType >= TOTAL_AURAS) return;
- AuraList::iterator iter, next;
- for (iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end(); iter = next)
- {
- next = iter;
- ++next;
-
- if (*iter)
- {
- RemoveAurasDueToSpell((*iter)->GetId());
- if (!m_modAuras[auraType].empty())
- next = m_modAuras[auraType].begin();
- else
- return;
- }
- }
-}
-
-bool Unit::HasAuraType(AuraType auraType) const
-{
- return (!m_modAuras[auraType].empty());
-}
-
-/* Called by DealDamage for auras that have a chance to be dispelled on damage taken. */
-void Unit::RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage)
-{
- if(!HasAuraType(auraType))
- return;
-
- // The chance to dispell an aura depends on the damage taken with respect to the casters level.
- uint32 max_dmg = getLevel() > 8 ? 25 * getLevel() - 150 : 50;
- float chance = float(damage) / max_dmg * 100.0f;
- if (roll_chance_f(chance))
- RemoveSpellsCausingAura(auraType);
-}
-
-uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss)
-{
- if (!pVictim->isAlive() || pVictim->isInFlight() || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
- return 0;
-
- //You don't lose health from damage taken from another player while in a sanctuary
- //You still see it in the combat log though
- if(pVictim != this && GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER)
- {
- const AreaTableEntry *area = GetAreaEntryByAreaID(pVictim->GetAreaId());
- if(area && area->flags & AREA_FLAG_SANCTUARY) //sanctuary
- return 0;
- }
-
- // remove affects from victim (including from 0 damage and DoTs)
- if(pVictim != this)
- pVictim->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
-
- // remove affects from attacker at any non-DoT damage (including 0 damage)
- if( damagetype != DOT)
- {
- RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
- RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
-
- if(pVictim != this)
- RemoveSpellsCausingAura(SPELL_AURA_MOD_INVISIBILITY);
-
- if(pVictim->GetTypeId() == TYPEID_PLAYER && !pVictim->IsStandState() && !pVictim->hasUnitState(UNIT_STAT_STUNNED))
- pVictim->SetStandState(PLAYER_STATE_NONE);
- }
-
- //Script Event damage Deal
- if( GetTypeId()== TYPEID_UNIT && ((Creature *)this)->AI())
- ((Creature *)this)->AI()->DamageDeal(pVictim, damage);
- //Script Event damage taken
- if( pVictim->GetTypeId()== TYPEID_UNIT && ((Creature *)pVictim)->AI() )
- ((Creature *)pVictim)->AI()->DamageTaken(this, damage);
-
- if(!damage)
- {
- // Rage from physical damage received .
- if(cleanDamage && cleanDamage->damage && (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL) && pVictim->GetTypeId() == TYPEID_PLAYER && (pVictim->getPowerType() == POWER_RAGE))
- ((Player*)pVictim)->RewardRage(cleanDamage->damage, 0, false);
-
- return 0;
- }
-
- pVictim->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_FEAR, damage);
- // root type spells do not dispell the root effect
- if(!spellProto || spellProto->Mechanic != MECHANIC_ROOT)
- pVictim->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_ROOT, damage);
-
- if(pVictim->GetTypeId() != TYPEID_PLAYER)
- {
- // no xp,health if type 8 /critters/
- if ( pVictim->GetCreatureType() == CREATURE_TYPE_CRITTER)
- {
- pVictim->setDeathState(JUST_DIED);
- pVictim->SetHealth(0);
-
- // allow loot only if has loot_id in creature_template
- CreatureInfo const* cInfo = ((Creature*)pVictim)->GetCreatureInfo();
- if(cInfo && cInfo->lootid)
- pVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
-
- // some critters required for quests
- if(GetTypeId() == TYPEID_PLAYER)
- ((Player*)this)->KilledMonster(pVictim->GetEntry(),pVictim->GetGUID());
-
- return damage;
- }
-
- if(!pVictim->isInCombat() && ((Creature*)pVictim)->AI())
- ((Creature*)pVictim)->AI()->AttackStart(this);
- }
-
- DEBUG_LOG("DealDamageStart");
-
- uint32 health = pVictim->GetHealth();
- sLog.outDetail("deal dmg:%d to health:%d ",damage,health);
-
- // duel ends when player has 1 or less hp
- bool duel_hasEnded = false;
- if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->duel && damage >= (health-1))
- {
- // prevent kill only if killed in duel and killed by opponent or opponent controlled creature
- if(((Player*)pVictim)->duel->opponent==this || ((Player*)pVictim)->duel->opponent->GetGUID() == GetOwnerGUID())
- damage = health-1;
-
- duel_hasEnded = true;
- }
- //Get in CombatState
- if(pVictim != this && damagetype != DOT)
- {
- SetInCombatWith(pVictim);
- pVictim->SetInCombatWith(this);
-
- if(Player* attackedPlayer = pVictim->GetCharmerOrOwnerPlayerOrPlayerItself())
- SetContestedPvP(attackedPlayer);
- }
-
- // Rage from Damage made (only from direct weapon damage)
- if( cleanDamage && damagetype==DIRECT_DAMAGE && this != pVictim && GetTypeId() == TYPEID_PLAYER && (getPowerType() == POWER_RAGE))
- {
- uint32 weaponSpeedHitFactor;
-
- switch(cleanDamage->attackType)
- {
- case BASE_ATTACK:
- {
- if(cleanDamage->hitOutCome == MELEE_HIT_CRIT)
- weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 7);
- else
- weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 3.5f);
-
- ((Player*)this)->RewardRage(damage, weaponSpeedHitFactor, true);
-
- break;
- }
- case OFF_ATTACK:
- {
- if(cleanDamage->hitOutCome == MELEE_HIT_CRIT)
- weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 3.5f);
- else
- weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 1.75f);
-
- ((Player*)this)->RewardRage(damage, weaponSpeedHitFactor, true);
-
- break;
- }
- case RANGED_ATTACK:
- break;
- }
- }
-
- if(pVictim->GetTypeId() == TYPEID_PLAYER && GetTypeId() == TYPEID_PLAYER)
- {
- if(((Player*)pVictim)->InBattleGround())
- {
- Player *killer = ((Player*)this);
- if(killer != ((Player*)pVictim))
- if(BattleGround *bg = killer->GetBattleGround())
- bg->UpdatePlayerScore(killer, SCORE_DAMAGE_DONE, damage);
- }
- }
-
- if (pVictim->GetTypeId() == TYPEID_UNIT && !((Creature*)pVictim)->isPet() && !((Creature*)pVictim)->hasLootRecipient())
- ((Creature*)pVictim)->SetLootRecipient(this);
- if (health <= damage)
- {
- DEBUG_LOG("DealDamage: victim just died");
-
- // find player: owner of controlled `this` or `this` itself maybe
- Player *player = GetCharmerOrOwnerPlayerOrPlayerItself();
-
- if(pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->GetLootRecipient())
- player = ((Creature*)pVictim)->GetLootRecipient();
- // Reward player, his pets, and group/raid members
- // call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
- if(player && player!=pVictim)
- if(player->RewardPlayerAndGroupAtKill(pVictim))
- player->ProcDamageAndSpell(pVictim,PROC_FLAG_KILL_XP_GIVER,PROC_FLAG_NONE);
-
- DEBUG_LOG("DealDamageAttackStop");
-
- // stop combat
- pVictim->CombatStop();
- pVictim->getHostilRefManager().deleteReferences();
-
- bool damageFromSpiritOfRedemtionTalent = spellProto && spellProto->Id == 27795;
-
- // if talent known but not triggered (check priest class for speedup check)
- Aura* spiritOfRedemtionTalentReady = NULL;
- if( !damageFromSpiritOfRedemtionTalent && // not called from SPELL_AURA_SPIRIT_OF_REDEMPTION
- pVictim->GetTypeId()==TYPEID_PLAYER && pVictim->getClass()==CLASS_PRIEST )
- {
- AuraList const& vDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
- for(AuraList::const_iterator itr = vDummyAuras.begin(); itr != vDummyAuras.end(); ++itr)
- {
- if((*itr)->GetSpellProto()->SpellIconID==1654)
- {
- spiritOfRedemtionTalentReady = *itr;
- break;
- }
- }
- }
-
- DEBUG_LOG("SET JUST_DIED");
- if(!spiritOfRedemtionTalentReady)
- pVictim->setDeathState(JUST_DIED);
-
- DEBUG_LOG("DealDamageHealth1");
-
- if(spiritOfRedemtionTalentReady)
- {
- // save value before aura remove
- uint32 ressSpellId = pVictim->GetUInt32Value(PLAYER_SELF_RES_SPELL);
- if(!ressSpellId)
- ressSpellId = ((Player*)pVictim)->GetResurrectionSpellId();
-
- //Remove all expected to remove at death auras (most important negative case like DoT or periodic triggers)
- pVictim->RemoveAllAurasOnDeath();
-
- // restore for use at real death
- pVictim->SetUInt32Value(PLAYER_SELF_RES_SPELL,ressSpellId);
-
- // FORM_SPIRITOFREDEMPTION and related auras
- pVictim->CastSpell(pVictim,27827,true,NULL,spiritOfRedemtionTalentReady);
- }
- else
- pVictim->SetHealth(0);
-
- // remember victim PvP death for corpse type and corpse reclaim delay
- // at original death (not at SpiritOfRedemtionTalent timeout)
- if( pVictim->GetTypeId()==TYPEID_PLAYER && !damageFromSpiritOfRedemtionTalent )
- ((Player*)pVictim)->SetPvPDeath(player!=NULL);
-
- // Call KilledUnit for creatures
- if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->AI())
- ((Creature*)this)->AI()->KilledUnit(pVictim);
-
- // 10% durability loss on death
- // clean InHateListOf
- if (pVictim->GetTypeId() == TYPEID_PLAYER)
- {
- // only if not player and not controlled by player pet. And not at BG
- if (durabilityLoss && !player && !((Player*)pVictim)->InBattleGround())
- {
- DEBUG_LOG("We are dead, loosing 10 percents durability");
- ((Player*)pVictim)->DurabilityLossAll(0.10f,false);
- // durability lost message
- WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0);
- ((Player*)pVictim)->GetSession()->SendPacket(&data);
- }
- }
- else // creature died
- {
- DEBUG_LOG("DealDamageNotPlayer");
- Creature *cVictim = (Creature*)pVictim;
-
- if(!cVictim->isPet())
- {
- cVictim->DeleteThreatList();
- cVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
- }
- // Call creature just died function
- if (cVictim->AI())
- cVictim->AI()->JustDied(this);
-
- // Dungeon specific stuff, only applies to players killing creatures
- if(cVictim->GetInstanceId())
- {
- Map *m = cVictim->GetMap();
- Player *creditedPlayer = GetCharmerOrOwnerPlayerOrPlayerItself();
- // TODO: do instance binding anyway if the charmer/owner is offline
-
- if(m->IsDungeon() && creditedPlayer)
- {
- if(m->IsRaid() || m->IsHeroic())
- {
- if(cVictim->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
- ((InstanceMap *)m)->PermBindAllPlayers(creditedPlayer);
- }
- else
- {
- // the reset time is set but not added to the scheduler
- // until the players leave the instance
- time_t resettime = cVictim->GetRespawnTimeEx() + 2 * HOUR;
- if(InstanceSave *save = sInstanceSaveManager.GetInstanceSave(cVictim->GetInstanceId()))
- if(save->GetResetTime() < resettime) save->SetResetTime(resettime);
- }
- }
- }
- }
-
- // last damage from non duel opponent or opponent controlled creature
- if(duel_hasEnded)
- {
- assert(pVictim->GetTypeId()==TYPEID_PLAYER);
- Player *he = (Player*)pVictim;
-
- assert(he->duel);
-
- he->duel->opponent->CombatStopWithPets(true);
- he->CombatStopWithPets(true);
-
- he->DuelComplete(DUEL_INTERUPTED);
- }
-
- // battleground things (do this at the end, so the death state flag will be properly set to handle in the bg->handlekill)
- if(pVictim->GetTypeId() == TYPEID_PLAYER && (((Player*)pVictim)->InBattleGround()))
- {
- Player *killed = ((Player*)pVictim);
- Player *killer = NULL;
- if(GetTypeId() == TYPEID_PLAYER)
- killer = ((Player*)this);
- else if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet())
- {
- Unit *owner = GetOwner();
- if(owner && owner->GetTypeId() == TYPEID_PLAYER)
- killer = ((Player*)owner);
- }
-
- if(killer)
- if(BattleGround *bg = killed->GetBattleGround())
- bg->HandleKillPlayer(killed, killer); // drop flags and etc
- }
- }
- else // if (health <= damage)
- {
- DEBUG_LOG("DealDamageAlive");
-
- pVictim->ModifyHealth(- (int32)damage);
-
- // Check if health is below 20% (apply damage before to prevent case when after ProcDamageAndSpell health < damage
- if(pVictim->GetHealth()*5 < pVictim->GetMaxHealth())
- {
- uint32 procVictim = PROC_FLAG_NONE;
-
- // if just dropped below 20% (for CheatDeath)
- if((pVictim->GetHealth()+damage)*5 > pVictim->GetMaxHealth())
- procVictim = PROC_FLAG_LOW_HEALTH;
-
- ProcDamageAndSpell(pVictim,PROC_FLAG_TARGET_LOW_HEALTH,procVictim);
- }
-
- if(damagetype != DOT)
- {
- if(getVictim())
- {
- // if have target and damage pVictim just call AI recation
- if(pVictim != getVictim() && pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->AI())
- ((Creature*)pVictim)->AI()->AttackedBy(this);
- }
- else
- {
- // if not have main target then attack state with target (including AI call)
- //start melee attacks only after melee hit
- Attack(pVictim,(damagetype == DIRECT_DAMAGE));
- }
- }
-
- // polymorphed and other negative transformed cases
- if(pVictim->getTransForm() && pVictim->hasUnitState(UNIT_STAT_CONFUSED))
- pVictim->RemoveAurasDueToSpell(pVictim->getTransForm());
-
- if(damagetype == DIRECT_DAMAGE|| damagetype == SPELL_DIRECT_DAMAGE)
- pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_DIRECT_DAMAGE);
-
- if (pVictim->GetTypeId() != TYPEID_PLAYER)
- {
- if(spellProto && IsDamageToThreatSpell(spellProto))
- pVictim->AddThreat(this, damage*2, damageSchoolMask, spellProto);
- else
- pVictim->AddThreat(this, damage, damageSchoolMask, spellProto);
- }
- else // victim is a player
- {
- // Rage from damage received
- if(this != pVictim && pVictim->getPowerType() == POWER_RAGE)
- {
- uint32 rage_damage = damage + (cleanDamage ? cleanDamage->damage : 0);
- ((Player*)pVictim)->RewardRage(rage_damage, 0, false);
- }
-
- // random durability for items (HIT TAKEN)
- if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_DAMAGE)))
- {
- EquipmentSlots slot = EquipmentSlots(urand(0,EQUIPMENT_SLOT_END-1));
- ((Player*)pVictim)->DurabilityPointLossForEquipSlot(slot);
- }
- }
-
- if(GetTypeId()==TYPEID_PLAYER)
- {
- // random durability for items (HIT DONE)
- if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_DAMAGE)))
- {
- EquipmentSlots slot = EquipmentSlots(urand(0,EQUIPMENT_SLOT_END-1));
- ((Player*)this)->DurabilityPointLossForEquipSlot(slot);
- }
- }
-
- // TODO: Store auras by interrupt flag to speed this up.
- AuraMap& vAuras = pVictim->GetAuras();
- for (AuraMap::iterator i = vAuras.begin(), next; i != vAuras.end(); i = next)
- {
- const SpellEntry *se = i->second->GetSpellProto();
- next = i; ++next;
- if( se->AuraInterruptFlags & AURA_INTERRUPT_FLAG_DAMAGE )
- {
- bool remove = true;
- if (se->procFlags & (1<<3))
- {
- if (!roll_chance_i(se->procChance))
- remove = false;
- }
- if (remove)
- {
- pVictim->RemoveAurasDueToSpell(i->second->GetId());
- // FIXME: this may cause the auras with proc chance to be rerolled several times
- next = vAuras.begin();
- }
- }
- }
-
- if (damagetype != NODAMAGE && damage && pVictim->GetTypeId() == TYPEID_PLAYER)
- {
- if( damagetype != DOT )
- {
- for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
- {
- // skip channeled spell (processed differently below)
- if (i == CURRENT_CHANNELED_SPELL)
- continue;
-
- if(Spell* spell = pVictim->m_currentSpells[i])
- if(spell->getState() == SPELL_STATE_PREPARING)
- spell->Delayed();
- }
- }
-
- if(Spell* spell = pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL])
- {
- if (spell->getState() == SPELL_STATE_CASTING)
- {
- uint32 channelInterruptFlags = spell->m_spellInfo->ChannelInterruptFlags;
- if( channelInterruptFlags & CHANNEL_FLAG_DELAY )
- {
- if(pVictim!=this) //don't shorten the duration of channeling if you damage yourself
- spell->DelayedChannel();
- }
- else if( (channelInterruptFlags & (CHANNEL_FLAG_DAMAGE | CHANNEL_FLAG_DAMAGE2)) )
- {
- sLog.outDetail("Spell %u canceled at damage!",spell->m_spellInfo->Id);
- pVictim->InterruptSpell(CURRENT_CHANNELED_SPELL);
- }
- }
- else if (spell->getState() == SPELL_STATE_DELAYED)
- // break channeled spell in delayed state on damage
- {
- sLog.outDetail("Spell %u canceled at damage!",spell->m_spellInfo->Id);
- pVictim->InterruptSpell(CURRENT_CHANNELED_SPELL);
- }
- }
- }
-
- // last damage from duel opponent
- if(duel_hasEnded)
- {
- assert(pVictim->GetTypeId()==TYPEID_PLAYER);
- Player *he = (Player*)pVictim;
-
- assert(he->duel);
-
- he->SetHealth(1);
-
- he->duel->opponent->CombatStopWithPets(true);
- he->CombatStopWithPets(true);
-
- he->CastSpell(he, 7267, true); // beg
- he->DuelComplete(DUEL_WON);
- }
- }
-
- DEBUG_LOG("DealDamageEnd returned %d damage", damage);
-
- return damage;
-}
-
-void Unit::CastStop(uint32 except_spellid)
-{
- for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
- if (m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id!=except_spellid)
- InterruptSpell(i,false);
-}
-
-void Unit::CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
-
- if(!spellInfo)
- {
- sLog.outError("CastSpell: unknown spell id %i by caster: %s %u)", spellId,(GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
- return;
- }
-
- CastSpell(Victim,spellInfo,triggered,castItem,triggredByAura, originalCaster);
-}
-
-void Unit::CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
-{
- if(!spellInfo)
- {
- sLog.outError("CastSpell: unknown spell by caster: %s %u)", (GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
- return;
- }
-
- if (castItem)
- DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
-
- if(!originalCaster && triggredByAura)
- originalCaster = triggredByAura->GetCasterGUID();
-
- Spell *spell = new Spell(this, spellInfo, triggered, originalCaster );
-
- SpellCastTargets targets;
- targets.setUnitTarget( Victim );
- spell->m_CastItem = castItem;
- spell->prepare(&targets, triggredByAura);
-}
-
-void Unit::CastCustomSpell(Unit* Victim,uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
-
- if(!spellInfo)
- {
- sLog.outError("CastCustomSpell: unknown spell id %i\n", spellId);
- return;
- }
-
- CastCustomSpell(Victim,spellInfo,bp0,bp1,bp2,triggered,castItem,triggredByAura, originalCaster);
-}
-
-void Unit::CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
-{
- if(!spellInfo)
- {
- sLog.outError("CastCustomSpell: unknown spell");
- return;
- }
-
- if (castItem)
- DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
-
- if(!originalCaster && triggredByAura)
- originalCaster = triggredByAura->GetCasterGUID();
-
- Spell *spell = new Spell(this, spellInfo, triggered, originalCaster);
-
- if(bp0)
- spell->m_currentBasePoints[0] = *bp0-int32(spellInfo->EffectBaseDice[0]);
-
- if(bp1)
- spell->m_currentBasePoints[1] = *bp1-int32(spellInfo->EffectBaseDice[1]);
-
- if(bp2)
- spell->m_currentBasePoints[2] = *bp2-int32(spellInfo->EffectBaseDice[2]);
-
- SpellCastTargets targets;
- targets.setUnitTarget( Victim );
- spell->m_CastItem = castItem;
- spell->prepare(&targets, triggredByAura);
-}
-
-// used for scripting
-void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
-
- if(!spellInfo)
- {
- sLog.outError("CastSpell(x,y,z): unknown spell id %i by caster: %s %u)", spellId,(GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
- return;
- }
-
- CastSpell(x, y, z,spellInfo,triggered,castItem,triggredByAura, originalCaster);
-}
-
-// used for scripting
-void Unit::CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
-{
- if(!spellInfo)
- {
- sLog.outError("CastSpell(x,y,z): unknown spell by caster: %s %u)", (GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
- return;
- }
-
- if (castItem)
- DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
-
- if(!originalCaster && triggredByAura)
- originalCaster = triggredByAura->GetCasterGUID();
-
- Spell *spell = new Spell(this, spellInfo, triggered, originalCaster );
-
- SpellCastTargets targets;
- targets.setDestination(x, y, z);
- spell->m_CastItem = castItem;
- spell->prepare(&targets, triggredByAura);
-}
-
-void Unit::DealFlatDamage(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit, bool isTriggeredSpell)
-{
- // TODO this in only generic way, check for exceptions
- DEBUG_LOG("DealFlatDamage (BEFORE) >> DMG:%u", *damage);
-
- // Per-damage calss calculation
- switch (spellInfo->DmgClass)
- {
- // Melee and Ranged Spells
- case SPELL_DAMAGE_CLASS_RANGED:
- case SPELL_DAMAGE_CLASS_MELEE:
- {
- // Calculate physical outcome
- MeleeHitOutcome outcome = RollPhysicalOutcomeAgainst(pVictim, BASE_ATTACK, spellInfo);
-
- //Used to store the Hit Outcome
- cleanDamage->hitOutCome = outcome;
-
- // Return miss/evade first (sends miss message)
- switch(outcome)
- {
- case MELEE_HIT_EVADE:
- {
- SendAttackStateUpdate(HITINFO_MISS, pVictim, 1, GetSpellSchoolMask(spellInfo), 0, 0,0,VICTIMSTATE_EVADES,0);
- *damage = 0;
- return;
- }
- case MELEE_HIT_MISS:
- {
- SendAttackStateUpdate(HITINFO_MISS, pVictim, 1, GetSpellSchoolMask(spellInfo), 0, 0,0,VICTIMSTATE_NORMAL,0);
- *damage = 0;
-
- if(GetTypeId()== TYPEID_PLAYER)
- ((Player*)this)->UpdateWeaponSkill(BASE_ATTACK);
-
- CastMeleeProcDamageAndSpell(pVictim,0,GetSpellSchoolMask(spellInfo),BASE_ATTACK,MELEE_HIT_MISS,spellInfo,isTriggeredSpell);
- return;
- }
- }
-
- // Hitinfo, Victimstate
- uint32 hitInfo = HITINFO_NORMALSWING;
- VictimState victimState = VICTIMSTATE_NORMAL;
-
- // Physical Damage
- if ( GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_NORMAL )
- {
- uint32 modDamage=*damage;
-
- // apply spellmod to Done damage
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_DAMAGE, *damage);
-
- //Calculate armor mitigation
- uint32 damageAfterArmor = CalcArmorReducedDamage(pVictim, *damage);
-
- // random durability for main hand weapon (ABSORB)
- if(damageAfterArmor < *damage)
- if(pVictim->GetTypeId() == TYPEID_PLAYER)
- if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_ABSORB)))
- ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EquipmentSlots(urand(EQUIPMENT_SLOT_START,EQUIPMENT_SLOT_BACK)));
-
- cleanDamage->damage += *damage - damageAfterArmor;
- *damage = damageAfterArmor;
- }
- // Magical Damage
- else
- {
- // Calculate damage bonus
- *damage = SpellDamageBonus(pVictim, spellInfo, *damage, SPELL_DIRECT_DAMAGE);
- }
-
- // Classify outcome
- switch (outcome)
- {
- case MELEE_HIT_BLOCK_CRIT:
- case MELEE_HIT_CRIT:
- {
- uint32 bonusDmg = *damage;
-
- // Apply crit_damage bonus
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, bonusDmg);
-
- uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
- AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS);
- for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
- if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
- bonusDmg = uint32(bonusDmg * ((*i)->GetModifier()->m_amount+100.0f)/100.0f);
-
- *damage += bonusDmg;
-
- // Resilience - reduce crit damage
- if (pVictim->GetTypeId()==TYPEID_PLAYER)
- {
- uint32 resilienceReduction = ((Player*)pVictim)->GetMeleeCritDamageReduction(*damage);
- cleanDamage->damage += resilienceReduction;
- *damage -= resilienceReduction;
- }
-
- *crit = true;
- hitInfo |= HITINFO_CRITICALHIT;
-
- ModifyAuraState(AURA_STATE_CRIT, true);
- StartReactiveTimer( REACTIVE_CRIT );
-
- if(getClass()==CLASS_HUNTER)
- {
- ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, true);
- StartReactiveTimer( REACTIVE_HUNTER_CRIT );
- }
-
- if ( outcome == MELEE_HIT_BLOCK_CRIT )
- {
- uint32 blocked_amount = uint32(pVictim->GetShieldBlockValue());
- if (blocked_amount >= *damage)
- {
- hitInfo |= HITINFO_SWINGNOHITSOUND;
- victimState = VICTIMSTATE_BLOCKS;
- cleanDamage->damage += *damage; // To Help Calculate Rage
- *damage = 0;
- }
- else
- {
- // To Help Calculate Rage
- cleanDamage->damage += blocked_amount;
- *damage = *damage - blocked_amount;
- }
-
- pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
- pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
-
- if(pVictim->GetTypeId() == TYPEID_PLAYER)
- {
- // Update defense
- ((Player*)pVictim)->UpdateDefense();
-
- // random durability for main hand weapon (BLOCK)
- if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
- ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
- }
- }
- break;
- }
- case MELEE_HIT_PARRY:
- {
- cleanDamage->damage += *damage; // To Help Calculate Rage
- *damage = 0;
- victimState = VICTIMSTATE_PARRY;
-
- // Counter-attack ( explained in Unit::DoAttackDamage() )
- if(pVictim->GetTypeId()==TYPEID_PLAYER || !(((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN) )
- {
- // Get attack timers
- float offtime = float(pVictim->getAttackTimer(OFF_ATTACK));
- float basetime = float(pVictim->getAttackTimer(BASE_ATTACK));
-
- // Reduce attack time
- if (pVictim->haveOffhandWeapon() && offtime < basetime)
- {
- float percent20 = pVictim->GetAttackTime(OFF_ATTACK) * 0.20;
- float percent60 = 3 * percent20;
- if(offtime > percent20 && offtime <= percent60)
- {
- pVictim->setAttackTimer(OFF_ATTACK, uint32(percent20));
- }
- else if(offtime > percent60)
- {
- offtime -= 2 * percent20;
- pVictim->setAttackTimer(OFF_ATTACK, uint32(offtime));
- }
- }
- else
- {
- float percent20 = pVictim->GetAttackTime(BASE_ATTACK) * 0.20;
- float percent60 = 3 * percent20;
- if(basetime > percent20 && basetime <= percent60)
- {
- pVictim->setAttackTimer(BASE_ATTACK, uint32(percent20));
- }
- else if(basetime > percent60)
- {
- basetime -= 2 * percent20;
- pVictim->setAttackTimer(BASE_ATTACK, uint32(basetime));
- }
- }
- }
-
- if(pVictim->GetTypeId() == TYPEID_PLAYER)
- {
- // Update victim defense ?
- ((Player*)pVictim)->UpdateDefense();
-
- // random durability for main hand weapon (PARRY)
- if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_PARRY)))
- ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_MAINHAND);
- }
-
- // Set parry flags
- pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
-
- // Mongoose bite - set only Counterattack here
- if (pVictim->getClass() == CLASS_HUNTER)
- {
- pVictim->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true);
- pVictim->StartReactiveTimer( REACTIVE_HUNTER_PARRY );
- }
- else
- {
- pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
- pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
- }
- break;
- }
- case MELEE_HIT_DODGE:
- {
- if(pVictim->GetTypeId() == TYPEID_PLAYER)
- ((Player*)pVictim)->UpdateDefense();
-
- cleanDamage->damage += *damage; // To Help Calculate Rage
- *damage = 0;
- hitInfo |= HITINFO_SWINGNOHITSOUND;
- victimState = VICTIMSTATE_DODGE;
-
- // Set dodge flags
- pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
-
- // Overpower
- if (GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR)
- {
- ((Player*)this)->AddComboPoints(pVictim, 1);
- StartReactiveTimer( REACTIVE_OVERPOWER );
- }
-
- // Riposte
- if (pVictim->getClass() != CLASS_ROGUE)
- {
- pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
- pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
- }
- break;
- }
- case MELEE_HIT_BLOCK:
- {
- uint32 blocked_amount = uint32(pVictim->GetShieldBlockValue());
- if (blocked_amount >= *damage)
- {
- hitInfo |= HITINFO_SWINGNOHITSOUND;
- victimState = VICTIMSTATE_BLOCKS;
- cleanDamage->damage += *damage; // To Help Calculate Rage
- *damage = 0;
- }
- else
- {
- // To Help Calculate Rage
- cleanDamage->damage += blocked_amount;
- *damage = *damage - blocked_amount;
- }
-
- pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
- pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
-
- if(pVictim->GetTypeId() == TYPEID_PLAYER)
- {
- // Update defense
- ((Player*)pVictim)->UpdateDefense();
-
- // random durability for main hand weapon (BLOCK)
- if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
- ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
- }
- break;
- }
- case MELEE_HIT_EVADE: // already processed early
- case MELEE_HIT_MISS: // already processed early
- case MELEE_HIT_GLANCING:
- case MELEE_HIT_CRUSHING:
- case MELEE_HIT_NORMAL:
- break;
- }
-
- // do all damage=0 cases here
- if(*damage == 0)
- CastMeleeProcDamageAndSpell(pVictim,0,GetSpellSchoolMask(spellInfo),BASE_ATTACK,outcome,spellInfo,isTriggeredSpell);
-
- break;
- }
- // Magical Attacks
- case SPELL_DAMAGE_CLASS_NONE:
- case SPELL_DAMAGE_CLASS_MAGIC:
- {
- // Calculate damage bonus
- *damage = SpellDamageBonus(pVictim, spellInfo, *damage, SPELL_DIRECT_DAMAGE);
-
- *crit = isSpellCrit(pVictim, spellInfo, GetSpellSchoolMask(spellInfo), BASE_ATTACK);
- if (*crit)
- {
- *damage = SpellCriticalBonus(spellInfo, *damage, pVictim);
-
- // Resilience - reduce crit damage
- if (pVictim && pVictim->GetTypeId()==TYPEID_PLAYER)
- {
- uint32 damage_reduction = ((Player *)pVictim)->GetSpellCritDamageReduction(*damage);
- if(*damage > damage_reduction)
- *damage -= damage_reduction;
- else
- *damage = 0;
- }
-
- cleanDamage->hitOutCome = MELEE_HIT_CRIT;
- }
- // spell proc all magic damage==0 case in this function
- if(*damage == 0)
- {
- // Procflags
- uint32 procAttacker = PROC_FLAG_HIT_SPELL;
- uint32 procVictim = (PROC_FLAG_STRUCK_SPELL|PROC_FLAG_TAKE_DAMAGE);
-
- ProcDamageAndSpell(pVictim, procAttacker, procVictim, 0, GetSpellSchoolMask(spellInfo), spellInfo, isTriggeredSpell);
- }
-
- break;
- }
- }
-
- // TODO this in only generic way, check for exceptions
- DEBUG_LOG("DealFlatDamage (AFTER) >> DMG:%u", *damage);
-}
-
-uint32 Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool isTriggeredSpell, bool useSpellDamage)
-{
- if(!this || !pVictim)
- return 0;
- if(!this->isAlive() || !pVictim->isAlive())
- return 0;
-
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
- if(!spellInfo)
- return 0;
-
- CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL);
- bool crit = false;
-
- if (useSpellDamage)
- DealFlatDamage(pVictim, spellInfo, &damage, &cleanDamage, &crit, isTriggeredSpell);
-
- // If we actually dealt some damage (spell proc's for 0 damage (normal and magic) called in DealFlatDamage)
- if(damage > 0)
- {
- // Calculate absorb & resists
- uint32 absorb = 0;
- uint32 resist = 0;
-
- CalcAbsorbResist(pVictim,GetSpellSchoolMask(spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
-
- //No more damage left, target absorbed and/or resisted all damage
- if (damage > absorb + resist)
- damage -= absorb + resist; //Remove Absorbed and Resisted from damage actually dealt
- else
- {
- uint32 HitInfo = HITINFO_SWINGNOHITSOUND;
-
- if (absorb)
- HitInfo |= HITINFO_ABSORB;
- if (resist)
- {
- HitInfo |= HITINFO_RESIST;
- ProcDamageAndSpell(pVictim, PROC_FLAG_TARGET_RESISTS, PROC_FLAG_RESIST_SPELL, 0, GetSpellSchoolMask(spellInfo), spellInfo,isTriggeredSpell);
- }
-
- //Send resist
- SendAttackStateUpdate(HitInfo, pVictim, 1, GetSpellSchoolMask(spellInfo), damage, absorb,resist,VICTIMSTATE_NORMAL,0);
- return 0;
- }
-
- // Deal damage done
- damage = DealDamage(pVictim, damage, &cleanDamage, SPELL_DIRECT_DAMAGE, GetSpellSchoolMask(spellInfo), spellInfo, true);
-
- // Send damage log
- sLog.outDetail("SpellNonMeleeDamageLog: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u,absorb is %u,resist is %u",
- GetGUIDLow(), GetTypeId(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, spellID, absorb,resist);
-
- // Actual log sent to client
- SendSpellNonMeleeDamageLog(pVictim, spellID, damage, GetSpellSchoolMask(spellInfo), absorb, resist, false, 0, crit);
-
- // Procflags
- uint32 procAttacker = PROC_FLAG_HIT_SPELL;
- uint32 procVictim = (PROC_FLAG_STRUCK_SPELL|PROC_FLAG_TAKE_DAMAGE);
-
- if (crit)
- {
- procAttacker |= PROC_FLAG_CRIT_SPELL;
- procVictim |= PROC_FLAG_STRUCK_CRIT_SPELL;
- }
-
- ProcDamageAndSpell(pVictim, procAttacker, procVictim, damage, GetSpellSchoolMask(spellInfo), spellInfo, isTriggeredSpell);
-
- return damage;
- }
- else
- {
- // all spell proc for 0 normal and magic damage called in DealFlatDamage
-
- //Check for rage
- if(cleanDamage.damage)
- // Rage from damage received.
- if(pVictim->GetTypeId() == TYPEID_PLAYER && (pVictim->getPowerType() == POWER_RAGE))
- ((Player*)pVictim)->RewardRage(cleanDamage.damage, 0, false);
-
- return 0;
- }
-}
-
-void Unit::HandleEmoteCommand(uint32 anim_id)
-{
- WorldPacket data( SMSG_EMOTE, 12 );
- data << anim_id << GetGUID();
- WPAssert(data.size() == 12);
-
- SendMessageToSet(&data, true);
-}
-
-uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage)
-{
- uint32 newdamage = 0;
- float armor = pVictim->GetArmor();
- // Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura
- armor += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, SPELL_SCHOOL_MASK_NORMAL);
-
- if (armor<0.0f) armor=0.0f;
-
- float tmpvalue = 0.0f;
- if(getLevel() <= 59) //Level 1-59
- tmpvalue = armor / (armor + 400.0f + 85.0f * getLevel());
- else if(getLevel() < 70) //Level 60-69
- tmpvalue = armor / (armor - 22167.5f + 467.5f * getLevel());
- else //Level 70+
- tmpvalue = armor / (armor + 10557.5f);
-
- if(tmpvalue < 0.0f)
- tmpvalue = 0.0f;
- if(tmpvalue > 0.75f)
- tmpvalue = 0.75f;
- newdamage = uint32(damage - (damage * tmpvalue));
-
- return (newdamage > 1) ? newdamage : 1;
-}
-
-void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist)
-{
- if(!pVictim || !pVictim->isAlive() || !damage)
- return;
-
- // Magic damage, check for resists
- if ((schoolMask & SPELL_SCHOOL_MASK_NORMAL)==0)
- {
- // Get base victim resistance for school
- float tmpvalue2 = (float)pVictim->GetResistance(GetFirstSchoolInMask(schoolMask));
- // Ignore resistance by self SPELL_AURA_MOD_TARGET_RESISTANCE aura
- tmpvalue2 += (float)GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask);
-
- tmpvalue2 *= (float)(0.15f / getLevel());
- if (tmpvalue2 < 0.0f)
- tmpvalue2 = 0.0f;
- if (tmpvalue2 > 0.75f)
- tmpvalue2 = 0.75f;
- uint32 ran = urand(0, 100);
- uint32 faq[4] = {24,6,4,6};
- uint8 m = 0;
- float Binom = 0.0f;
- for (uint8 i = 0; i < 4; i++)
- {
- Binom += 2400 *( powf(tmpvalue2, i) * powf( (1-tmpvalue2), (4-i)))/faq[i];
- if (ran > Binom )
- ++m;
- else
- break;
- }
- if (damagetype == DOT && m == 4)
- *resist += uint32(damage - 1);
- else
- *resist += uint32(damage * m / 4);
- if(*resist > damage)
- *resist = damage;
- }
- else
- *resist = 0;
-
- int32 RemainingDamage = damage - *resist;
-
- // absorb without mana cost
- int32 reflectDamage = 0;
- Aura* reflectAura = NULL;
- AuraList const& vSchoolAbsorb = pVictim->GetAurasByType(SPELL_AURA_SCHOOL_ABSORB);
- for(AuraList::const_iterator i = vSchoolAbsorb.begin(), next; i != vSchoolAbsorb.end() && RemainingDamage > 0; i = next)
- {
- next = i; ++next;
-
- if (((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
- continue;
-
- // Cheat Death
- if((*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && (*i)->GetSpellProto()->SpellIconID == 2109)
- {
- if (((Player*)pVictim)->HasSpellCooldown(31231))
- continue;
- if (pVictim->GetHealth() <= RemainingDamage)
- {
- int32 chance = (*i)->GetModifier()->m_amount;
- if (roll_chance_i(chance))
- {
- pVictim->CastSpell(pVictim,31231,true);
- ((Player*)pVictim)->AddSpellCooldown(31231,0,time(NULL)+60);
-
- // with health > 10% lost health until health==10%, in other case no losses
- uint32 health10 = pVictim->GetMaxHealth()/10;
- RemainingDamage = pVictim->GetHealth() > health10 ? pVictim->GetHealth() - health10 : 0;
- }
- }
- continue;
- }
-
- int32 currentAbsorb;
-
- //Reflective Shield
- if ((pVictim != this) && (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_PRIEST && (*i)->GetSpellProto()->SpellFamilyFlags == 0x1)
- {
- if(Unit* caster = (*i)->GetCaster())
- {
- AuraList const& vOverRideCS = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
- for(AuraList::const_iterator k = vOverRideCS.begin(); k != vOverRideCS.end(); ++k)
- {
- switch((*k)->GetModifier()->m_miscvalue)
- {
- case 5065: // Rank 1
- case 5064: // Rank 2
- case 5063: // Rank 3
- case 5062: // Rank 4
- case 5061: // Rank 5
- {
- if(RemainingDamage >= (*i)->GetModifier()->m_amount)
- reflectDamage = (*i)->GetModifier()->m_amount * (*k)->GetModifier()->m_amount/100;
- else
- reflectDamage = (*k)->GetModifier()->m_amount * RemainingDamage/100;
- reflectAura = *i;
-
- } break;
- default: break;
- }
-
- if(reflectDamage)
- break;
- }
- }
- }
-
- if (RemainingDamage >= (*i)->GetModifier()->m_amount)
- {
- currentAbsorb = (*i)->GetModifier()->m_amount;
- pVictim->RemoveAurasDueToSpell((*i)->GetId());
- next = vSchoolAbsorb.begin();
- }
- else
- {
- currentAbsorb = RemainingDamage;
- (*i)->GetModifier()->m_amount -= RemainingDamage;
- }
-
- RemainingDamage -= currentAbsorb;
- }
- // do not cast spells while looping auras; auras can get invalid otherwise
- if (reflectDamage)
- pVictim->CastCustomSpell(this, 33619, &reflectDamage, NULL, NULL, true, NULL, reflectAura);
-
- // absorb by mana cost
- AuraList const& vManaShield = pVictim->GetAurasByType(SPELL_AURA_MANA_SHIELD);
- for(AuraList::const_iterator i = vManaShield.begin(), next; i != vManaShield.end() && RemainingDamage > 0; i = next)
- {
- next = i; ++next;
-
- // check damage school mask
- if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
- continue;
-
- int32 currentAbsorb;
- if (RemainingDamage >= (*i)->GetModifier()->m_amount)
- currentAbsorb = (*i)->GetModifier()->m_amount;
- else
- currentAbsorb = RemainingDamage;
-
- float manaMultiplier = (*i)->GetSpellProto()->EffectMultipleValue[(*i)->GetEffIndex()];
- if(Player *modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod((*i)->GetId(), SPELLMOD_MULTIPLE_VALUE, manaMultiplier);
-
- int32 maxAbsorb = int32(pVictim->GetPower(POWER_MANA) / manaMultiplier);
- if (currentAbsorb > maxAbsorb)
- currentAbsorb = maxAbsorb;
-
- (*i)->GetModifier()->m_amount -= currentAbsorb;
- if((*i)->GetModifier()->m_amount <= 0)
- {
- pVictim->RemoveAurasDueToSpell((*i)->GetId());
- next = vManaShield.begin();
- }
-
- int32 manaReduction = int32(currentAbsorb * manaMultiplier);
- pVictim->ApplyPowerMod(POWER_MANA, manaReduction, false);
-
- RemainingDamage -= currentAbsorb;
- }
-
- AuraList const& vSplitDamageFlat = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_FLAT);
- for(AuraList::const_iterator i = vSplitDamageFlat.begin(), next; i != vSplitDamageFlat.end() && RemainingDamage >= 0; i = next)
- {
- next = i; ++next;
-
- // check damage school mask
- if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
- continue;
-
- // Damage can be splitted only if aura has an alive caster
- Unit *caster = (*i)->GetCaster();
- if(!caster || caster == pVictim || !caster->IsInWorld() || !caster->isAlive())
- continue;
-
- int32 currentAbsorb;
- if (RemainingDamage >= (*i)->GetModifier()->m_amount)
- currentAbsorb = (*i)->GetModifier()->m_amount;
- else
- currentAbsorb = RemainingDamage;
-
- RemainingDamage -= currentAbsorb;
-
- SendSpellNonMeleeDamageLog(caster, (*i)->GetSpellProto()->Id, currentAbsorb, schoolMask, 0, 0, false, 0, false);
-
- CleanDamage cleanDamage = CleanDamage(currentAbsorb, BASE_ATTACK, MELEE_HIT_NORMAL);
- DealDamage(caster, currentAbsorb, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false);
- }
-
- AuraList const& vSplitDamagePct = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_PCT);
- for(AuraList::const_iterator i = vSplitDamagePct.begin(), next; i != vSplitDamagePct.end() && RemainingDamage >= 0; i = next)
- {
- next = i; ++next;
-
- // check damage school mask
- if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
- continue;
-
- // Damage can be splitted only if aura has an alive caster
- Unit *caster = (*i)->GetCaster();
- if(!caster || caster == pVictim || !caster->IsInWorld() || !caster->isAlive())
- continue;
-
- int32 splitted = int32(RemainingDamage * (*i)->GetModifier()->m_amount / 100.0f);
-
- RemainingDamage -= splitted;
-
- SendSpellNonMeleeDamageLog(caster, (*i)->GetSpellProto()->Id, splitted, schoolMask, 0, 0, false, 0, false);
-
- CleanDamage cleanDamage = CleanDamage(splitted, BASE_ATTACK, MELEE_HIT_NORMAL);
- DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false);
- }
-
- *absorb = damage - RemainingDamage - *resist;
-}
-
-void Unit::DoAttackDamage (Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchoolMask damageSchoolMask, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted, bool isTriggeredSpell)
-{
- MeleeHitOutcome outcome;
-
- // If is casted Melee spell, calculate like physical
- if(!spellCasted)
- outcome = RollMeleeOutcomeAgainst (pVictim, attType);
- else
- outcome = RollPhysicalOutcomeAgainst (pVictim, attType, spellCasted);
-
- if(outcome == MELEE_HIT_MISS ||outcome == MELEE_HIT_DODGE ||outcome == MELEE_HIT_BLOCK ||outcome == MELEE_HIT_PARRY)
- pVictim->AddThreat(this, 0.0f);
- switch(outcome)
- {
- case MELEE_HIT_EVADE:
- {
- *hitInfo |= HITINFO_MISS;
- *damage = 0;
- cleanDamage->damage = 0;
- return;
- }
- case MELEE_HIT_MISS:
- {
- *hitInfo |= HITINFO_MISS;
- *damage = 0;
- cleanDamage->damage = 0;
- if(GetTypeId()== TYPEID_PLAYER)
- ((Player*)this)->UpdateWeaponSkill(attType);
- return;
- }
- }
-
- /// If this is a creature and it attacks from behind it has a probability to daze it's victim
- if( (outcome==MELEE_HIT_CRIT || outcome==MELEE_HIT_CRUSHING || outcome==MELEE_HIT_NORMAL || outcome==MELEE_HIT_GLANCING) &&
- GetTypeId() != TYPEID_PLAYER && !((Creature*)this)->GetCharmerOrOwnerGUID() && !pVictim->HasInArc(M_PI, this) )
- {
- // -probability is between 0% and 40%
- // 20% base chance
- float Probability = 20;
-
- //there is a newbie protection, at level 10 just 7% base chance; assuming linear function
- if( pVictim->getLevel() < 30 )
- Probability = 0.65f*pVictim->getLevel()+0.5;
-
- uint32 VictimDefense=pVictim->GetDefenseSkillValue(this);
- uint32 AttackerMeleeSkill=GetUnitMeleeSkill(pVictim);
-
- Probability *= AttackerMeleeSkill/(float)VictimDefense;
-
- if(Probability > 40.0f)
- Probability = 40.0f;
-
- if(roll_chance_f(Probability))
- CastSpell(pVictim, 1604, true);
- }
-
- //Calculate the damage after armor mitigation if SPELL_SCHOOL_NORMAL
- if (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL)
- {
- uint32 damageAfterArmor = CalcArmorReducedDamage(pVictim, *damage);
-
- // random durability for main hand weapon (ABSORB)
- if(damageAfterArmor < *damage)
- if(pVictim->GetTypeId() == TYPEID_PLAYER)
- if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_ABSORB)))
- ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EquipmentSlots(urand(EQUIPMENT_SLOT_START,EQUIPMENT_SLOT_BACK)));
-
- cleanDamage->damage += *damage - damageAfterArmor;
- *damage = damageAfterArmor;
- }
-
- if(GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() != CREATURE_TYPE_CRITTER )
- ((Player*)this)->UpdateCombatSkills(pVictim, attType, outcome, false);
-
- if(GetTypeId() != TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER)
- ((Player*)pVictim)->UpdateCombatSkills(this, attType, outcome, true);
-
- switch (outcome)
- {
- case MELEE_HIT_BLOCK_CRIT:
- case MELEE_HIT_CRIT:
- {
- //*hitInfo = 0xEA;
- // 0xEA
- *hitInfo = HITINFO_CRITICALHIT | HITINFO_NORMALSWING2 | 0x8;
-
- // Crit bonus calc
- uint32 crit_bonus;
- crit_bonus = *damage;
-
- // Apply crit_damage bonus for melee spells
- if (spellCasted)
- {
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellCasted->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
-
- uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
- AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS);
- for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
- if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
- crit_bonus = uint32(crit_bonus * ((*i)->GetModifier()->m_amount+100.0f)/100.0f);
- }
-
- *damage += crit_bonus;
-
- uint32 resilienceReduction = 0;
-
- if(attType == RANGED_ATTACK)
- {
- int32 mod = pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE);
- *damage = int32((*damage) * float((100.0f + mod)/100.0f));
- // Resilience - reduce crit damage
- if (pVictim->GetTypeId()==TYPEID_PLAYER)
- resilienceReduction = ((Player*)pVictim)->GetRangedCritDamageReduction(*damage);
- }
- else
- {
- int32 mod = pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE);
- mod += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE);
- *damage = int32((*damage) * float((100.0f + mod)/100.0f));
- // Resilience - reduce crit damage
- if (pVictim->GetTypeId()==TYPEID_PLAYER)
- resilienceReduction = ((Player*)pVictim)->GetMeleeCritDamageReduction(*damage);
- }
-
- *damage -= resilienceReduction;
- cleanDamage->damage += resilienceReduction;
-
- if(GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() != CREATURE_TYPE_CRITTER )
- ((Player*)this)->UpdateWeaponSkill(attType);
-
- ModifyAuraState(AURA_STATE_CRIT, true);
- StartReactiveTimer( REACTIVE_CRIT );
-
- if(getClass()==CLASS_HUNTER)
- {
- ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, true);
- StartReactiveTimer( REACTIVE_HUNTER_CRIT );
- }
-
- if ( outcome == MELEE_HIT_BLOCK_CRIT )
- {
- *blocked_amount = pVictim->GetShieldBlockValue();
-
- if (pVictim->GetUnitBlockChance())
- pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD);
- else
- pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
-
- //Only set VICTIMSTATE_BLOCK on a full block
- if (*blocked_amount >= uint32(*damage))
- {
- *victimState = VICTIMSTATE_BLOCKS;
- *blocked_amount = uint32(*damage);
- }
-
- if(pVictim->GetTypeId() == TYPEID_PLAYER)
- {
- // Update defense
- ((Player*)pVictim)->UpdateDefense();
-
- // random durability for main hand weapon (BLOCK)
- if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
- ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
- }
-
- pVictim->ModifyAuraState(AURA_STATE_DEFENSE,true);
- pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
- break;
- }
-
- pVictim->HandleEmoteCommand(EMOTE_ONESHOT_WOUNDCRITICAL);
- break;
- }
- case MELEE_HIT_PARRY:
- {
- if(attType == RANGED_ATTACK) //range attack - no parry
- {
- outcome = MELEE_HIT_NORMAL;
- break;
- }
-
- cleanDamage->damage += *damage;
- *damage = 0;
- *victimState = VICTIMSTATE_PARRY;
-
- // instant (maybe with small delay) counter attack
- {
- float offtime = float(pVictim->getAttackTimer(OFF_ATTACK));
- float basetime = float(pVictim->getAttackTimer(BASE_ATTACK));
-
- // after parry nearest next attack time will reduced at %40 from full attack time.
- // The delay cannot be reduced to less than 20% of your weapon's base swing delay.
- if (pVictim->haveOffhandWeapon() && offtime < basetime)
- {
- float percent20 = pVictim->GetAttackTime(OFF_ATTACK)*0.20;
- float percent60 = 3*percent20;
- // set to 20% if in range 20%...20+40% of full time
- if(offtime > percent20 && offtime <= percent60)
- {
- pVictim->setAttackTimer(OFF_ATTACK,uint32(percent20));
- }
- // decrease at %40 from full time
- else if(offtime > percent60)
- {
- offtime -= 2*percent20;
- pVictim->setAttackTimer(OFF_ATTACK,uint32(offtime));
- }
- // ELSE not changed
- }
- else
- {
- float percent20 = pVictim->GetAttackTime(BASE_ATTACK)*0.20;
- float percent60 = 3*percent20;
- // set to 20% if in range 20%...20+40% of full time
- if(basetime > percent20 && basetime <= percent60)
- {
- pVictim->setAttackTimer(BASE_ATTACK,uint32(percent20));
- }
- // decrease at %40 from full time
- else if(basetime > percent60)
- {
- basetime -= 2*percent20;
- pVictim->setAttackTimer(BASE_ATTACK,uint32(basetime));
- }
- // ELSE not changed
- }
- }
-
- if(pVictim->GetTypeId() == TYPEID_PLAYER)
- {
- // Update victim defense ?
- ((Player*)pVictim)->UpdateDefense();
-
- // random durability for main hand weapon (PARRY)
- if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_PARRY)))
- ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_MAINHAND);
- }
-
- pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
-
- if (pVictim->getClass() == CLASS_HUNTER)
- {
- pVictim->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true);
- pVictim->StartReactiveTimer( REACTIVE_HUNTER_PARRY );
- }
- else
- {
- pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
- pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
- }
-
- CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
- return;
- }
- case MELEE_HIT_DODGE:
- {
- if(attType == RANGED_ATTACK) //range attack - no dodge
- {
- outcome = MELEE_HIT_NORMAL;
- break;
- }
-
- cleanDamage->damage += *damage;
- *damage = 0;
- *victimState = VICTIMSTATE_DODGE;
-
- if(pVictim->GetTypeId() == TYPEID_PLAYER)
- ((Player*)pVictim)->UpdateDefense();
-
- pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
-
- if (pVictim->getClass() != CLASS_ROGUE) // Riposte
- {
- pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
- pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
- }
-
- // Overpower
- if (GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR)
- {
- ((Player*)this)->AddComboPoints(pVictim, 1);
- StartReactiveTimer( REACTIVE_OVERPOWER );
- }
-
- CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
- return;
- }
- case MELEE_HIT_BLOCK:
- {
- *blocked_amount = pVictim->GetShieldBlockValue();
-
- if (pVictim->GetUnitBlockChance())
- pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD);
- else
- pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
-
- //Only set VICTIMSTATE_BLOCK on a full block
- if (*blocked_amount >= uint32(*damage))
- {
- *victimState = VICTIMSTATE_BLOCKS;
- *blocked_amount = uint32(*damage);
- }
-
- if(pVictim->GetTypeId() == TYPEID_PLAYER)
- {
- // Update defense
- ((Player*)pVictim)->UpdateDefense();
-
- // random durability for main hand weapon (BLOCK)
- if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
- ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
- }
-
- pVictim->ModifyAuraState(AURA_STATE_DEFENSE,true);
- pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
-
- break;
- }
- case MELEE_HIT_GLANCING:
- {
- float reducePercent = 1.0f; //damage factor
-
- // calculate base values and mods
- float baseLowEnd = 1.3;
- float baseHighEnd = 1.2;
- switch(getClass()) // lowering base values for casters
- {
- case CLASS_SHAMAN:
- case CLASS_PRIEST:
- case CLASS_MAGE:
- case CLASS_WARLOCK:
- case CLASS_DRUID:
- baseLowEnd -= 0.7;
- baseHighEnd -= 0.3;
- break;
- }
-
- float maxLowEnd = 0.6;
- switch(getClass()) // upper for melee classes
- {
- case CLASS_WARRIOR:
- case CLASS_ROGUE:
- maxLowEnd = 0.91; //If the attacker is a melee class then instead the lower value of 0.91
- }
-
- // calculate values
- int32 diff = int32(pVictim->GetDefenseSkillValue(this)) - int32(GetWeaponSkillValue(attType,pVictim));
- float lowEnd = baseLowEnd - ( 0.05f * diff );
- float highEnd = baseHighEnd - ( 0.03f * diff );
-
- // apply max/min bounds
- if ( lowEnd < 0.01f ) //the low end must not go bellow 0.01f
- lowEnd = 0.01f;
- else if ( lowEnd > maxLowEnd ) //the smaller value of this and 0.6 is kept as the low end
- lowEnd = maxLowEnd;
-
- if ( highEnd < 0.2f ) //high end limits
- highEnd = 0.2f;
- if ( highEnd > 0.99f )
- highEnd = 0.99f;
-
- if(lowEnd > highEnd) // prevent negative range size
- lowEnd = highEnd;
-
- reducePercent = lowEnd + rand_norm() * ( highEnd - lowEnd );
-
- *damage = uint32(reducePercent * *damage);
- cleanDamage->damage += *damage;
- *hitInfo |= HITINFO_GLANCING;
- break;
- }
- case MELEE_HIT_CRUSHING:
- {
- // 150% normal damage
- *damage += (*damage / 2);
- cleanDamage->damage = *damage;
- *hitInfo |= HITINFO_CRUSHING;
- // TODO: victimState, victim animation?
- break;
- }
- default:
- break;
- }
-
- // apply melee damage bonus and absorb only if base damage not fully blocked to prevent negative damage or damage with full block
- if(*victimState != VICTIMSTATE_BLOCKS)
- {
- MeleeDamageBonus(pVictim, damage,attType,spellCasted);
- CalcAbsorbResist(pVictim, damageSchoolMask, DIRECT_DAMAGE, *damage-*blocked_amount, absorbDamage, resistDamage);
- }
-
- if (*absorbDamage) *hitInfo |= HITINFO_ABSORB;
- if (*resistDamage) *hitInfo |= HITINFO_RESIST;
-
- cleanDamage->damage += *blocked_amount;
-
- if (*damage <= *absorbDamage + *resistDamage + *blocked_amount)
- {
- //*hitInfo = 0x00010020;
- //*hitInfo |= HITINFO_SWINGNOHITSOUND;
- //*damageType = 0;
- CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
- return;
- }
-
- // update at damage Judgement aura duration that applied by attacker at victim
- if(*damage)
- {
- AuraMap const& vAuras = pVictim->GetAuras();
- for(AuraMap::const_iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr)
- {
- SpellEntry const *spellInfo = (*itr).second->GetSpellProto();
- if( (spellInfo->AttributesEx3 & 0x40000) && spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN &&
- ((*itr).second->GetCasterGUID() == GetGUID() && (!spellCasted || spellCasted->Id == 35395)) )
- {
- (*itr).second->SetAuraDuration((*itr).second->GetAuraMaxDuration());
- (*itr).second->UpdateAuraDuration();
- }
- }
- }
-
- CastMeleeProcDamageAndSpell(pVictim, (*damage - *absorbDamage - *resistDamage - *blocked_amount), damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
-
- // victim's damage shield
- // yet another hack to fix crashes related to the aura getting removed during iteration
- std::set<Aura*> alreadyDone;
- uint32 removedAuras = pVictim->m_removedAuras;
- AuraList const& vDamageShields = pVictim->GetAurasByType(SPELL_AURA_DAMAGE_SHIELD);
- for(AuraList::const_iterator i = vDamageShields.begin(), next = vDamageShields.begin(); i != vDamageShields.end(); i = next)
- {
- ++next;
- if (alreadyDone.find(*i) == alreadyDone.end())
- {
- alreadyDone.insert(*i);
- pVictim->SpellNonMeleeDamageLog(this, (*i)->GetId(), (*i)->GetModifier()->m_amount, false, false);
- if (pVictim->m_removedAuras > removedAuras)
- {
- removedAuras = pVictim->m_removedAuras;
- next = vDamageShields.begin();
- }
- }
- }
-}
-
-void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool extra )
-{
- if(hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) )
- return;
-
- if (!pVictim->isAlive())
- return;
-
- if(IsNonMeleeSpellCasted(false))
- return;
-
- uint32 hitInfo;
- if (attType == BASE_ATTACK)
- hitInfo = HITINFO_NORMALSWING2;
- else if (attType == OFF_ATTACK)
- hitInfo = HITINFO_LEFTSWING;
- else
- return; // ignore ranaged case
-
- uint32 extraAttacks = m_extraAttacks;
-
- // melee attack spell casted at main hand attack only
- if (attType == BASE_ATTACK && m_currentSpells[CURRENT_MELEE_SPELL])
- {
- m_currentSpells[CURRENT_MELEE_SPELL]->cast();
-
- // not recent extra attack only at any non extra attack (melee spell case)
- if(!extra && extraAttacks)
- {
- while(m_extraAttacks)
- {
- AttackerStateUpdate(pVictim, BASE_ATTACK, true);
- if(m_extraAttacks > 0)
- --m_extraAttacks;
- }
- }
-
- return;
- }
-
- VictimState victimState = VICTIMSTATE_NORMAL;
-
- CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
- uint32 blocked_dmg = 0;
- uint32 absorbed_dmg = 0;
- uint32 resisted_dmg = 0;
-
- SpellSchoolMask meleeSchoolMask = GetMeleeDamageSchoolMask();
-
- if(pVictim->IsImmunedToDamage(meleeSchoolMask,true)) // use charges
- {
- SendAttackStateUpdate (HITINFO_NORMALSWING, pVictim, 1, meleeSchoolMask, 0, 0, 0, VICTIMSTATE_IS_IMMUNE, 0);
-
- // not recent extra attack only at any non extra attack (miss case)
- if(!extra && extraAttacks)
- {
- while(m_extraAttacks)
- {
- AttackerStateUpdate(pVictim, BASE_ATTACK, true);
- if(m_extraAttacks > 0)
- --m_extraAttacks;
- }
- }
-
- return;
- }
-
- uint32 damage = CalculateDamage (attType, false);
-
- DoAttackDamage (pVictim, &damage, &cleanDamage, &blocked_dmg, meleeSchoolMask, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, attType);
-
- if (hitInfo & HITINFO_MISS)
- //send miss
- SendAttackStateUpdate (hitInfo, pVictim, 1, meleeSchoolMask, damage, absorbed_dmg, resisted_dmg, victimState, blocked_dmg);
- else
- {
- //do animation
- SendAttackStateUpdate (hitInfo, pVictim, 1, meleeSchoolMask, damage, absorbed_dmg, resisted_dmg, victimState, blocked_dmg);
-
- if (damage > (absorbed_dmg + resisted_dmg + blocked_dmg))
- damage -= (absorbed_dmg + resisted_dmg + blocked_dmg);
- else
- damage = 0;
-
- DealDamage (pVictim, damage, &cleanDamage, DIRECT_DAMAGE, meleeSchoolMask, NULL, true);
-
- if(GetTypeId() == TYPEID_PLAYER && pVictim->isAlive())
- {
- for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
- ((Player*)this)->CastItemCombatSpell(((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0,i),pVictim,attType);
- }
- }
-
- if (GetTypeId() == TYPEID_PLAYER)
- DEBUG_LOG("AttackerStateUpdate: (Player) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
- GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, absorbed_dmg, blocked_dmg, resisted_dmg);
- else
- DEBUG_LOG("AttackerStateUpdate: (NPC) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
- GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, absorbed_dmg, blocked_dmg, resisted_dmg);
-
- // extra attack only at any non extra attack (normal case)
- if(!extra && extraAttacks)
- {
- while(m_extraAttacks)
- {
- AttackerStateUpdate(pVictim, BASE_ATTACK, true);
- if(m_extraAttacks > 0)
- --m_extraAttacks;
- }
- }
-}
-
-MeleeHitOutcome Unit::RollPhysicalOutcomeAgainst (Unit const *pVictim, WeaponAttackType attType, SpellEntry const *spellInfo)
-{
- // Miss chance based on melee
- float miss_chance = MeleeMissChanceCalc(pVictim, attType);
-
- // Critical hit chance
- float crit_chance = GetUnitCriticalChance(attType, pVictim);
- // this is to avoid compiler issue when declaring variables inside if
- float block_chance, parry_chance, dodge_chance;
-
- // cannot be dodged/parried/blocked
- if(spellInfo->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK)
- {
- block_chance = 0.0f;
- parry_chance = 0.0f;
- dodge_chance = 0.0f;
- }
- else
- {
- // parry can be avoided only by some abilites
- parry_chance = pVictim->GetUnitParryChance();
- // block might be bypassed by it as well
- block_chance = pVictim->GetUnitBlockChance();
- // stunned target cannot dodge and this is check in GetUnitDodgeChance()
- dodge_chance = pVictim->GetUnitDodgeChance();
- }
-
- // Only players can have Talent&Spell bonuses
- if (GetTypeId() == TYPEID_PLAYER)
- {
- // Increase from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL aura
- crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, spellInfo->SchoolMask);
-
- if( dodge_chance != 0.0f ) // if dodge chance is already 0, ignore talents fpr speed
- {
- AuraList const& mCanNotBeDodge = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT);
- for(AuraList::const_iterator i = mCanNotBeDodge.begin(); i != mCanNotBeDodge.end(); ++i)
- {
- // can't be dodged rogue finishing move
- if((*i)->GetModifier()->m_miscvalue == VICTIMSTATE_DODGE)
- {
- if(spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE))
- {
- dodge_chance = 0.0f;
- break;
- }
- }
- }
- }
- }
-
- // Spellmods
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance);
-
- DEBUG_LOG("PHYSICAL OUTCOME: miss %f crit %f dodge %f parry %f block %f",miss_chance,crit_chance,dodge_chance,parry_chance, block_chance);
-
- return RollMeleeOutcomeAgainst(pVictim, attType, int32(crit_chance*100), int32(miss_chance*100),int32(dodge_chance*100),int32(parry_chance*100),int32(block_chance*100), true);
-}
-
-MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit *pVictim, WeaponAttackType attType) const
-{
- // This is only wrapper
-
- // Miss chance based on melee
- float miss_chance = MeleeMissChanceCalc(pVictim, attType);
-
- // Critical hit chance
- float crit_chance = GetUnitCriticalChance(attType, pVictim);
-
- // stunned target cannot dodge and this is check in GetUnitDodgeChance() (returned 0 in this case)
- float dodge_chance = pVictim->GetUnitDodgeChance();
- float block_chance = pVictim->GetUnitBlockChance();
- float parry_chance = pVictim->GetUnitParryChance();
-
- // Useful if want to specify crit & miss chances for melee, else it could be removed
- DEBUG_LOG("MELEE OUTCOME: miss %f crit %f dodge %f parry %f block %f", miss_chance,crit_chance,dodge_chance,parry_chance,block_chance);
-
- return RollMeleeOutcomeAgainst(pVictim, attType, int32(crit_chance*100), int32(miss_chance*100), int32(dodge_chance*100),int32(parry_chance*100),int32(block_chance*100), false);
-}
-
-MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance, bool SpellCasted ) const
-{
- if(pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
- return MELEE_HIT_EVADE;
-
- int32 attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(pVictim);
- int32 victimMaxSkillValueForLevel = pVictim->GetMaxSkillValueForLevel(this);
-
- int32 attackerWeaponSkill = GetWeaponSkillValue(attType,pVictim);
- int32 victimDefenseSkill = pVictim->GetDefenseSkillValue(this);
-
- // bonus from skills is 0.04%
- int32 skillBonus = 4 * ( attackerWeaponSkill - victimMaxSkillValueForLevel );
- int32 skillBonus2 = 4 * ( attackerMaxSkillValueForLevel - victimDefenseSkill );
- int32 sum = 0, tmp = 0;
- int32 roll = urand (0, 10000);
-
- DEBUG_LOG ("RollMeleeOutcomeAgainst: skill bonus of %d for attacker", skillBonus);
- DEBUG_LOG ("RollMeleeOutcomeAgainst: rolled %d, miss %d, dodge %d, parry %d, block %d, crit %d",
- roll, miss_chance, dodge_chance, parry_chance, block_chance, crit_chance);
-
- tmp = miss_chance;
-
- if (tmp > 0 && roll < (sum += tmp ))
- {
- DEBUG_LOG ("RollMeleeOutcomeAgainst: MISS");
- return MELEE_HIT_MISS;
- }
-
- // always crit against a sitting target (except 0 crit chance)
- if( pVictim->GetTypeId() == TYPEID_PLAYER && crit_chance > 0 && !pVictim->IsStandState() )
- {
- DEBUG_LOG ("RollMeleeOutcomeAgainst: CRIT (sitting victim)");
- return MELEE_HIT_CRIT;
- }
-
- // Dodge chance
-
- // only players can't dodge if attacker is behind
- if (pVictim->GetTypeId() == TYPEID_PLAYER && !pVictim->HasInArc(M_PI,this))
- {
- DEBUG_LOG ("RollMeleeOutcomeAgainst: attack came from behind and victim was a player.");
- }
- else
- {
- // Reduce dodge chance by attacker expertise rating
- if (GetTypeId() == TYPEID_PLAYER)
- dodge_chance -= int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType)*100);
-
- // Modify dodge chance by attacker SPELL_AURA_MOD_COMBAT_RESULT_CHANCE
- dodge_chance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE);
-
- tmp = dodge_chance;
- if ( (tmp > 0) // check if unit _can_ dodge
- && ((tmp -= skillBonus) > 0)
- && roll < (sum += tmp))
- {
- DEBUG_LOG ("RollMeleeOutcomeAgainst: DODGE <%d, %d)", sum-tmp, sum);
- return MELEE_HIT_DODGE;
- }
- }
-
- // parry & block chances
-
- // check if attack comes from behind, nobody can parry or block if attacker is behind
- if (!pVictim->HasInArc(M_PI,this))
- {
- DEBUG_LOG ("RollMeleeOutcomeAgainst: attack came from behind.");
- }
- else
- {
- // Reduce parry chance by attacker expertise rating
- if (GetTypeId() == TYPEID_PLAYER)
- parry_chance-= int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType)*100);
-
- if(pVictim->GetTypeId()==TYPEID_PLAYER || !(((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY) )
- {
- int32 tmp = int32(parry_chance);
- if ( (tmp > 0) // check if unit _can_ parry
- && ((tmp -= skillBonus) > 0)
- && (roll < (sum += tmp)))
- {
- DEBUG_LOG ("RollMeleeOutcomeAgainst: PARRY <%d, %d)", sum-tmp, sum);
- return MELEE_HIT_PARRY;
- }
- }
-
- if(pVictim->GetTypeId()==TYPEID_PLAYER || !(((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK) )
- {
- tmp = block_chance;
- if ( (tmp > 0) // check if unit _can_ block
- && ((tmp -= skillBonus) > 0)
- && (roll < (sum += tmp)))
- {
- // Critical chance
- tmp = crit_chance + skillBonus2;
- if ( GetTypeId() == TYPEID_PLAYER && SpellCasted && tmp > 0 )
- {
- if ( roll_chance_i(tmp/100))
- {
- DEBUG_LOG ("RollMeleeOutcomeAgainst: BLOCKED CRIT");
- return MELEE_HIT_BLOCK_CRIT;
- }
- }
- DEBUG_LOG ("RollMeleeOutcomeAgainst: BLOCK <%d, %d)", sum-tmp, sum);
- return MELEE_HIT_BLOCK;
- }
- }
- }
-
- // Critical chance
- tmp = crit_chance + skillBonus2;
-
- if (tmp > 0 && roll < (sum += tmp))
- {
- DEBUG_LOG ("RollMeleeOutcomeAgainst: CRIT <%d, %d)", sum-tmp, sum);
- return MELEE_HIT_CRIT;
- }
-
- // Max 40% chance to score a glancing blow against mobs that are higher level (can do only players and pets and not with ranged weapon)
- if( attType != RANGED_ATTACK && !SpellCasted &&
- (GetTypeId() == TYPEID_PLAYER || ((Creature*)this)->isPet()) &&
- pVictim->GetTypeId() != TYPEID_PLAYER && !((Creature*)pVictim)->isPet() &&
- getLevel() < pVictim->getLevelForTarget(this) )
- {
- // cap possible value (with bonuses > max skill)
- int32 skill = attackerWeaponSkill;
- int32 maxskill = attackerMaxSkillValueForLevel;
- skill = (skill > maxskill) ? maxskill : skill;
-
- tmp = (10 + (victimDefenseSkill - skill)) * 100;
- tmp = tmp > 4000 ? 4000 : tmp;
- if (roll < (sum += tmp))
- {
- DEBUG_LOG ("RollMeleeOutcomeAgainst: GLANCING <%d, %d)", sum-4000, sum);
- return MELEE_HIT_GLANCING;
- }
- }
-
- if(GetTypeId()!=TYPEID_PLAYER && !(((Creature*)this)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRUSH) && !((Creature*)this)->isPet() )
- {
- // mobs can score crushing blows if they're 3 or more levels above victim
- // or when their weapon skill is 15 or more above victim's defense skill
- tmp = victimDefenseSkill;
- int32 tmpmax = victimMaxSkillValueForLevel;
- // having defense above your maximum (from items, talents etc.) has no effect
- tmp = tmp > tmpmax ? tmpmax : tmp;
- // tmp = mob's level * 5 - player's current defense skill
- tmp = attackerMaxSkillValueForLevel - tmp;
- if(tmp >= 15)
- {
- // add 2% chance per lacking skill point, min. is 15%
- tmp = tmp * 200 - 1500;
- if (roll < (sum += tmp))
- {
- DEBUG_LOG ("RollMeleeOutcomeAgainst: CRUSHING <%d, %d)", sum-tmp, sum);
- return MELEE_HIT_CRUSHING;
- }
- }
- }
-
- DEBUG_LOG ("RollMeleeOutcomeAgainst: NORMAL");
- return MELEE_HIT_NORMAL;
-}
-
-uint32 Unit::CalculateDamage (WeaponAttackType attType, bool normalized)
-{
- float min_damage, max_damage;
-
- if (normalized && GetTypeId()==TYPEID_PLAYER)
- ((Player*)this)->CalculateMinMaxDamage(attType,normalized,min_damage, max_damage);
- else
- {
- switch (attType)
- {
- case RANGED_ATTACK:
- min_damage = GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE);
- max_damage = GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE);
- break;
- case BASE_ATTACK:
- min_damage = GetFloatValue(UNIT_FIELD_MINDAMAGE);
- max_damage = GetFloatValue(UNIT_FIELD_MAXDAMAGE);
- break;
- case OFF_ATTACK:
- min_damage = GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE);
- max_damage = GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE);
- break;
- // Just for good manner
- default:
- min_damage = 0.0f;
- max_damage = 0.0f;
- break;
- }
- }
-
- if (min_damage > max_damage)
- {
- std::swap(min_damage,max_damage);
- }
-
- if(max_damage == 0.0f)
- max_damage = 5.0f;
-
- return urand((uint32)min_damage, (uint32)max_damage);
-}
-
-float Unit::CalculateLevelPenalty(SpellEntry const* spellProto) const
-{
- if(spellProto->spellLevel <= 0)
- return 1.0f;
-
- float LvlPenalty = 0.0f;
-
- if(spellProto->spellLevel < 20)
- LvlPenalty = 20.0f - spellProto->spellLevel * 3.75f;
- float LvlFactor = (float(spellProto->spellLevel) + 6.0f) / float(getLevel());
- if(LvlFactor > 1.0f)
- LvlFactor = 1.0f;
-
- return (100.0f - LvlPenalty) * LvlFactor / 100.0f;
-}
-
-void Unit::SendAttackStart(Unit* pVictim)
-{
- WorldPacket data( SMSG_ATTACKSTART, 16 );
- data << GetGUID();
- data << pVictim->GetGUID();
-
- SendMessageToSet(&data, true);
- DEBUG_LOG( "WORLD: Sent SMSG_ATTACKSTART" );
-}
-
-void Unit::SendAttackStop(Unit* victim)
-{
- if(!victim)
- return;
-
- WorldPacket data( SMSG_ATTACKSTOP, (4+16) ); // we guess size
- data.append(GetPackGUID());
- data.append(victim->GetPackGUID()); // can be 0x00...
- data << uint32(0); // can be 0x1
- SendMessageToSet(&data, true);
- sLog.outDetail("%s %u stopped attacking %s %u", (GetTypeId()==TYPEID_PLAYER ? "player" : "creature"), GetGUIDLow(), (victim->GetTypeId()==TYPEID_PLAYER ? "player" : "creature"),victim->GetGUIDLow());
-
- /*if(victim->GetTypeId() == TYPEID_UNIT)
- ((Creature*)victim)->AI().EnterEvadeMode(this);*/
-}
-
-/*
-// Melee based spells can be miss, parry or dodge on this step
-// Crit or block - determined on damage calculation phase! (and can be both in some time)
-float Unit::MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell)
-{
- // Calculate hit chance (more correct for chance mod)
- int32 HitChance;
-
- // PvP - PvE melee chances
- int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 5 : 7;
- int32 leveldif = pVictim->getLevelForTarget(this) - getLevelForTarget(pVictim);
- if(leveldif < 3)
- HitChance = 95 - leveldif;
- else
- HitChance = 93 - (leveldif - 2) * lchance;
-
- // Hit chance depends from victim auras
- if(attType == RANGED_ATTACK)
- HitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE);
- else
- HitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE);
-
- // Spellmod from SPELLMOD_RESIST_MISS_CHANCE
- if(Player *modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, HitChance);
-
- // Miss = 100 - hit
- float miss_chance= 100.0f - HitChance;
-
- // Bonuses from attacker aura and ratings
- if (attType == RANGED_ATTACK)
- miss_chance -= m_modRangedHitChance;
- else
- miss_chance -= m_modMeleeHitChance;
-
- // bonus from skills is 0.04%
- miss_chance -= skillDiff * 0.04f;
-
- // Limit miss chance from 0 to 60%
- if (miss_chance < 0.0f)
- return 0.0f;
- if (miss_chance > 60.0f)
- return 60.0f;
- return miss_chance;
-}
-
-// Melee based spells hit result calculations
-SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell)
-{
- WeaponAttackType attType = BASE_ATTACK;
-
- if (spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED)
- attType = RANGED_ATTACK;
-
- // bonus from skills is 0.04% per skill Diff
- int32 attackerWeaponSkill = int32(GetWeaponSkillValue(attType,pVictim));
- int32 skillDiff = attackerWeaponSkill - int32(pVictim->GetMaxSkillValueForLevel(this));
- int32 fullSkillDiff = attackerWeaponSkill - int32(pVictim->GetDefenseSkillValue(this));
-
- uint32 roll = urand (0, 10000);
- uint32 missChance = uint32(MeleeSpellMissChance(pVictim, attType, fullSkillDiff, spell)*100.0f);
-
- // Roll miss
- uint32 tmp = missChance;
- if (roll < tmp)
- return SPELL_MISS_MISS;
-
- // Same spells cannot be parry/dodge
- if (spell->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK)
- return SPELL_MISS_NONE;
-
- // Ranged attack can`t miss too
- if (attType == RANGED_ATTACK)
- return SPELL_MISS_NONE;
-
- bool attackFromBehind = !pVictim->HasInArc(M_PI,this);
-
- // Roll dodge
- int32 dodgeChance = int32(pVictim->GetUnitDodgeChance()*100.0f) - skillDiff * 4;
- // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE
- dodgeChance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE);
-
- // Reduce dodge chance by attacker expertise rating
- if (GetTypeId() == TYPEID_PLAYER)
- dodgeChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f);
- if (dodgeChance < 0)
- dodgeChance = 0;
-
- // Can`t dodge from behind in PvP (but its possible in PvE)
- if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER && attackFromBehind)
- dodgeChance = 0;
-
- // Rogue talent`s cant be dodged
- AuraList const& mCanNotBeDodge = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT);
- for(AuraList::const_iterator i = mCanNotBeDodge.begin(); i != mCanNotBeDodge.end(); ++i)
- {
- if((*i)->GetModifier()->m_miscvalue == VICTIMSTATE_DODGE) // can't be dodged rogue finishing move
- {
- if(spell->SpellFamilyName==SPELLFAMILY_ROGUE && (spell->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE))
- {
- dodgeChance = 0;
- break;
- }
- }
- }
-
- tmp += dodgeChance;
- if (roll < tmp)
- return SPELL_MISS_DODGE;
-
- // Roll parry
- int32 parryChance = int32(pVictim->GetUnitParryChance()*100.0f) - skillDiff * 4;
- // Reduce parry chance by attacker expertise rating
- if (GetTypeId() == TYPEID_PLAYER)
- parryChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f);
- // Can`t parry from behind
- if (parryChance < 0 || attackFromBehind)
- parryChance = 0;
-
- tmp += parryChance;
- if (roll < tmp)
- return SPELL_MISS_PARRY;
-
- return SPELL_MISS_NONE;
-}*/
-
-// TODO need use unit spell resistances in calculations
-SpellMissInfo Unit::MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell)
-{
- // Can`t miss on dead target (on skinning for example)
- if (!pVictim->isAlive())
- return SPELL_MISS_NONE;
-
- SpellSchoolMask schoolMask = GetSpellSchoolMask(spell);
- // PvP - PvE spell misschances per leveldif > 2
- int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 7 : 11;
- int32 leveldif = int32(pVictim->getLevelForTarget(this)) - int32(getLevelForTarget(pVictim));
-
- // Base hit chance from attacker and victim levels
- int32 modHitChance;
- if(leveldif < 3)
- modHitChance = 96 - leveldif;
- else
- modHitChance = 94 - (leveldif - 2) * lchance;
-
- // Spellmod from SPELLMOD_RESIST_MISS_CHANCE
- if(Player *modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance);
- // Increase from attacker SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT auras
- modHitChance+=GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT, schoolMask);
- // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras
- modHitChance+= pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask);
- // Reduce spell hit chance for Area of effect spells from victim SPELL_AURA_MOD_AOE_AVOIDANCE aura
- if (IsAreaOfEffectSpell(spell))
- modHitChance-=pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE);
- // Reduce spell hit chance for dispel mechanic spells from victim SPELL_AURA_MOD_DISPEL_RESIST
- if (IsDispelSpell(spell))
- modHitChance-=pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_DISPEL_RESIST);
- // Chance resist mechanic (select max value from every mechanic spell effect)
- int32 resist_mech = 0;
- // Get effects mechanic and chance
- for(int eff = 0; eff < 3; ++eff)
- {
- int32 effect_mech = GetEffectMechanic(spell, eff);
- if (effect_mech)
- {
- int32 temp = pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, effect_mech);
- if (resist_mech < temp)
- resist_mech = temp;
- }
- }
- // Apply mod
- modHitChance-=resist_mech;
-
- // Chance resist debuff
- modHitChance-=pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(spell->Dispel));
-
- int32 HitChance = modHitChance * 100;
- // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings
- HitChance += int32(m_modSpellHitChance*100.0f);
-
- // Decrease hit chance from victim rating bonus
- if (pVictim->GetTypeId()==TYPEID_PLAYER)
- HitChance -= int32(((Player*)pVictim)->GetRatingBonusValue(CR_HIT_TAKEN_SPELL)*100.0f);
-
- if (HitChance < 100) HitChance = 100;
- if (HitChance > 9900) HitChance = 9900;
-
- uint32 rand = urand(0,10000);
- if (rand > HitChance)
- return SPELL_MISS_RESIST;
- return SPELL_MISS_NONE;
-}
-
-SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool CanReflect)
-{
- // Return evade for units in evade mode
- if (pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
- return SPELL_MISS_EVADE;
-
- // Check for immune (use charges)
- if (pVictim->IsImmunedToSpell(spell,true))
- return SPELL_MISS_IMMUNE;
-
- // All positive spells can`t miss
- // TODO: client not show miss log for this spells - so need find info for this in dbc and use it!
- if (IsPositiveSpell(spell->Id))
- return SPELL_MISS_NONE;
-
- // Check for immune (use charges)
- if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell),true))
- return SPELL_MISS_IMMUNE;
-
- // Try victim reflect spell
- if (CanReflect)
- {
- // specialized first
- Unit::AuraList const& mReflectSpellsSchool = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL);
- for(Unit::AuraList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i)
- {
- if((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spell))
- {
- int32 reflectchance = (*i)->GetModifier()->m_amount;
- if (reflectchance > 0 && roll_chance_i(reflectchance))
- {
- if((*i)->m_procCharges > 0)
- {
- --(*i)->m_procCharges;
- if((*i)->m_procCharges==0)
- pVictim->RemoveAurasDueToSpell((*i)->GetId());
- }
- return SPELL_MISS_REFLECT;
- }
- }
- }
-
- // generic reflection
- Unit::AuraList const& mReflectSpells = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS);
- for(Unit::AuraList::const_iterator i = mReflectSpells.begin(); i != mReflectSpells.end(); ++i)
- {
- int32 reflectchance = (*i)->GetModifier()->m_amount;
- if (reflectchance > 0 && roll_chance_i(reflectchance))
- {
- if((*i)->m_procCharges > 0)
- {
- --(*i)->m_procCharges;
- if((*i)->m_procCharges==0)
- pVictim->RemoveAurasDueToSpell((*i)->GetId());
- }
- return SPELL_MISS_REFLECT;
- }
- }
- }
-
- // Temporary solution for melee based spells and spells vs SPELL_SCHOOL_NORMAL (hit result calculated after)
- for (int i=0;i<3;i++)
- {
- if (spell->Effect[i] == SPELL_EFFECT_WEAPON_DAMAGE ||
- spell->Effect[i] == SPELL_EFFECT_WEAPON_PERCENT_DAMAGE ||
- spell->Effect[i] == SPELL_EFFECT_NORMALIZED_WEAPON_DMG ||
- spell->Effect[i] == SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL)
- return SPELL_MISS_NONE;
- }
-
- // TODO need use this code for spell hit result calculation
- // now code commented for compotability
- switch (spell->DmgClass)
- {
- case SPELL_DAMAGE_CLASS_RANGED:
- case SPELL_DAMAGE_CLASS_MELEE:
-// return MeleeSpellHitResult(pVictim, spell);
- return SPELL_MISS_NONE;
- case SPELL_DAMAGE_CLASS_NONE:
- case SPELL_DAMAGE_CLASS_MAGIC:
- return MagicSpellHitResult(pVictim, spell);
- }
- return SPELL_MISS_NONE;
-}
-
-float Unit::MeleeMissChanceCalc(const Unit *pVictim, WeaponAttackType attType) const
-{
- if(!pVictim)
- return 0.0f;
-
- // Base misschance 5%
- float misschance = 5.0f;
-
- // DualWield - Melee spells and physical dmg spells - 5% , white damage 24%
- if (haveOffhandWeapon() && attType != RANGED_ATTACK)
- {
- bool isNormal = false;
- for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
- {
- if( m_currentSpells[i] && (GetSpellSchoolMask(m_currentSpells[i]->m_spellInfo) & SPELL_SCHOOL_MASK_NORMAL) )
- {
- isNormal = true;
- break;
- }
- }
- if (isNormal || m_currentSpells[CURRENT_MELEE_SPELL])
- {
- misschance = 5.0f;
- }
- else
- {
- misschance = 24.0f;
- }
- }
-
- // PvP : PvE melee misschances per leveldif > 2
- int32 chance = pVictim->GetTypeId() == TYPEID_PLAYER ? 5 : 7;
-
- int32 leveldif = int32(pVictim->getLevelForTarget(this)) - int32(getLevelForTarget(pVictim));
- if(leveldif < 0)
- leveldif = 0;
-
- // Hit chance from attacker based on ratings and auras
- float m_modHitChance;
- if (attType == RANGED_ATTACK)
- m_modHitChance = m_modRangedHitChance;
- else
- m_modHitChance = m_modMeleeHitChance;
-
- if(leveldif < 3)
- misschance += (leveldif - m_modHitChance);
- else
- misschance += ((leveldif - 2) * chance - m_modHitChance);
-
- // Hit chance for victim based on ratings
- if (pVictim->GetTypeId()==TYPEID_PLAYER)
- {
- if (attType == RANGED_ATTACK)
- misschance += ((Player*)pVictim)->GetRatingBonusValue(CR_HIT_TAKEN_RANGED);
- else
- misschance += ((Player*)pVictim)->GetRatingBonusValue(CR_HIT_TAKEN_MELEE);
- }
-
- // Modify miss chance by victim auras
- if(attType == RANGED_ATTACK)
- misschance -= pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE);
- else
- misschance -= pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE);
-
- // Modify miss chance from skill difference ( bonus from skills is 0.04% )
- int32 skillBonus = int32(GetWeaponSkillValue(attType,pVictim)) - int32(pVictim->GetDefenseSkillValue(this));
- misschance -= skillBonus * 0.04f;
-
- // Limit miss chance from 0 to 60%
- if ( misschance < 0.0f)
- return 0.0f;
- if ( misschance > 60.0f)
- return 60.0f;
-
- return misschance;
-}
-
-uint32 Unit::GetDefenseSkillValue(Unit const* target) const
-{
- if(GetTypeId() == TYPEID_PLAYER)
- {
- // in PvP use full skill instead current skill value
- uint32 value = (target && target->GetTypeId() == TYPEID_PLAYER)
- ? ((Player*)this)->GetMaxSkillValue(SKILL_DEFENSE)
- : ((Player*)this)->GetSkillValue(SKILL_DEFENSE);
- value += uint32(((Player*)this)->GetRatingBonusValue(CR_DEFENSE_SKILL));
- return value;
- }
- else
- return GetUnitMeleeSkill(target);
-}
-
-float Unit::GetUnitDodgeChance() const
-{
- if(hasUnitState(UNIT_STAT_STUNNED))
- return 0.0f;
- if( GetTypeId() == TYPEID_PLAYER )
- return GetFloatValue(PLAYER_DODGE_PERCENTAGE);
- else
- {
- if(((Creature const*)this)->isTotem())
- return 0.0f;
- else
- {
- float dodge = 5.0f;
- dodge += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
- return dodge > 0.0f ? dodge : 0.0f;
- }
- }
-}
-
-float Unit::GetUnitParryChance() const
-{
- if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNNED))
- return 0.0f;
-
- float chance = 0.0f;
-
- if(GetTypeId() == TYPEID_PLAYER)
- {
- Player const* player = (Player const*)this;
- if(player->CanParry() )
- {
- Item *tmpitem = player->GetWeaponForAttack(BASE_ATTACK,true);
- if(!tmpitem)
- tmpitem = player->GetWeaponForAttack(OFF_ATTACK,true);
-
- if(tmpitem)
- chance = GetFloatValue(PLAYER_PARRY_PERCENTAGE);
- }
- }
- else if(GetTypeId() == TYPEID_UNIT)
- {
- if(GetCreatureType() == CREATURE_TYPE_HUMANOID)
- {
- chance = 5.0f;
- chance += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
- }
- }
-
- return chance > 0.0f ? chance : 0.0f;
-}
-
-float Unit::GetUnitBlockChance() const
-{
- if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNNED))
- return 0.0f;
-
- if(GetTypeId() == TYPEID_PLAYER)
- {
- Player const* player = (Player const*)this;
- if(player->CanBlock() )
- {
- Item *tmpitem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
- if(tmpitem && !tmpitem->IsBroken() && tmpitem->GetProto()->Block)
- return GetFloatValue(PLAYER_BLOCK_PERCENTAGE);
- }
- // is player but has no block ability or no not broken shield equiped
- return 0.0f;
- }
- else
- {
- if(((Creature const*)this)->isTotem())
- return 0.0f;
- else
- {
- float block = 5.0f;
- block += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);
- return block > 0.0f ? block : 0.0f;
- }
- }
-}
-
-float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVictim) const
-{
- float crit;
-
- if(GetTypeId() == TYPEID_PLAYER)
- {
- switch(attackType)
- {
- case BASE_ATTACK:
- crit = GetFloatValue( PLAYER_CRIT_PERCENTAGE );
- break;
- case OFF_ATTACK:
- crit = GetFloatValue( PLAYER_OFFHAND_CRIT_PERCENTAGE );
- break;
- case RANGED_ATTACK:
- crit = GetFloatValue( PLAYER_RANGED_CRIT_PERCENTAGE );
- break;
- // Just for good manner
- default:
- crit = 0.0f;
- break;
- }
- }
- else
- {
- crit = 5.0f;
- crit += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PERCENT);
- }
-
- // flat aura mods
- if(attackType == RANGED_ATTACK)
- crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE);
- else
- crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE);
-
- crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
-
- // reduce crit chance from Rating for players
- if (pVictim->GetTypeId()==TYPEID_PLAYER)
- {
- if (attackType==RANGED_ATTACK)
- crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_RANGED);
- else
- crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE);
- }
-
- if (crit < 0.0f)
- crit = 0.0f;
- return crit;
-}
-
-uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) const
-{
- uint32 value = 0;
- if(GetTypeId() == TYPEID_PLAYER)
- {
- Item* item = ((Player*)this)->GetWeaponForAttack(attType,true);
-
- // feral or unarmed skill only for base attack
- if(attType != BASE_ATTACK && !item )
- return 0;
-
- if(((Player*)this)->IsInFeralForm())
- return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact
-
- // weaon skill or (unarmed for base attack)
- uint32 skill = item ? item->GetSkill() : SKILL_UNARMED;
-
- // in PvP use full skill instead current skill value
- value = (target && target->GetTypeId() == TYPEID_PLAYER)
- ? ((Player*)this)->GetMaxSkillValue(skill)
- : ((Player*)this)->GetSkillValue(skill);
- // Modify value from ratings
- value += uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL));
- switch (attType)
- {
- case BASE_ATTACK: value+=uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL_MAINHAND));break;
- case OFF_ATTACK: value+=uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL_OFFHAND));break;
- case RANGED_ATTACK: value+=uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL_RANGED));break;
- }
- }
- else
- value = GetUnitMeleeSkill(target);
- return value;
-}
-
-void Unit::_UpdateSpells( uint32 time )
-{
- if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
- _UpdateAutoRepeatSpell();
-
- // remove finished spells from current pointers
- for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
- {
- if (m_currentSpells[i] && m_currentSpells[i]->getState() == SPELL_STATE_FINISHED)
- {
- m_currentSpells[i]->SetDeletable(true); // spell may be safely deleted now
- m_currentSpells[i] = NULL; // remove pointer
- }
- }
-
- // TODO: Find a better way to prevent crash when multiple auras are removed.
- m_removedAuras = 0;
- for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
- if ((*i).second)
- (*i).second->SetUpdated(false);
-
- for (AuraMap::iterator i = m_Auras.begin(), next; i != m_Auras.end(); i = next)
- {
- next = i;
- ++next;
- if ((*i).second)
- {
- // prevent double update
- if ((*i).second->IsUpdated())
- continue;
- (*i).second->SetUpdated(true);
- (*i).second->Update( time );
- // several auras can be deleted due to update
- if (m_removedAuras)
- {
- if (m_Auras.empty()) break;
- next = m_Auras.begin();
- m_removedAuras = 0;
- }
- }
- }
-
- for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end();)
- {
- if ((*i).second)
- {
- if ( !(*i).second->GetAuraDuration() && !((*i).second->IsPermanent() || ((*i).second->IsPassive())) )
- {
- RemoveAura(i);
- }
- else
- {
- ++i;
- }
- }
- else
- {
- ++i;
- }
- }
-
- if(!m_gameObj.empty())
- {
- std::list<GameObject*>::iterator ite1, dnext1;
- for (ite1 = m_gameObj.begin(); ite1 != m_gameObj.end(); ite1 = dnext1)
- {
- dnext1 = ite1;
- //(*i)->Update( difftime );
- if( !(*ite1)->isSpawned() )
- {
- (*ite1)->SetOwnerGUID(0);
- (*ite1)->SetRespawnTime(0);
- (*ite1)->Delete();
- dnext1 = m_gameObj.erase(ite1);
- }
- else
- ++dnext1;
- }
- }
-}
-
-void Unit::_UpdateAutoRepeatSpell()
-{
- //check "realtime" interrupts
- if ( (GetTypeId() == TYPEID_PLAYER && ((Player*)this)->isMoving()) || IsNonMeleeSpellCasted(false,false,true) )
- {
- // cancel wand shoot
- if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
- InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
- m_AutoRepeatFirstCast = true;
- return;
- }
-
- //apply delay
- if ( m_AutoRepeatFirstCast && getAttackTimer(RANGED_ATTACK) < 500 )
- setAttackTimer(RANGED_ATTACK,500);
- m_AutoRepeatFirstCast = false;
-
- //castroutine
- if (isAttackReady(RANGED_ATTACK))
- {
- // Check if able to cast
- if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CanCast(true))
- {
- InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
- return;
- }
-
- // we want to shoot
- Spell* spell = new Spell(this, m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo, true, 0);
- spell->prepare(&(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets));
-
- // all went good, reset attack
- resetAttackTimer(RANGED_ATTACK);
- }
-}
-
-void Unit::SetCurrentCastedSpell( Spell * pSpell )
-{
- assert(pSpell); // NULL may be never passed here, use InterruptSpell or InterruptNonMeleeSpells
-
- uint32 CSpellType = pSpell->GetCurrentContainer();
-
- pSpell->SetDeletable(false); // spell will not be deleted until gone from current pointers
- if (pSpell == m_currentSpells[CSpellType]) return; // avoid breaking self
-
- // break same type spell if it is not delayed
- InterruptSpell(CSpellType,false);
-
- // special breakage effects:
- switch (CSpellType)
- {
- case CURRENT_GENERIC_SPELL:
- {
- // generic spells always break channeled not delayed spells
- InterruptSpell(CURRENT_CHANNELED_SPELL,false);
-
- // autorepeat breaking
- if ( m_currentSpells[CURRENT_AUTOREPEAT_SPELL] )
- {
- // break autorepeat if not Auto Shot
- if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
- InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
- m_AutoRepeatFirstCast = true;
- }
- } break;
-
- case CURRENT_CHANNELED_SPELL:
- {
- // channel spells always break generic non-delayed and any channeled spells
- InterruptSpell(CURRENT_GENERIC_SPELL,false);
- InterruptSpell(CURRENT_CHANNELED_SPELL);
-
- // it also does break autorepeat if not Auto Shot
- if ( m_currentSpells[CURRENT_AUTOREPEAT_SPELL] &&
- m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351 )
- InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
- } break;
-
- case CURRENT_AUTOREPEAT_SPELL:
- {
- // only Auto Shoot does not break anything
- if (pSpell->m_spellInfo->Category == 351)
- {
- // generic autorepeats break generic non-delayed and channeled non-delayed spells
- InterruptSpell(CURRENT_GENERIC_SPELL,false);
- InterruptSpell(CURRENT_CHANNELED_SPELL,false);
- }
- // special action: set first cast flag
- m_AutoRepeatFirstCast = true;
- } break;
-
- default:
- {
- // other spell types don't break anything now
- } break;
- }
-
- // current spell (if it is still here) may be safely deleted now
- if (m_currentSpells[CSpellType])
- m_currentSpells[CSpellType]->SetDeletable(true);
-
- // set new current spell
- m_currentSpells[CSpellType] = pSpell;
-}
-
-void Unit::InterruptSpell(uint32 spellType, bool withDelayed)
-{
- assert(spellType < CURRENT_MAX_SPELL);
-
- if(m_currentSpells[spellType] && (withDelayed || m_currentSpells[spellType]->getState() != SPELL_STATE_DELAYED) )
- {
- // send autorepeat cancel message for autorepeat spells
- if (spellType == CURRENT_AUTOREPEAT_SPELL)
- {
- if(GetTypeId()==TYPEID_PLAYER)
- ((Player*)this)->SendAutoRepeatCancel();
- }
-
- if (m_currentSpells[spellType]->getState() != SPELL_STATE_FINISHED)
- m_currentSpells[spellType]->cancel();
- m_currentSpells[spellType]->SetDeletable(true);
- m_currentSpells[spellType] = NULL;
- }
-}
-
-bool Unit::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skipAutorepeat) const
-{
- // We don't do loop here to explicitly show that melee spell is excluded.
- // Maybe later some special spells will be excluded too.
-
- // generic spells are casted when they are not finished and not delayed
- if ( m_currentSpells[CURRENT_GENERIC_SPELL] &&
- (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) &&
- (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED) )
- return(true);
-
- // channeled spells may be delayed, but they are still considered casted
- else if ( !skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] &&
- (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED) )
- return(true);
-
- // autorepeat spells may be finished or delayed, but they are still considered casted
- else if ( !skipAutorepeat && m_currentSpells[CURRENT_AUTOREPEAT_SPELL] )
- return(true);
-
- return(false);
-}
-
-void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id)
-{
- // generic spells are interrupted if they are not finished or delayed
- if (m_currentSpells[CURRENT_GENERIC_SPELL] && (!spell_id || m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Id==spell_id))
- {
- if ( (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) &&
- (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED) )
- m_currentSpells[CURRENT_GENERIC_SPELL]->cancel();
- m_currentSpells[CURRENT_GENERIC_SPELL]->SetDeletable(true);
- m_currentSpells[CURRENT_GENERIC_SPELL] = NULL;
- }
-
- // autorepeat spells are interrupted if they are not finished or delayed
- if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && (!spell_id || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id==spell_id))
- {
- // send disable autorepeat packet in any case
- if(GetTypeId()==TYPEID_PLAYER)
- ((Player*)this)->SendAutoRepeatCancel();
-
- if ( (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() != SPELL_STATE_FINISHED) &&
- (withDelayed || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() != SPELL_STATE_DELAYED) )
- m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->cancel();
- m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SetDeletable(true);
- m_currentSpells[CURRENT_AUTOREPEAT_SPELL] = NULL;
- }
-
- // channeled spells are interrupted if they are not finished, even if they are delayed
- if (m_currentSpells[CURRENT_CHANNELED_SPELL] && (!spell_id || m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id==spell_id))
- {
- if (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED)
- m_currentSpells[CURRENT_CHANNELED_SPELL]->cancel();
- m_currentSpells[CURRENT_CHANNELED_SPELL]->SetDeletable(true);
- m_currentSpells[CURRENT_CHANNELED_SPELL] = NULL;
- }
-}
-
-Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const
-{
- for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
- if(m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id==spell_id)
- return m_currentSpells[i];
- return NULL;
-}
-
-bool Unit::isInFront(Unit const* target, float distance, float arc) const
-{
- return IsWithinDistInMap(target, distance) && HasInArc( arc, target );
-}
-
-void Unit::SetInFront(Unit const* target)
-{
- SetOrientation(GetAngle(target));
-}
-
-bool Unit::isInBack(Unit const* target, float distance, float arc) const
-{
- return IsWithinDistInMap(target, distance) && !HasInArc( 2 * M_PI - arc, target );
-}
-
-bool Unit::isInAccessablePlaceFor(Creature const* c) const
-{
- if(IsInWater())
- return c->canSwim();
- else
- return c->canWalk() || c->canFly();
-}
-
-bool Unit::IsInWater() const
-{
- return MapManager::Instance().GetBaseMap(GetMapId())->IsInWater(GetPositionX(),GetPositionY(), GetPositionZ());
-}
-
-bool Unit::IsUnderWater() const
-{
- return MapManager::Instance().GetBaseMap(GetMapId())->IsUnderWater(GetPositionX(),GetPositionY(),GetPositionZ());
-}
-
-void Unit::DeMorph()
-{
- SetDisplayId(GetNativeDisplayId());
-}
-
-int32 Unit::GetTotalAuraModifier(AuraType auratype) const
-{
- int32 modifier = 0;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- modifier += (*i)->GetModifier()->m_amount;
-
- return modifier;
-}
-
-float Unit::GetTotalAuraMultiplier(AuraType auratype) const
-{
- float multipler = 1.0f;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- multipler *= (100.0f + (*i)->GetModifier()->m_amount)/100.0f;
-
- return multipler;
-}
-
-int32 Unit::GetMaxPositiveAuraModifier(AuraType auratype) const
-{
- int32 modifier = 0;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- if ((*i)->GetModifier()->m_amount > modifier)
- modifier = (*i)->GetModifier()->m_amount;
-
- return modifier;
-}
-
-int32 Unit::GetMaxNegativeAuraModifier(AuraType auratype) const
-{
- int32 modifier = 0;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- if ((*i)->GetModifier()->m_amount < modifier)
- modifier = (*i)->GetModifier()->m_amount;
-
- return modifier;
-}
-
-int32 Unit::GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
-{
- int32 modifier = 0;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- {
- Modifier* mod = (*i)->GetModifier();
- if (mod->m_miscvalue & misc_mask)
- modifier += mod->m_amount;
- }
- return modifier;
-}
-
-float Unit::GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask) const
-{
- float multipler = 1.0f;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- {
- Modifier* mod = (*i)->GetModifier();
- if (mod->m_miscvalue & misc_mask)
- multipler *= (100.0f + mod->m_amount)/100.0f;
- }
- return multipler;
-}
-
-int32 Unit::GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
-{
- int32 modifier = 0;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- {
- Modifier* mod = (*i)->GetModifier();
- if (mod->m_miscvalue & misc_mask && mod->m_amount > modifier)
- modifier = mod->m_amount;
- }
-
- return modifier;
-}
-
-int32 Unit::GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
-{
- int32 modifier = 0;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- {
- Modifier* mod = (*i)->GetModifier();
- if (mod->m_miscvalue & misc_mask && mod->m_amount < modifier)
- modifier = mod->m_amount;
- }
-
- return modifier;
-}
-
-int32 Unit::GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
-{
- int32 modifier = 0;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- {
- Modifier* mod = (*i)->GetModifier();
- if (mod->m_miscvalue == misc_value)
- modifier += mod->m_amount;
- }
- return modifier;
-}
-
-float Unit::GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 misc_value) const
-{
- float multipler = 1.0f;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- {
- Modifier* mod = (*i)->GetModifier();
- if (mod->m_miscvalue == misc_value)
- multipler *= (100.0f + mod->m_amount)/100.0f;
- }
- return multipler;
-}
-
-int32 Unit::GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
-{
- int32 modifier = 0;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- {
- Modifier* mod = (*i)->GetModifier();
- if (mod->m_miscvalue == misc_value && mod->m_amount > modifier)
- modifier = mod->m_amount;
- }
-
- return modifier;
-}
-
-int32 Unit::GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
-{
- int32 modifier = 0;
-
- AuraList const& mTotalAuraList = GetAurasByType(auratype);
- for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
- {
- Modifier* mod = (*i)->GetModifier();
- if (mod->m_miscvalue == misc_value && mod->m_amount < modifier)
- modifier = mod->m_amount;
- }
-
- return modifier;
-}
-
-bool Unit::AddAura(Aura *Aur)
-{
- // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load)
- if( !isAlive() && Aur->GetId() != 20584 && Aur->GetId() != 8326 && Aur->GetId() != 2584 &&
- (GetTypeId()!=TYPEID_PLAYER || !((Player*)this)->GetSession()->PlayerLoading()) )
- {
- delete Aur;
- return false;
- }
-
- if(Aur->GetTarget() != this)
- {
- sLog.outError("Aura (spell %u eff %u) add to aura list of %s (lowguid: %u) but Aura target is %s (lowguid: %u)",
- Aur->GetId(),Aur->GetEffIndex(),(GetTypeId()==TYPEID_PLAYER?"player":"creature"),GetGUIDLow(),
- (Aur->GetTarget()->GetTypeId()==TYPEID_PLAYER?"player":"creature"),Aur->GetTarget()->GetGUIDLow());
- delete Aur;
- return false;
- }
-
- SpellEntry const* aurSpellInfo = Aur->GetSpellProto();
-
- spellEffectPair spair = spellEffectPair(Aur->GetId(), Aur->GetEffIndex());
- AuraMap::iterator i = m_Auras.find( spair );
-
- // take out same spell
- if (i != m_Auras.end())
- {
- // passive and persistent auras can stack with themselves any number of times
- if (!Aur->IsPassive() && !Aur->IsPersistent())
- {
- // replace aura if next will > spell StackAmount
- if(aurSpellInfo->StackAmount)
- {
- if(m_Auras.count(spair) >= aurSpellInfo->StackAmount)
- RemoveAura(i,AURA_REMOVE_BY_STACK);
- }
- // if StackAmount==0 not allow auras from same caster
- else
- {
- for(AuraMap::iterator i2 = m_Auras.lower_bound(spair); i2 != m_Auras.upper_bound(spair); ++i2)
- {
- if(i2->second->GetCasterGUID()==Aur->GetCasterGUID())
- {
- // can be only single (this check done at _each_ aura add
- RemoveAura(i2,AURA_REMOVE_BY_STACK);
- break;
- }
-
- bool stop = false;
- switch(aurSpellInfo->EffectApplyAuraName[Aur->GetEffIndex()])
- {
- // DoT/HoT/etc
- case SPELL_AURA_PERIODIC_DAMAGE: // allow stack
- case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
- case SPELL_AURA_PERIODIC_LEECH:
- case SPELL_AURA_PERIODIC_HEAL:
- case SPELL_AURA_OBS_MOD_HEALTH:
- case SPELL_AURA_PERIODIC_MANA_LEECH:
- case SPELL_AURA_PERIODIC_ENERGIZE:
- case SPELL_AURA_OBS_MOD_MANA:
- case SPELL_AURA_POWER_BURN_MANA:
- break;
- default: // not allow
- // can be only single (this check done at _each_ aura add
- RemoveAura(i2,AURA_REMOVE_BY_STACK);
- stop = true;
- break;
- }
-
- if(stop)
- break;
- }
- }
- }
- }
-
- // passive auras stack with all (except passive spell proc auras)
- if ((!Aur->IsPassive() || !IsPassiveStackableSpell(Aur->GetId())) &&
- !(Aur->GetId() == 20584 || Aur->GetId() == 8326))
- {
- if (!RemoveNoStackAurasDueToAura(Aur))
- {
- delete Aur;
- return false; // couldnt remove conflicting aura with higher rank
- }
- }
-
- // update single target auras list (before aura add to aura list, to prevent unexpected remove recently added aura)
- if (IsSingleTargetSpell(aurSpellInfo) && Aur->GetTarget())
- {
- // caster pointer can be deleted in time aura remove, find it by guid at each iteration
- for(;;)
- {
- Unit* caster = Aur->GetCaster();
- if(!caster) // caster deleted and not required adding scAura
- break;
-
- bool restart = false;
- AuraList& scAuras = caster->GetSingleCastAuras();
- for(AuraList::iterator itr = scAuras.begin(); itr != scAuras.end(); ++itr)
- {
- if( (*itr)->GetTarget() != Aur->GetTarget() &&
- IsSingleTargetSpells((*itr)->GetSpellProto(),aurSpellInfo) )
- {
- if ((*itr)->IsInUse())
- {
- sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for IsSingleTargetSpell", (*itr)->GetId(), (*itr)->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
- continue;
- }
- (*itr)->GetTarget()->RemoveAura((*itr)->GetId(), (*itr)->GetEffIndex());
- restart = true;
- break;
- }
- }
-
- if(!restart)
- {
- // done
- scAuras.push_back(Aur);
- break;
- }
- }
- }
-
- // add aura, register in lists and arrays
- Aur->_AddAura();
- m_Auras.insert(AuraMap::value_type(spellEffectPair(Aur->GetId(), Aur->GetEffIndex()), Aur));
- if (Aur->GetModifier()->m_auraname < TOTAL_AURAS)
- {
- m_modAuras[Aur->GetModifier()->m_auraname].push_back(Aur);
- }
-
- Aur->ApplyModifier(true,true);
- sLog.outDebug("Aura %u now is in use", Aur->GetModifier()->m_auraname);
- return true;
-}
-
-void Unit::RemoveRankAurasDueToSpell(uint32 spellId)
-{
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
- if(!spellInfo)
- return;
- AuraMap::iterator i,next;
- for (i = m_Auras.begin(); i != m_Auras.end(); i = next)
- {
- next = i;
- ++next;
- uint32 i_spellId = (*i).second->GetId();
- if((*i).second && i_spellId && i_spellId != spellId)
- {
- if(spellmgr.IsRankSpellDueToSpell(spellInfo,i_spellId))
- {
- RemoveAurasDueToSpell(i_spellId);
-
- if( m_Auras.empty() )
- break;
- else
- next = m_Auras.begin();
- }
- }
- }
-}
-
-bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur)
-{
- if (!Aur)
- return false;
-
- SpellEntry const* spellProto = Aur->GetSpellProto();
- if (!spellProto)
- return false;
-
- uint32 spellId = Aur->GetId();
- uint32 effIndex = Aur->GetEffIndex();
-
- SpellSpecific spellId_spec = GetSpellSpecific(spellId);
-
- AuraMap::iterator i,next;
- for (i = m_Auras.begin(); i != m_Auras.end(); i = next)
- {
- next = i;
- ++next;
- if (!(*i).second) continue;
-
- SpellEntry const* i_spellProto = (*i).second->GetSpellProto();
-
- if (!i_spellProto)
- continue;
-
- uint32 i_spellId = i_spellProto->Id;
-
- if(IsPassiveSpell(i_spellId))
- {
- if(IsPassiveStackableSpell(i_spellId))
- continue;
-
- // passive non-stackable spells not stackable only with another rank of same spell
- if (!spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId))
- continue;
- }
-
- uint32 i_effIndex = (*i).second->GetEffIndex();
-
- if(i_spellId == spellId) continue;
-
- bool is_triggered_by_spell = false;
- // prevent triggered aura of removing aura that triggered it
- for(int j = 0; j < 3; ++j)
- if (i_spellProto->EffectTriggerSpell[j] == spellProto->Id)
- is_triggered_by_spell = true;
- if (is_triggered_by_spell) continue;
-
- for(int j = 0; j < 3; ++j)
- {
- // prevent remove dummy triggered spells at next effect aura add
- switch(spellProto->Effect[j]) // main spell auras added added after triggred spell
- {
- case SPELL_EFFECT_DUMMY:
- switch(spellId)
- {
- case 5420: if(i_spellId==34123) is_triggered_by_spell = true; break;
- }
- break;
- }
-
- if(is_triggered_by_spell)
- break;
-
- // prevent remove form main spell by triggred passive spells
- switch(i_spellProto->EffectApplyAuraName[j]) // main aura added before triggered spell
- {
- case SPELL_AURA_MOD_SHAPESHIFT:
- switch(i_spellId)
- {
- case 24858: if(spellId==24905) is_triggered_by_spell = true; break;
- case 33891: if(spellId==5420 || spellId==34123) is_triggered_by_spell = true; break;
- case 34551: if(spellId==22688) is_triggered_by_spell = true; break;
- }
- break;
- }
- }
-
- if(!is_triggered_by_spell)
- {
- SpellSpecific i_spellId_spec = GetSpellSpecific(i_spellId);
-
- bool is_sspc = IsSingleFromSpellSpecificPerCaster(spellId_spec,i_spellId_spec);
-
- if( is_sspc && Aur->GetCasterGUID() == (*i).second->GetCasterGUID() )
- {
- // cannot remove higher rank
- if (spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId))
- if(CompareAuraRanks(spellId, effIndex, i_spellId, i_effIndex) < 0)
- return false;
-
- // Its a parent aura (create this aura in ApplyModifier)
- if ((*i).second->IsInUse())
- {
- sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
- continue;
- }
- RemoveAurasDueToSpell(i_spellId);
-
- if( m_Auras.empty() )
- break;
- else
- next = m_Auras.begin();
- }
- else if( !is_sspc && spellmgr.IsNoStackSpellDueToSpell(spellId, i_spellId) )
- {
- // Its a parent aura (create this aura in ApplyModifier)
- if ((*i).second->IsInUse())
- {
- sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
- continue;
- }
- RemoveAurasDueToSpell(i_spellId);
-
- if( m_Auras.empty() )
- break;
- else
- next = m_Auras.begin();
- }
- // Potions stack aura by aura (elixirs/flask already checked)
- else if( spellProto->SpellFamilyName == SPELLFAMILY_POTION && i_spellProto->SpellFamilyName == SPELLFAMILY_POTION )
- {
- if (IsNoStackAuraDueToAura(spellId, effIndex, i_spellId, i_effIndex))
- {
- if(CompareAuraRanks(spellId, effIndex, i_spellId, i_effIndex) < 0)
- return false; // cannot remove higher rank
-
- // Its a parent aura (create this aura in ApplyModifier)
- if ((*i).second->IsInUse())
- {
- sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
- continue;
- }
- RemoveAura(i);
- next = i;
- }
- }
- }
- }
- return true;
-}
-
-void Unit::RemoveAura(uint32 spellId, uint32 effindex, Aura* except)
-{
- spellEffectPair spair = spellEffectPair(spellId, effindex);
- for(AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair);)
- {
- if(iter->second!=except)
- {
- RemoveAura(iter);
- iter = m_Auras.lower_bound(spair);
- }
- else
- ++iter;
- }
-}
-
-void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit *dispeler)
-{
- for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
- {
- Aura *aur = iter->second;
- if (aur->GetId() == spellId && aur->GetCasterGUID() == casterGUID)
- {
- // Custom dispel case
- // Unstable Affliction
- if (aur->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (aur->GetSpellProto()->SpellFamilyFlags & 0x010000000000LL))
- {
- int32 damage = aur->GetModifier()->m_amount*9;
- uint64 caster_guid = aur->GetCasterGUID();
-
- // Remove aura
- RemoveAura(iter, AURA_REMOVE_BY_DISPEL);
-
- // backfire damage and silence
- dispeler->CastCustomSpell(dispeler, 31117, &damage, NULL, NULL, true, NULL, NULL,caster_guid);
-
- iter = m_Auras.begin(); // iterator can be invalidate at cast if self-dispel
- }
- else
- RemoveAura(iter, AURA_REMOVE_BY_DISPEL);
- }
- else
- ++iter;
- }
-}
-
-void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit *stealer)
-{
- for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
- {
- Aura *aur = iter->second;
- if (aur->GetId() == spellId && aur->GetCasterGUID() == casterGUID)
- {
- int32 basePoints = aur->GetBasePoints();
- // construct the new aura for the attacker
- Aura * new_aur = CreateAura(aur->GetSpellProto(), aur->GetEffIndex(), &basePoints, stealer);
- if(!new_aur)
- continue;
-
- // set its duration and maximum duration
- // max duration 2 minutes (in msecs)
- int32 dur = aur->GetAuraDuration();
- const int32 max_dur = 2*MINUTE*1000;
- new_aur->SetAuraMaxDuration( max_dur > dur ? dur : max_dur );
- new_aur->SetAuraDuration( max_dur > dur ? dur : max_dur );
-
- // add the new aura to stealer
- stealer->AddAura(new_aur);
-
- // Remove aura as dispel
- RemoveAura(iter, AURA_REMOVE_BY_DISPEL);
- }
- else
- ++iter;
- }
-}
-
-void Unit::RemoveAurasDueToSpellByCancel(uint32 spellId)
-{
- for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
- {
- if (iter->second->GetId() == spellId)
- RemoveAura(iter, AURA_REMOVE_BY_CANCEL);
- else
- ++iter;
- }
-}
-
-void Unit::RemoveAurasWithDispelType( DispelType type )
-{
- // Create dispel mask by dispel type
- uint32 dispelMask = GetDispellMask(type);
- // Dispel all existing auras vs current dispell type
- AuraMap& auras = GetAuras();
- for(AuraMap::iterator itr = auras.begin(); itr != auras.end(); )
- {
- SpellEntry const* spell = itr->second->GetSpellProto();
- if( (1<<spell->Dispel) & dispelMask )
- {
- // Dispel aura
- RemoveAurasDueToSpell(spell->Id);
- itr = auras.begin();
- }
- else
- ++itr;
- }
-}
-
-void Unit::RemoveSingleAuraFromStack(uint32 spellId, uint32 effindex)
-{
- AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex));
- if(iter != m_Auras.end())
- RemoveAura(iter);
-}
-
-void Unit::RemoveAurasDueToSpell(uint32 spellId, Aura* except)
-{
- for (int i = 0; i < 3; ++i)
- RemoveAura(spellId,i,except);
-}
-
-void Unit::RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId)
-{
- for (int k=0; k < 3; ++k)
- {
- spellEffectPair spair = spellEffectPair(spellId, k);
- for (AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair);)
- {
- if (iter->second->GetCastItemGUID() == castItem->GetGUID())
- {
- RemoveAura(iter);
- iter = m_Auras.upper_bound(spair); // overwrite by more appropriate
- }
- else
- ++iter;
- }
- }
-}
-
-void Unit::RemoveAurasWithInterruptFlags(uint32 flags)
-{
- for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
- {
- if (iter->second->GetSpellProto()->AuraInterruptFlags & flags)
- RemoveAura(iter);
- else
- ++iter;
- }
-}
-
-void Unit::RemoveNotOwnSingleTargetAuras()
-{
- // single target auras from other casters
- for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
- {
- if (iter->second->GetCasterGUID()!=GetGUID() && IsSingleTargetSpell(iter->second->GetSpellProto()))
- RemoveAura(iter);
- else
- ++iter;
- }
-
- // single target auras at other targets
- AuraList& scAuras = GetSingleCastAuras();
- for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end(); )
- {
- Aura* aura = *iter;
- if (aura->GetTarget()!=this)
- {
- scAuras.erase(iter); // explicitly remove, instead waiting remove in RemoveAura
- aura->GetTarget()->RemoveAura(aura->GetId(),aura->GetEffIndex());
- iter = scAuras.begin();
- }
- else
- ++iter;
- }
-
-}
-
-void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode)
-{
- if (IsSingleTargetSpell((*i).second->GetSpellProto()))
- {
- if(Unit* caster = (*i).second->GetCaster())
- {
- AuraList& scAuras = caster->GetSingleCastAuras();
- scAuras.remove((*i).second);
- }
- else
- {
- sLog.outError("Couldn't find the caster of the single target aura, may crash later!");
- assert(false);
- }
- }
-
- if ((*i).second->GetModifier()->m_auraname < TOTAL_AURAS)
- {
- m_modAuras[(*i).second->GetModifier()->m_auraname].remove((*i).second);
- }
-
- // remove from list before mods removing (prevent cyclic calls, mods added before including to aura list - use reverse order)
- Aura* Aur = i->second;
- // Set remove mode
- Aur->SetRemoveMode(mode);
- // some ShapeshiftBoosts at remove trigger removing other auras including parent Shapeshift aura
- // remove aura from list before to prevent deleting it before
- m_Auras.erase(i);
- ++m_removedAuras; // internal count used by unit update
-
- // Status unsummoned at aura remove
- Totem* statue = NULL;
- if(IsChanneledSpell(Aur->GetSpellProto()))
- if(Unit* caster = Aur->GetCaster())
- if(caster->GetTypeId()==TYPEID_UNIT && ((Creature*)caster)->isTotem() && ((Totem*)caster)->GetTotemType()==TOTEM_STATUE)
- statue = ((Totem*)caster);
-
- sLog.outDebug("Aura %u now is remove mode %d",Aur->GetModifier()->m_auraname, mode);
- Aur->ApplyModifier(false,true);
- Aur->_RemoveAura();
- delete Aur;
-
- if(statue)
- statue->UnSummon();
-
- // only way correctly remove all auras from list
- if( m_Auras.empty() )
- i = m_Auras.end();
- else
- i = m_Auras.begin();
-}
-
-void Unit::RemoveAllAuras()
-{
- while (!m_Auras.empty())
- {
- AuraMap::iterator iter = m_Auras.begin();
- RemoveAura(iter);
- }
-}
-
-void Unit::RemoveArenaAuras(bool onleave)
-{
- // in join, remove positive buffs, on end, remove negative
- // used to remove positive visible auras in arenas
- for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
- {
- if ( !(iter->second->GetSpellProto()->AttributesEx4 & (1<<21)) // don't remove stances, shadowform, pally/hunter auras
- && !iter->second->IsPassive() // don't remove passive auras
- && (!(iter->second->GetSpellProto()->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) || !(iter->second->GetSpellProto()->Attributes & SPELL_ATTR_UNK8)) // not unaffected by invulnerability auras or not having that unknown flag (that seemed the most probable)
- && (iter->second->IsPositive() ^ onleave)) // remove positive buffs on enter, negative buffs on leave
- RemoveAura(iter);
- else
- ++iter;
- }
-}
-
-void Unit::RemoveAllAurasOnDeath()
-{
- // used just after dieing to remove all visible auras
- // and disable the mods for the passive ones
- for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
- {
- if (!iter->second->IsPassive() && !iter->second->IsDeathPersistent())
- RemoveAura(iter, AURA_REMOVE_BY_DEATH);
- else
- ++iter;
- }
-}
-
-void Unit::DelayAura(uint32 spellId, uint32 effindex, int32 delaytime)
-{
- AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex));
- if (iter != m_Auras.end())
- {
- if (iter->second->GetAuraDuration() < delaytime)
- iter->second->SetAuraDuration(0);
- else
- iter->second->SetAuraDuration(iter->second->GetAuraDuration() - delaytime);
- iter->second->UpdateAuraDuration();
- sLog.outDebug("Aura %u partially interrupted on unit %u, new duration: %u ms",iter->second->GetModifier()->m_auraname, GetGUIDLow(), iter->second->GetAuraDuration());
- }
-}
-
-void Unit::_RemoveAllAuraMods()
-{
- for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
- {
- (*i).second->ApplyModifier(false);
- }
-}
-
-void Unit::_ApplyAllAuraMods()
-{
- for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
- {
- (*i).second->ApplyModifier(true);
- }
-}
-
-Aura* Unit::GetAura(uint32 spellId, uint32 effindex)
-{
- AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex));
- if (iter != m_Auras.end())
- return iter->second;
- return NULL;
-}
-
-void Unit::AddDynObject(DynamicObject* dynObj)
-{
- m_dynObjGUIDs.push_back(dynObj->GetGUID());
-}
-
-void Unit::RemoveDynObject(uint32 spellid)
-{
- if(m_dynObjGUIDs.empty())
- return;
- for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();)
- {
- DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
- if(!dynObj)
- {
- i = m_dynObjGUIDs.erase(i);
- }
- else if(spellid == 0 || dynObj->GetSpellId() == spellid)
- {
- dynObj->Delete();
- i = m_dynObjGUIDs.erase(i);
- }
- else
- ++i;
- }
-}
-
-void Unit::RemoveAllDynObjects()
-{
- while(!m_dynObjGUIDs.empty())
- {
- DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
- if(dynObj)
- dynObj->Delete();
- m_dynObjGUIDs.erase(m_dynObjGUIDs.begin());
- }
-}
-
-DynamicObject * Unit::GetDynObject(uint32 spellId, uint32 effIndex)
-{
- for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();)
- {
- DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
- if(!dynObj)
- {
- i = m_dynObjGUIDs.erase(i);
- continue;
- }
-
- if (dynObj->GetSpellId() == spellId && dynObj->GetEffIndex() == effIndex)
- return dynObj;
- ++i;
- }
- return NULL;
-}
-
-DynamicObject * Unit::GetDynObject(uint32 spellId)
-{
- for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();)
- {
- DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
- if(!dynObj)
- {
- i = m_dynObjGUIDs.erase(i);
- continue;
- }
-
- if (dynObj->GetSpellId() == spellId)
- return dynObj;
- ++i;
- }
- return NULL;
-}
-
-void Unit::AddGameObject(GameObject* gameObj)
-{
- assert(gameObj && gameObj->GetOwnerGUID()==0);
- m_gameObj.push_back(gameObj);
- gameObj->SetOwnerGUID(GetGUID());
-}
-
-void Unit::RemoveGameObject(GameObject* gameObj, bool del)
-{
- assert(gameObj && gameObj->GetOwnerGUID()==GetGUID());
-
- // GO created by some spell
- if ( GetTypeId()==TYPEID_PLAYER && gameObj->GetSpellId() )
- {
- SpellEntry const* createBySpell = sSpellStore.LookupEntry(gameObj->GetSpellId());
- // Need activate spell use for owner
- if (createBySpell && createBySpell->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
- ((Player*)this)->SendCooldownEvent(createBySpell);
- }
- gameObj->SetOwnerGUID(0);
- m_gameObj.remove(gameObj);
- if(del)
- {
- gameObj->SetRespawnTime(0);
- gameObj->Delete();
- }
-}
-
-void Unit::RemoveGameObject(uint32 spellid, bool del)
-{
- if(m_gameObj.empty())
- return;
- std::list<GameObject*>::iterator i, next;
- for (i = m_gameObj.begin(); i != m_gameObj.end(); i = next)
- {
- next = i;
- if(spellid == 0 || (*i)->GetSpellId() == spellid)
- {
- (*i)->SetOwnerGUID(0);
- if(del)
- {
- (*i)->SetRespawnTime(0);
- (*i)->Delete();
- }
-
- next = m_gameObj.erase(i);
- }
- else
- ++next;
- }
-}
-
-void Unit::RemoveAllGameObjects()
-{
- // remove references to unit
- for(std::list<GameObject*>::iterator i = m_gameObj.begin(); i != m_gameObj.end();)
- {
- (*i)->SetOwnerGUID(0);
- (*i)->SetRespawnTime(0);
- (*i)->Delete();
- i = m_gameObj.erase(i);
- }
-}
-
-void Unit::SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchoolMask damageSchoolMask,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit)
-{
- sLog.outDebug("Sending: SMSG_SPELLNONMELEEDAMAGELOG");
- WorldPacket data(SMSG_SPELLNONMELEEDAMAGELOG, (16+4+4+1+4+4+1+1+4+4+1)); // we guess size
- data.append(target->GetPackGUID());
- data.append(GetPackGUID());
- data << uint32(SpellID);
- data << uint32(Damage-AbsorbedDamage-Resist-Blocked);
- data << uint8(damageSchoolMask); // spell school
- data << uint32(AbsorbedDamage); // AbsorbedDamage
- data << uint32(Resist); // resist
- data << uint8(PhysicalDamage); // if 1, then client show spell name (example: %s's ranged shot hit %s for %u school or %s suffers %u school damage from %s's spell_name
- data << uint8(0); // unk isFromAura
- data << uint32(Blocked); // blocked
- data << uint32(CriticalHit ? 0x27 : 0x25); // hitType, flags: 0x2 - SPELL_HIT_TYPE_CRIT, 0x10 - replace caster?
- data << uint8(0); // isDebug?
- SendMessageToSet( &data, true );
-}
-
-void Unit::SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo)
-{
- WorldPacket data(SMSG_SPELLLOGMISS, (4+8+1+4+8+1));
- data << uint32(spellID);
- data << uint64(GetGUID());
- data << uint8(0); // can be 0 or 1
- data << uint32(1); // target count
- // for(i = 0; i < target count; ++i)
- data << uint64(target->GetGUID()); // target GUID
- data << uint8(missInfo);
- // end loop
- SendMessageToSet(&data, true);
-}
-
-void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount)
-{
- sLog.outDebug("WORLD: Sending SMSG_ATTACKERSTATEUPDATE");
-
- WorldPacket data(SMSG_ATTACKERSTATEUPDATE, (16+45)); // we guess size
- data << (uint32)HitInfo;
- data.append(GetPackGUID());
- data.append(target->GetPackGUID());
- data << (uint32)(Damage-AbsorbDamage-Resist-BlockedAmount);
-
- data << (uint8)SwingType; // count?
-
- // for(i = 0; i < SwingType; ++i)
- data << (uint32)damageSchoolMask;
- data << (float)(Damage-AbsorbDamage-Resist-BlockedAmount);
- // still need to double check damage
- data << (uint32)(Damage-AbsorbDamage-Resist-BlockedAmount);
- data << (uint32)AbsorbDamage;
- data << (uint32)Resist;
- // end loop
-
- data << (uint32)TargetState;
-
- if( AbsorbDamage == 0 ) //also 0x3E8 = 0x3E8, check when that happens
- data << (uint32)0;
- else
- data << (uint32)-1;
-
- data << (uint32)0;
- data << (uint32)BlockedAmount;
-
- SendMessageToSet( &data, true );
-}
-
-void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage, SpellSchoolMask damageSchoolMask, SpellEntry const *procSpell, bool isTriggeredSpell, WeaponAttackType attType)
-{
- sLog.outDebug("ProcDamageAndSpell: attacker flags are 0x%x, victim flags 0x%x", procAttacker, procVictim);
- if(procSpell)
- sLog.outDebug("ProcDamageAndSpell: invoked due to spell id %u %s", procSpell->Id, (isTriggeredSpell?"(triggered)":""));
-
- // Assign melee/ranged proc flags for magic attacks, that are actually melee/ranged abilities
- // not assign for spell proc triggered spell to prevent infinity (or unexpacted 2-3 times) melee damage spell proc call with melee damage effect
- // That is the question though if it's fully correct
- if(procSpell && !isTriggeredSpell)
- {
- if(procSpell->DmgClass == SPELL_DAMAGE_CLASS_MELEE)
- {
- if(procAttacker & PROC_FLAG_HIT_SPELL) procAttacker |= PROC_FLAG_HIT_MELEE;
- if(procAttacker & PROC_FLAG_CRIT_SPELL) procAttacker |= PROC_FLAG_CRIT_MELEE;
- if(procVictim & PROC_FLAG_STRUCK_SPELL) procVictim |= PROC_FLAG_STRUCK_MELEE;
- if(procVictim & PROC_FLAG_STRUCK_CRIT_SPELL) procVictim |= PROC_FLAG_STRUCK_CRIT_MELEE;
- attType = BASE_ATTACK; // Melee abilities are assumed to be dealt with mainhand weapon
- }
- else if (procSpell->DmgClass == SPELL_DAMAGE_CLASS_RANGED)
- {
- if(procAttacker & PROC_FLAG_HIT_SPELL) procAttacker |= PROC_FLAG_HIT_RANGED;
- if(procAttacker & PROC_FLAG_CRIT_SPELL) procAttacker |= PROC_FLAG_CRIT_RANGED;
- if(procVictim & PROC_FLAG_STRUCK_SPELL) procVictim |= PROC_FLAG_STRUCK_RANGED;
- if(procVictim & PROC_FLAG_STRUCK_CRIT_SPELL) procVictim |= PROC_FLAG_STRUCK_CRIT_RANGED;
- attType = RANGED_ATTACK;
- }
- }
- if(damage && (procVictim & (PROC_FLAG_STRUCK_MELEE|PROC_FLAG_STRUCK_RANGED|PROC_FLAG_STRUCK_SPELL)))
- procVictim |= (PROC_FLAG_TAKE_DAMAGE|PROC_FLAG_TOUCH);
-
- // Not much to do if no flags are set.
- if (procAttacker)
- {
- // procces auras that not generate casts at proc event before auras that generate casts to prevent proc aura added at prev. proc aura execute in set
- ProcDamageAndSpellFor(false,pVictim,procAttacker,attackerProcEffectAuraTypes,attType, procSpell, damage, damageSchoolMask);
- ProcDamageAndSpellFor(false,pVictim,procAttacker,attackerProcCastAuraTypes,attType, procSpell, damage, damageSchoolMask);
- }
-
- // Now go on with a victim's events'n'auras
- // Not much to do if no flags are set or there is no victim
- if(pVictim && pVictim->isAlive() && procVictim)
- {
- // procces auras that not generate casts at proc event before auras that generate casts to prevent proc aura added at prev. proc aura execute in set
- pVictim->ProcDamageAndSpellFor(true,this,procVictim,victimProcEffectAuraTypes,attType,procSpell, damage, damageSchoolMask);
- pVictim->ProcDamageAndSpellFor(true,this,procVictim,victimProcCastAuraTypes,attType,procSpell, damage, damageSchoolMask);
- }
-}
-
-void Unit::CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, SpellSchoolMask damageSchoolMask, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted, bool isTriggeredSpell)
-{
- if(!pVictim)
- return;
-
- uint32 procAttacker = PROC_FLAG_NONE;
- uint32 procVictim = PROC_FLAG_NONE;
-
- switch(outcome)
- {
- case MELEE_HIT_EVADE:
- return;
- case MELEE_HIT_MISS:
- if(attType == BASE_ATTACK || attType == OFF_ATTACK)
- {
- procAttacker = PROC_FLAG_MISS;
- }
- break;
- case MELEE_HIT_BLOCK_CRIT:
- case MELEE_HIT_CRIT:
- if(spellCasted && attType == BASE_ATTACK)
- {
- procAttacker |= PROC_FLAG_CRIT_SPELL;
- procVictim |= PROC_FLAG_STRUCK_CRIT_SPELL;
- if ( outcome == MELEE_HIT_BLOCK_CRIT )
- {
- procVictim |= PROC_FLAG_BLOCK;
- procAttacker |= PROC_FLAG_TARGET_BLOCK;
- }
- }
- else if(attType == BASE_ATTACK || attType == OFF_ATTACK)
- {
- procAttacker = PROC_FLAG_HIT_MELEE | PROC_FLAG_CRIT_MELEE;
- procVictim = PROC_FLAG_STRUCK_MELEE | PROC_FLAG_STRUCK_CRIT_MELEE;
- }
- else
- {
- procAttacker = PROC_FLAG_HIT_RANGED | PROC_FLAG_CRIT_RANGED;
- procVictim = PROC_FLAG_STRUCK_RANGED | PROC_FLAG_STRUCK_CRIT_RANGED;
- }
- break;
- case MELEE_HIT_PARRY:
- procAttacker = PROC_FLAG_TARGET_DODGE_OR_PARRY;
- procVictim = PROC_FLAG_PARRY;
- break;
- case MELEE_HIT_BLOCK:
- procAttacker = PROC_FLAG_TARGET_BLOCK;
- procVictim = PROC_FLAG_BLOCK;
- break;
- case MELEE_HIT_DODGE:
- procAttacker = PROC_FLAG_TARGET_DODGE_OR_PARRY;
- procVictim = PROC_FLAG_DODGE;
- break;
- case MELEE_HIT_CRUSHING:
- if(attType == BASE_ATTACK || attType == OFF_ATTACK)
- {
- procAttacker = PROC_FLAG_HIT_MELEE | PROC_FLAG_CRIT_MELEE;
- procVictim = PROC_FLAG_STRUCK_MELEE | PROC_FLAG_STRUCK_CRIT_MELEE;
- }
- else
- {
- procAttacker = PROC_FLAG_HIT_RANGED | PROC_FLAG_CRIT_RANGED;
- procVictim = PROC_FLAG_STRUCK_RANGED | PROC_FLAG_STRUCK_CRIT_RANGED;
- }
- break;
- default:
- if(attType == BASE_ATTACK || attType == OFF_ATTACK)
- {
- procAttacker = PROC_FLAG_HIT_MELEE;
- procVictim = PROC_FLAG_STRUCK_MELEE;
- }
- else
- {
- procAttacker = PROC_FLAG_HIT_RANGED;
- procVictim = PROC_FLAG_STRUCK_RANGED;
- }
- break;
- }
-
- if(damage > 0)
- procVictim |= PROC_FLAG_TAKE_DAMAGE;
-
- if(procAttacker != PROC_FLAG_NONE || procVictim != PROC_FLAG_NONE)
- ProcDamageAndSpell(pVictim, procAttacker, procVictim, damage, damageSchoolMask, spellCasted, isTriggeredSpell, attType);
-}
-
-bool Unit::HandleHasteAuraProc(Unit *pVictim, SpellEntry const *hasteSpell, uint32 /*effIndex*/, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 /*procFlag*/, uint32 cooldown)
-{
- Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
- ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
-
- uint32 triggered_spell_id = 0;
- Unit* target = pVictim;
- int32 basepoints0 = 0;
-
- switch(hasteSpell->SpellFamilyName)
- {
- case SPELLFAMILY_ROGUE:
- {
- switch(hasteSpell->Id)
- {
- // Blade Flurry
- case 13877:
- case 33735:
- {
- target = SelectNearbyTarget();
- if(!target)
- return false;
- basepoints0 = damage;
- triggered_spell_id = 22482;
- break;
- }
- }
- break;
- }
- }
-
- // processed charge only counting case
- if(!triggered_spell_id)
- return true;
-
- SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
-
- if(!triggerEntry)
- {
- sLog.outError("Unit::HandleHasteAuraProc: Spell %u have not existed triggered spell %u",hasteSpell->Id,triggered_spell_id);
- return false;
- }
-
- // default case
- if(!target || target!=this && !target->isAlive())
- return false;
-
- if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
- return false;
-
- if(basepoints0)
- CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
- else
- CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura);
-
- if( cooldown && GetTypeId()==TYPEID_PLAYER )
- ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
-
- return true;
-}
-
-bool Unit::HandleDummyAuraProc(Unit *pVictim, SpellEntry const *dummySpell, uint32 effIndex, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 cooldown)
-{
- Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
- ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
-
- uint32 triggered_spell_id = 0;
- Unit* target = pVictim;
- int32 basepoints0 = 0;
-
- switch(dummySpell->SpellFamilyName)
- {
- case SPELLFAMILY_GENERIC:
- {
- switch (dummySpell->Id)
- {
- // Eye of Eye
- case 9799:
- case 25988:
- {
- // prevent damage back from weapon special attacks
- if (!procSpell || procSpell->DmgClass != SPELL_DAMAGE_CLASS_MAGIC )
- return false;
-
- // return damage % to attacker but < 50% own total health
- basepoints0 = triggeredByAura->GetModifier()->m_amount*int32(damage)/100;
- if(basepoints0 > GetMaxHealth()/2)
- basepoints0 = GetMaxHealth()/2;
-
- triggered_spell_id = 25997;
- break;
- }
- // Sweeping Strikes
- case 12328:
- case 18765:
- case 35429:
- {
- // prevent chain of triggred spell from same triggred spell
- if(procSpell && procSpell->Id==26654)
- return false;
-
- target = SelectNearbyTarget();
- if(!target)
- return false;
-
- triggered_spell_id = 26654;
- break;
- }
- // Unstable Power
- case 24658:
- {
- if (!procSpell || procSpell->Id == 24659)
- return false;
- // Need remove one 24659 aura
- RemoveSingleAuraFromStack(24659, 0);
- RemoveSingleAuraFromStack(24659, 1);
- return true;
- }
- // Restless Strength
- case 24661:
- {
- // Need remove one 24662 aura
- RemoveSingleAuraFromStack(24662, 0);
- return true;
- }
- // Adaptive Warding (Frostfire Regalia set)
- case 28764:
- {
- if(!procSpell)
- return false;
-
- // find Mage Armor
- bool found = false;
- AuraList const& mRegenInterupt = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
- for(AuraList::const_iterator iter = mRegenInterupt.begin(); iter != mRegenInterupt.end(); ++iter)
- {
- if(SpellEntry const* iterSpellProto = (*iter)->GetSpellProto())
- {
- if(iterSpellProto->SpellFamilyName==SPELLFAMILY_MAGE && (iterSpellProto->SpellFamilyFlags & 0x10000000))
- {
- found=true;
- break;
- }
- }
- }
- if(!found)
- return false;
-
- switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell)))
- {
- case SPELL_SCHOOL_NORMAL:
- case SPELL_SCHOOL_HOLY:
- return false; // ignored
- case SPELL_SCHOOL_FIRE: triggered_spell_id = 28765; break;
- case SPELL_SCHOOL_NATURE: triggered_spell_id = 28768; break;
- case SPELL_SCHOOL_FROST: triggered_spell_id = 28766; break;
- case SPELL_SCHOOL_SHADOW: triggered_spell_id = 28769; break;
- case SPELL_SCHOOL_ARCANE: triggered_spell_id = 28770; break;
- default:
- return false;
- }
-
- target = this;
- break;
- }
- // Obsidian Armor (Justice Bearer`s Pauldrons shoulder)
- case 27539:
- {
- if(!procSpell)
- return false;
-
- // not from DoT
- bool found = false;
- for(int j = 0; j < 3; ++j)
- {
- if(procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE||procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE_PERCENT)
- {
- found = true;
- break;
- }
- }
- if(found)
- return false;
-
- switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell)))
- {
- case SPELL_SCHOOL_NORMAL:
- return false; // ignore
- case SPELL_SCHOOL_HOLY: triggered_spell_id = 27536; break;
- case SPELL_SCHOOL_FIRE: triggered_spell_id = 27533; break;
- case SPELL_SCHOOL_NATURE: triggered_spell_id = 27538; break;
- case SPELL_SCHOOL_FROST: triggered_spell_id = 27534; break;
- case SPELL_SCHOOL_SHADOW: triggered_spell_id = 27535; break;
- case SPELL_SCHOOL_ARCANE: triggered_spell_id = 27540; break;
- default:
- return false;
- }
-
- target = this;
- break;
- }
- // Mana Leech (Passive) (Priest Pet Aura)
- case 28305:
- {
- // Cast on owner
- target = GetOwner();
- if(!target)
- return false;
-
- basepoints0 = int32(damage * 2.5f); // manaregen
- triggered_spell_id = 34650;
- break;
- }
- // Mark of Malice
- case 33493:
- {
- // Cast finish spell at last charge
- if (triggeredByAura->m_procCharges > 1)
- return false;
-
- target = this;
- triggered_spell_id = 33494;
- break;
- }
- // Twisted Reflection (boss spell)
- case 21063:
- triggered_spell_id = 21064;
- break;
- // Vampiric Aura (boss spell)
- case 38196:
- {
- basepoints0 = 3 * damage; // 300%
- if (basepoints0 < 0)
- return false;
-
- triggered_spell_id = 31285;
- target = this;
- break;
- }
- // Aura of Madness (Darkmoon Card: Madness trinket)
- //=====================================================
- // 39511 Sociopath: +35 strength (Paladin, Rogue, Druid, Warrior)
- // 40997 Delusional: +70 attack power (Rogue, Hunter, Paladin, Warrior, Druid)
- // 40998 Kleptomania: +35 agility (Warrior, Rogue, Paladin, Hunter, Druid)
- // 40999 Megalomania: +41 damage/healing (Druid, Shaman, Priest, Warlock, Mage, Paladin)
- // 41002 Paranoia: +35 spell/melee/ranged crit strike rating (All classes)
- // 41005 Manic: +35 haste (spell, melee and ranged) (All classes)
- // 41009 Narcissism: +35 intellect (Druid, Shaman, Priest, Warlock, Mage, Paladin, Hunter)
- // 41011 Martyr Complex: +35 stamina (All classes)
- // 41406 Dementia: Every 5 seconds either gives you +5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin)
- // 41409 Dementia: Every 5 seconds either gives you -5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin)
- case 39446:
- {
- if(GetTypeId() != TYPEID_PLAYER)
- return false;
-
- // Select class defined buff
- switch (getClass())
- {
- case CLASS_PALADIN: // 39511,40997,40998,40999,41002,41005,41009,41011,41409
- case CLASS_DRUID: // 39511,40997,40998,40999,41002,41005,41009,41011,41409
- {
- uint32 RandomSpell[]={39511,40997,40998,40999,41002,41005,41009,41011,41409};
- triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
- break;
- }
- case CLASS_ROGUE: // 39511,40997,40998,41002,41005,41011
- case CLASS_WARRIOR: // 39511,40997,40998,41002,41005,41011
- {
- uint32 RandomSpell[]={39511,40997,40998,41002,41005,41011};
- triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
- break;
- }
- case CLASS_PRIEST: // 40999,41002,41005,41009,41011,41406,41409
- case CLASS_SHAMAN: // 40999,41002,41005,41009,41011,41406,41409
- case CLASS_MAGE: // 40999,41002,41005,41009,41011,41406,41409
- case CLASS_WARLOCK: // 40999,41002,41005,41009,41011,41406,41409
- {
- uint32 RandomSpell[]={40999,41002,41005,41009,41011,41406,41409};
- triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
- break;
- }
- case CLASS_HUNTER: // 40997,40999,41002,41005,41009,41011,41406,41409
- {
- uint32 RandomSpell[]={40997,40999,41002,41005,41009,41011,41406,41409};
- triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
- break;
- }
- default:
- return false;
- }
-
- target = this;
- if (roll_chance_i(10))
- ((Player*)this)->Say("This is Madness!", LANG_UNIVERSAL);
- break;
- }
- /*
- // TODO: need find item for aura and triggered spells
- // Sunwell Exalted Caster Neck (??? neck)
- // cast ??? Light's Wrath if Exalted by Aldor
- // cast ??? Arcane Bolt if Exalted by Scryers*/
- case 46569:
- return false; // disable for while
- /*
- {
- if(GetTypeId() != TYPEID_PLAYER)
- return false;
-
- // Get Aldor reputation rank
- if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
- {
- target = this;
- triggered_spell_id = ???
- break;
- }
- // Get Scryers reputation rank
- if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
- {
- triggered_spell_id = ???
- break;
- }
- return false;
- }/**/
- // Sunwell Exalted Caster Neck (Shattered Sun Pendant of Acumen neck)
- // cast 45479 Light's Wrath if Exalted by Aldor
- // cast 45429 Arcane Bolt if Exalted by Scryers
- case 45481:
- {
- if(GetTypeId() != TYPEID_PLAYER)
- return false;
-
- // Get Aldor reputation rank
- if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
- {
- target = this;
- triggered_spell_id = 45479;
- break;
- }
- // Get Scryers reputation rank
- if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
- {
- triggered_spell_id = 45429;
- break;
- }
- return false;
- }
- // Sunwell Exalted Melee Neck (Shattered Sun Pendant of Might neck)
- // cast 45480 Light's Strength if Exalted by Aldor
- // cast 45428 Arcane Strike if Exalted by Scryers
- case 45482:
- {
- if(GetTypeId() != TYPEID_PLAYER)
- return false;
-
- // Get Aldor reputation rank
- if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
- {
- target = this;
- triggered_spell_id = 45480;
- break;
- }
- // Get Scryers reputation rank
- if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
- {
- triggered_spell_id = 45428;
- break;
- }
- return false;
- }
- // Sunwell Exalted Tank Neck (Shattered Sun Pendant of Resolve neck)
- // cast 45431 Arcane Insight if Exalted by Aldor
- // cast 45432 Light's Ward if Exalted by Scryers
- case 45483:
- {
- if(GetTypeId() != TYPEID_PLAYER)
- return false;
-
- // Get Aldor reputation rank
- if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
- {
- target = this;
- triggered_spell_id = 45432;
- break;
- }
- // Get Scryers reputation rank
- if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
- {
- target = this;
- triggered_spell_id = 45431;
- break;
- }
- return false;
- }
- // Sunwell Exalted Healer Neck (Shattered Sun Pendant of Restoration neck)
- // cast 45478 Light's Salvation if Exalted by Aldor
- // cast 45430 Arcane Surge if Exalted by Scryers
- case 45484:
- {
- if(GetTypeId() != TYPEID_PLAYER)
- return false;
-
- // Get Aldor reputation rank
- if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
- {
- target = this;
- triggered_spell_id = 45478;
- break;
- }
- // Get Scryers reputation rank
- if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
- {
- triggered_spell_id = 45430;
- break;
- }
- return false;
- }
- }
- break;
- }
- case SPELLFAMILY_MAGE:
- {
- // Magic Absorption
- if (dummySpell->SpellIconID == 459) // only this spell have SpellIconID == 459 and dummy aura
- {
- if (getPowerType() != POWER_MANA)
- return false;
-
- // mana reward
- basepoints0 = (triggeredByAura->GetModifier()->m_amount * GetMaxPower(POWER_MANA) / 100);
- target = this;
- triggered_spell_id = 29442;
- break;
- }
- // Master of Elements
- if (dummySpell->SpellIconID == 1920)
- {
- if(!procSpell)
- return false;
-
- // mana cost save
- basepoints0 = procSpell->manaCost * triggeredByAura->GetModifier()->m_amount/100;
- if( basepoints0 <=0 )
- return false;
-
- target = this;
- triggered_spell_id = 29077;
- break;
- }
- switch(dummySpell->Id)
- {
- // Ignite
- case 11119:
- case 11120:
- case 12846:
- case 12847:
- case 12848:
- {
- switch (dummySpell->Id)
- {
- case 11119: basepoints0 = int32(0.04f*damage); break;
- case 11120: basepoints0 = int32(0.08f*damage); break;
- case 12846: basepoints0 = int32(0.12f*damage); break;
- case 12847: basepoints0 = int32(0.16f*damage); break;
- case 12848: basepoints0 = int32(0.20f*damage); break;
- default:
- sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (IG)",dummySpell->Id);
- return false;
- }
-
- triggered_spell_id = 12654;
- break;
- }
- // Combustion
- case 11129:
- {
- //last charge and crit
- if( triggeredByAura->m_procCharges <= 1 && (procFlag & PROC_FLAG_CRIT_SPELL) )
- {
- RemoveAurasDueToSpell(28682); //-> remove Combustion auras
- return true; // charge counting (will removed)
- }
-
- CastSpell(this, 28682, true, castItem, triggeredByAura);
- return(procFlag & PROC_FLAG_CRIT_SPELL);// charge update only at crit hits, no hidden cooldowns
- }
- }
- break;
- }
- case SPELLFAMILY_WARRIOR:
- {
- // Retaliation
- if(dummySpell->SpellFamilyFlags==0x0000000800000000LL)
- {
- // check attack comes not from behind
- if (!HasInArc(M_PI, pVictim))
- return false;
-
- triggered_spell_id = 22858;
- break;
- }
- break;
- }
- case SPELLFAMILY_WARLOCK:
- {
- // Seed of Corruption
- if (dummySpell->SpellFamilyFlags & 0x0000001000000000LL)
- {
- Modifier* mod = triggeredByAura->GetModifier();
- // if damage is more than need or target die from damage deal finish spell
- // FIX ME: not triggered currently at death
- if( mod->m_amount <= damage || GetHealth() <= damage )
- {
- // remember guid before aura delete
- uint64 casterGuid = triggeredByAura->GetCasterGUID();
-
- // Remove aura (before cast for prevent infinite loop handlers)
- RemoveAurasDueToSpell(triggeredByAura->GetId());
-
- // Cast finish spell (triggeredByAura already not exist!)
- CastSpell(this, 27285, true, castItem, NULL, casterGuid);
- return true; // no hidden cooldown
- }
-
- // Damage counting
- mod->m_amount-=damage;
- return true;
- }
- // Seed of Corruption (Mobs cast) - no die req
- if (dummySpell->SpellFamilyFlags == 0x00LL && dummySpell->SpellIconID == 1932)
- {
- Modifier* mod = triggeredByAura->GetModifier();
- // if damage is more than need deal finish spell
- if( mod->m_amount <= damage )
- {
- // remember guid before aura delete
- uint64 casterGuid = triggeredByAura->GetCasterGUID();
-
- // Remove aura (before cast for prevent infinite loop handlers)
- RemoveAurasDueToSpell(triggeredByAura->GetId());
-
- // Cast finish spell (triggeredByAura already not exist!)
- CastSpell(this, 32865, true, castItem, NULL, casterGuid);
- return true; // no hidden cooldown
- }
- // Damage counting
- mod->m_amount-=damage;
- return true;
- }
- switch(dummySpell->Id)
- {
- // Nightfall
- case 18094:
- case 18095:
- {
- target = this;
- triggered_spell_id = 17941;
- break;
- }
- //Soul Leech
- case 30293:
- case 30295:
- case 30296:
- {
- // health
- basepoints0 = int32(damage*triggeredByAura->GetModifier()->m_amount/100);
- target = this;
- triggered_spell_id = 30294;
- break;
- }
- // Shadowflame (Voidheart Raiment set bonus)
- case 37377:
- {
- triggered_spell_id = 37379;
- break;
- }
- // Pet Healing (Corruptor Raiment or Rift Stalker Armor)
- case 37381:
- {
- target = GetPet();
- if(!target)
- return false;
-
- // heal amount
- basepoints0 = damage * triggeredByAura->GetModifier()->m_amount/100;
- triggered_spell_id = 37382;
- break;
- }
- // Shadowflame Hellfire (Voidheart Raiment set bonus)
- case 39437:
- {
- triggered_spell_id = 37378;
- break;
- }
- }
- break;
- }
- case SPELLFAMILY_PRIEST:
- {
- // Vampiric Touch
- if( dummySpell->SpellFamilyFlags & 0x0000040000000000LL )
- {
- if(!pVictim || !pVictim->isAlive())
- return false;
-
- // pVictim is caster of aura
- if(triggeredByAura->GetCasterGUID() != pVictim->GetGUID())
- return false;
-
- // energize amount
- basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100;
- pVictim->CastCustomSpell(pVictim,34919,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
- return true; // no hidden cooldown
- }
- switch(dummySpell->Id)
- {
- // Vampiric Embrace
- case 15286:
- {
- if(!pVictim || !pVictim->isAlive())
- return false;
-
- // pVictim is caster of aura
- if(triggeredByAura->GetCasterGUID() != pVictim->GetGUID())
- return false;
-
- // heal amount
- basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100;
- pVictim->CastCustomSpell(pVictim,15290,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
- return true; // no hidden cooldown
- }
- // Priest Tier 6 Trinket (Ashtongue Talisman of Acumen)
- case 40438:
- {
- // Shadow Word: Pain
- if( procSpell->SpellFamilyFlags & 0x0000000000008000LL )
- triggered_spell_id = 40441;
- // Renew
- else if( procSpell->SpellFamilyFlags & 0x0000000000000010LL )
- triggered_spell_id = 40440;
- else
- return false;
-
- target = this;
- break;
- }
- // Oracle Healing Bonus ("Garments of the Oracle" set)
- case 26169:
- {
- // heal amount
- basepoints0 = int32(damage * 10/100);
- target = this;
- triggered_spell_id = 26170;
- break;
- }
- // Frozen Shadoweave (Shadow's Embrace set) warning! its not only priest set
- case 39372:
- {
- if(!procSpell || (GetSpellSchoolMask(procSpell) & (SPELL_SCHOOL_MASK_FROST | SPELL_SCHOOL_MASK_SHADOW))==0 )
- return false;
-
- // heal amount
- basepoints0 = int32(damage * 2 / 100);
- target = this;
- triggered_spell_id = 39373;
- break;
- }
- // Vestments of Faith (Priest Tier 3) - 4 pieces bonus
- case 28809:
- {
- triggered_spell_id = 28810;
- break;
- }
- }
- break;
- }
- case SPELLFAMILY_DRUID:
- {
- switch(dummySpell->Id)
- {
- // Healing Touch (Dreamwalker Raiment set)
- case 28719:
- {
- // mana back
- basepoints0 = int32(procSpell->manaCost * 30 / 100);
- target = this;
- triggered_spell_id = 28742;
- break;
- }
- // Healing Touch Refund (Idol of Longevity trinket)
- case 28847:
- {
- target = this;
- triggered_spell_id = 28848;
- break;
- }
- // Mana Restore (Malorne Raiment set / Malorne Regalia set)
- case 37288:
- case 37295:
- {
- target = this;
- triggered_spell_id = 37238;
- break;
- }
- // Druid Tier 6 Trinket
- case 40442:
- {
- float chance;
-
- // Starfire
- if( procSpell->SpellFamilyFlags & 0x0000000000000004LL )
- {
- triggered_spell_id = 40445;
- chance = 25.f;
- }
- // Rejuvenation
- else if( procSpell->SpellFamilyFlags & 0x0000000000000010LL )
- {
- triggered_spell_id = 40446;
- chance = 25.f;
- }
- // Mangle (cat/bear)
- else if( procSpell->SpellFamilyFlags & 0x0000044000000000LL )
- {
- triggered_spell_id = 40452;
- chance = 40.f;
- }
- else
- return false;
-
- if (!roll_chance_f(chance))
- return false;
-
- target = this;
- break;
- }
- // Maim Interrupt
- case 44835:
- {
- // Deadly Interrupt Effect
- triggered_spell_id = 32747;
- break;
- }
- }
- break;
- }
- case SPELLFAMILY_ROGUE:
- {
- switch(dummySpell->Id)
- {
- // Deadly Throw Interrupt
- case 32748:
- {
- // Prevent cast Deadly Throw Interrupt on self from last effect (apply dummy) of Deadly Throw
- if(this == pVictim)
- return false;
-
- triggered_spell_id = 32747;
- break;
- }
- }
- // Quick Recovery
- if( dummySpell->SpellIconID == 2116 )
- {
- if(!procSpell)
- return false;
-
- // only rogue's finishing moves (maybe need additional checks)
- if( procSpell->SpellFamilyName!=SPELLFAMILY_ROGUE ||
- (procSpell->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE) == 0)
- return false;
-
- // energy cost save
- basepoints0 = procSpell->manaCost * triggeredByAura->GetModifier()->m_amount/100;
- if(basepoints0 <= 0)
- return false;
-
- target = this;
- triggered_spell_id = 31663;
- break;
- }
- break;
- }
- case SPELLFAMILY_HUNTER:
- {
- // Thrill of the Hunt
- if ( dummySpell->SpellIconID == 2236 )
- {
- if(!procSpell)
- return false;
-
- // mana cost save
- basepoints0 = procSpell->manaCost * 40/100;
- if(basepoints0 <= 0)
- return false;
-
- target = this;
- triggered_spell_id = 34720;
- break;
- }
- break;
- }
- case SPELLFAMILY_PALADIN:
- {
- // Seal of Righteousness - melee proc dummy
- if (dummySpell->SpellFamilyFlags&0x000000008000000LL && triggeredByAura->GetEffIndex()==0)
- {
- if(GetTypeId() != TYPEID_PLAYER)
- return false;
-
- uint32 spellId;
- switch (triggeredByAura->GetId())
- {
- case 21084: spellId = 25742; break; // Rank 1
- case 20287: spellId = 25740; break; // Rank 2
- case 20288: spellId = 25739; break; // Rank 3
- case 20289: spellId = 25738; break; // Rank 4
- case 20290: spellId = 25737; break; // Rank 5
- case 20291: spellId = 25736; break; // Rank 6
- case 20292: spellId = 25735; break; // Rank 7
- case 20293: spellId = 25713; break; // Rank 8
- case 27155: spellId = 27156; break; // Rank 9
- default:
- sLog.outError("Unit::HandleDummyAuraProc: non handled possibly SoR (Id = %u)", triggeredByAura->GetId());
- return false;
- }
- Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
- float speed = (item ? item->GetProto()->Delay : BASE_ATTACK_TIME)/1000.0f;
-
- float damageBasePoints;
- if(item && item->GetProto()->InventoryType == INVTYPE_2HWEAPON)
- // two hand weapon
- damageBasePoints=1.20f*triggeredByAura->GetModifier()->m_amount * 1.2f * 1.03f * speed/100.0f + 1;
- else
- // one hand weapon/no weapon
- damageBasePoints=0.85f*ceil(triggeredByAura->GetModifier()->m_amount * 1.2f * 1.03f * speed/100.0f) - 1;
-
- int32 damagePoint = int32(damageBasePoints + 0.03f * (GetWeaponDamageRange(BASE_ATTACK,MINDAMAGE)+GetWeaponDamageRange(BASE_ATTACK,MAXDAMAGE))/2.0f) + 1;
-
- // apply damage bonuses manually
- if(damagePoint >= 0)
- damagePoint = SpellDamageBonus(pVictim, dummySpell, damagePoint, SPELL_DIRECT_DAMAGE);
-
- CastCustomSpell(pVictim,spellId,&damagePoint,NULL,NULL,true,NULL, triggeredByAura);
- return true; // no hidden cooldown
- }
- // Seal of Blood do damage trigger
- if(dummySpell->SpellFamilyFlags & 0x0000040000000000LL)
- {
- switch(triggeredByAura->GetEffIndex())
- {
- case 0:
- // prevent chain triggering
- if(procSpell && procSpell->Id==31893 )
- return false;
-
- triggered_spell_id = 31893;
- break;
- case 1:
- {
- // damage
- basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100;
- target = this;
- triggered_spell_id = 32221;
- break;
- }
- }
- }
-
- switch(dummySpell->Id)
- {
- // Holy Power (Redemption Armor set)
- case 28789:
- {
- if(!pVictim)
- return false;
-
- // Set class defined buff
- switch (pVictim->getClass())
- {
- case CLASS_PALADIN:
- case CLASS_PRIEST:
- case CLASS_SHAMAN:
- case CLASS_DRUID:
- triggered_spell_id = 28795; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d.
- break;
- case CLASS_MAGE:
- case CLASS_WARLOCK:
- triggered_spell_id = 28793; // Increases the friendly target's spell damage and healing by up to $s1 for $d.
- break;
- case CLASS_HUNTER:
- case CLASS_ROGUE:
- triggered_spell_id = 28791; // Increases the friendly target's attack power by $s1 for $d.
- break;
- case CLASS_WARRIOR:
- triggered_spell_id = 28790; // Increases the friendly target's armor
- break;
- default:
- return false;
- }
- break;
- }
- //Seal of Vengeance
- case 31801:
- {
- if(effIndex != 0) // effect 1,2 used by seal unleashing code
- return false;
-
- triggered_spell_id = 31803;
- break;
- }
- // Spiritual Att.
- case 31785:
- case 33776:
- {
- // if healed by another unit (pVictim)
- if(this == pVictim)
- return false;
-
- // heal amount
- basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100;
- target = this;
- triggered_spell_id = 31786;
- break;
- }
- // Paladin Tier 6 Trinket (Ashtongue Talisman of Zeal)
- case 40470:
- {
- if( !procSpell )
- return false;
-
- float chance;
-
- // Flash of light/Holy light
- if( procSpell->SpellFamilyFlags & 0x00000000C0000000LL)
- {
- triggered_spell_id = 40471;
- chance = 15.f;
- }
- // Judgement
- else if( procSpell->SpellFamilyFlags & 0x0000000000800000LL )
- {
- triggered_spell_id = 40472;
- chance = 50.f;
- }
- else
- return false;
-
- if (!roll_chance_f(chance))
- return false;
-
- break;
- }
- }
- break;
- }
- case SPELLFAMILY_SHAMAN:
- {
- switch(dummySpell->Id)
- {
- // Totemic Power (The Earthshatterer set)
- case 28823:
- {
- if( !pVictim )
- return false;
-
- // Set class defined buff
- switch (pVictim->getClass())
- {
- case CLASS_PALADIN:
- case CLASS_PRIEST:
- case CLASS_SHAMAN:
- case CLASS_DRUID:
- triggered_spell_id = 28824; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d.
- break;
- case CLASS_MAGE:
- case CLASS_WARLOCK:
- triggered_spell_id = 28825; // Increases the friendly target's spell damage and healing by up to $s1 for $d.
- break;
- case CLASS_HUNTER:
- case CLASS_ROGUE:
- triggered_spell_id = 28826; // Increases the friendly target's attack power by $s1 for $d.
- break;
- case CLASS_WARRIOR:
- triggered_spell_id = 28827; // Increases the friendly target's armor
- break;
- default:
- return false;
- }
- break;
- }
- // Lesser Healing Wave (Totem of Flowing Water Relic)
- case 28849:
- {
- target = this;
- triggered_spell_id = 28850;
- break;
- }
- // Windfury Weapon (Passive) 1-5 Ranks
- case 33757:
- {
- if(GetTypeId()!=TYPEID_PLAYER)
- return false;
-
- if(!castItem || !castItem->IsEquipped())
- return false;
-
- // custom cooldown processing case
- if( cooldown && ((Player*)this)->HasSpellCooldown(dummySpell->Id))
- return false;
-
- uint32 spellId;
- switch (castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)))
- {
- case 283: spellId = 33757; break; //1 Rank
- case 284: spellId = 33756; break; //2 Rank
- case 525: spellId = 33755; break; //3 Rank
- case 1669:spellId = 33754; break; //4 Rank
- case 2636:spellId = 33727; break; //5 Rank
- default:
- {
- sLog.outError("Unit::HandleDummyAuraProc: non handled item enchantment (rank?) %u for spell id: %u (Windfury)",
- castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)),dummySpell->Id);
- return false;
- }
- }
-
- SpellEntry const* windfurySpellEntry = sSpellStore.LookupEntry(spellId);
- if(!windfurySpellEntry)
- {
- sLog.outError("Unit::HandleDummyAuraProc: non existed spell id: %u (Windfury)",spellId);
- return false;
- }
-
- int32 extra_attack_power = CalculateSpellDamage(windfurySpellEntry,0,windfurySpellEntry->EffectBasePoints[0],pVictim);
-
- // Off-Hand case
- if ( castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND )
- {
- // Value gained from additional AP
- basepoints0 = int32(extra_attack_power/14.0f * GetAttackTime(OFF_ATTACK)/1000/2);
- triggered_spell_id = 33750;
- }
- // Main-Hand case
- else
- {
- // Value gained from additional AP
- basepoints0 = int32(extra_attack_power/14.0f * GetAttackTime(BASE_ATTACK)/1000);
- triggered_spell_id = 25504;
- }
-
- // apply cooldown before cast to prevent processing itself
- if( cooldown )
- ((Player*)this)->AddSpellCooldown(dummySpell->Id,0,time(NULL) + cooldown);
-
- // Attack Twice
- for ( uint32 i = 0; i<2; ++i )
- CastCustomSpell(pVictim,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
-
- return true;
- }
- // Shaman Tier 6 Trinket
- case 40463:
- {
- if( !procSpell )
- return false;
-
- float chance;
- if (procSpell->SpellFamilyFlags & 0x0000000000000001LL)
- {
- triggered_spell_id = 40465; // Lightning Bolt
- chance = 15.f;
- }
- else if (procSpell->SpellFamilyFlags & 0x0000000000000080LL)
- {
- triggered_spell_id = 40465; // Lesser Healing Wave
- chance = 10.f;
- }
- else if (procSpell->SpellFamilyFlags & 0x0000001000000000LL)
- {
- triggered_spell_id = 40466; // Stormstrike
- chance = 50.f;
- }
- else
- return false;
-
- if (!roll_chance_f(chance))
- return false;
-
- target = this;
- break;
- }
- }
-
- // Earth Shield
- if(dummySpell->SpellFamilyFlags==0x40000000000LL)
- {
- if(GetTypeId() != TYPEID_PLAYER)
- return false;
-
- // heal
- basepoints0 = triggeredByAura->GetModifier()->m_amount;
- target = this;
- triggered_spell_id = 379;
- break;
- }
- // Lightning Overload
- if (dummySpell->SpellIconID == 2018) // only this spell have SpellFamily Shaman SpellIconID == 2018 and dummy aura
- {
- if(!procSpell || GetTypeId() != TYPEID_PLAYER || !pVictim )
- return false;
-
- // custom cooldown processing case
- if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(dummySpell->Id))
- return false;
-
- uint32 spellId = 0;
- // Every Lightning Bolt and Chain Lightning spell have dublicate vs half damage and zero cost
- switch (procSpell->Id)
- {
- // Lightning Bolt
- case 403: spellId = 45284; break; // Rank 1
- case 529: spellId = 45286; break; // Rank 2
- case 548: spellId = 45287; break; // Rank 3
- case 915: spellId = 45288; break; // Rank 4
- case 943: spellId = 45289; break; // Rank 5
- case 6041: spellId = 45290; break; // Rank 6
- case 10391: spellId = 45291; break; // Rank 7
- case 10392: spellId = 45292; break; // Rank 8
- case 15207: spellId = 45293; break; // Rank 9
- case 15208: spellId = 45294; break; // Rank 10
- case 25448: spellId = 45295; break; // Rank 11
- case 25449: spellId = 45296; break; // Rank 12
- // Chain Lightning
- case 421: spellId = 45297; break; // Rank 1
- case 930: spellId = 45298; break; // Rank 2
- case 2860: spellId = 45299; break; // Rank 3
- case 10605: spellId = 45300; break; // Rank 4
- case 25439: spellId = 45301; break; // Rank 5
- case 25442: spellId = 45302; break; // Rank 6
- default:
- sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (LO)", procSpell->Id);
- return false;
- }
- // No thread generated mod
- SpellModifier *mod = new SpellModifier;
- mod->op = SPELLMOD_THREAT;
- mod->value = -100;
- mod->type = SPELLMOD_PCT;
- mod->spellId = dummySpell->Id;
- mod->effectId = 0;
- mod->lastAffected = NULL;
- mod->mask = 0x0000000000000003LL;
- mod->charges = 0;
- ((Player*)this)->AddSpellMod(mod, true);
-
- // Remove cooldown (Chain Lightning - have Category Recovery time)
- if (procSpell->SpellFamilyFlags & 0x0000000000000002LL)
- ((Player*)this)->RemoveSpellCooldown(spellId);
-
- // Hmmm.. in most case spells alredy set half basepoints but...
- // Lightning Bolt (2-10 rank) have full basepoint and half bonus from level
- // As on wiki:
- // BUG: Rank 2 to 10 (and maybe 11) of Lightning Bolt will proc another Bolt with FULL damage (not halved). This bug is known and will probably be fixed soon.
- // So - no add changes :)
- CastSpell(pVictim, spellId, true, castItem, triggeredByAura);
-
- ((Player*)this)->AddSpellMod(mod, false);
-
- if( cooldown && GetTypeId()==TYPEID_PLAYER )
- ((Player*)this)->AddSpellCooldown(dummySpell->Id,0,time(NULL) + cooldown);
-
- return true;
- }
- break;
- }
- default:
- break;
- }
-
- // processed charge only counting case
- if(!triggered_spell_id)
- return true;
-
- SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
-
- if(!triggerEntry)
- {
- sLog.outError("Unit::HandleDummyAuraProc: Spell %u have not existed triggered spell %u",dummySpell->Id,triggered_spell_id);
- return false;
- }
-
- // default case
- if(!target || target!=this && !target->isAlive())
- return false;
-
- if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
- return false;
-
- if(basepoints0)
- CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
- else
- CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura);
-
- if( cooldown && GetTypeId()==TYPEID_PLAYER )
- ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
-
- return true;
-}
-
-bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags,WeaponAttackType attackType, uint32 cooldown)
-{
- SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto();
-
- Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
- ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
-
- uint32 triggered_spell_id = auraSpellInfo->EffectTriggerSpell[triggeredByAura->GetEffIndex()];
- Unit* target = !(procFlags & PROC_FLAG_HEAL) && IsPositiveSpell(triggered_spell_id) ? this : pVictim;
- int32 basepoints0 = 0;
-
- switch(auraSpellInfo->SpellFamilyName)
- {
- case SPELLFAMILY_GENERIC:
- {
- switch(auraSpellInfo->Id)
- {
- // Aegis of Preservation
- case 23780:
- //Aegis Heal (instead non-existed triggered spell)
- triggered_spell_id = 23781;
- target = this;
- break;
- // Elune's Touch (moonkin mana restore)
- case 24905:
- {
- // Elune's Touch (instead non-existed triggered spell)
- triggered_spell_id = 33926;
- basepoints0 = int32(0.3f * GetTotalAttackPowerValue(BASE_ATTACK));
- target = this;
- break;
- }
- // Enlightenment
- case 29601:
- {
- // only for cast with mana price
- if(!procSpell || procSpell->powerType!=POWER_MANA || procSpell->manaCost==0 && procSpell->ManaCostPercentage==0 && procSpell->manaCostPerlevel==0)
- return false;
- break; // fall through to normal cast
- }
- // Health Restore
- case 33510:
- {
- // at melee hit call std triggered spell
- if(procFlags & PROC_FLAG_HIT_MELEE)
- break; // fall through to normal cast
-
- // Mark of Conquest - else (at range hit) called custom case
- triggered_spell_id = 39557;
- target = this;
- break;
- }
- // Shaleskin
- case 36576:
- return true; // nothing to do
- // Forgotten Knowledge (Blade of Wizardry)
- case 38319:
- // only for harmful enemy targeted spell
- if(!pVictim || pVictim==this || !procSpell || IsPositiveSpell(procSpell->Id))
- return false;
- break; // fall through to normal cast
- // Aura of Wrath (Darkmoon Card: Wrath trinket bonus)
- case 39442:
- {
- // proc only at non-crit hits
- if(procFlags & (PROC_FLAG_CRIT_MELEE|PROC_FLAG_CRIT_RANGED|PROC_FLAG_CRIT_SPELL))
- return false;
- break; // fall through to normal cast
- }
- // Augment Pain (Timbal's Focusing Crystal trinket bonus)
- case 45054:
- {
- if(!procSpell)
- return false;
-
- //only periodic damage can trigger spell
- bool found = false;
- for(int j = 0; j < 3; ++j)
- {
- if( procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE ||
- procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE_PERCENT ||
- procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_LEECH )
- {
- found = true;
- break;
- }
- }
- if(!found)
- return false;
-
- break; // fall through to normal cast
- }
- // Evasive Maneuvers (Commendation of Kael'thas)
- case 45057:
- {
- // damage taken that reduces below 35% health
- // does NOT mean you must have been >= 35% before
- if (int32(GetHealth())-int32(damage) >= int32(GetMaxHealth()*0.35f))
- return false;
- break; // fall through to normal cast
- }
- }
-
- switch(triggered_spell_id)
- {
- // Setup
- case 15250:
- {
- // applied only for main target
- if(!pVictim || pVictim != getVictim())
- return false;
-
- // continue normal case
- break;
- }
- // Shamanistic Rage triggered spell
- case 30824:
- basepoints0 = int32(GetTotalAttackPowerValue(BASE_ATTACK)*triggeredByAura->GetModifier()->m_amount/100);
- break;
- }
- break;
- }
- case SPELLFAMILY_MAGE:
- {
- switch(auraSpellInfo->SpellIconID)
- {
- // Blazing Speed
- case 2127:
- //Blazing Speed (instead non-existed triggered spell)
- triggered_spell_id = 31643;
- target = this;
- break;
- }
- switch(auraSpellInfo->Id)
- {
- // Persistent Shield (Scarab Brooch)
- case 26467:
- basepoints0 = int32(damage * 0.15f);
- break;
- }
- break;
- }
- case SPELLFAMILY_WARRIOR:
- {
- //Rampage
- if((auraSpellInfo->SpellFamilyFlags & 0x100000) && auraSpellInfo->SpellIconID==2006)
- {
- //all ranks have effect[0]==AURA (Proc Trigger Spell, non-existed)
- //and effect[1]==TriggerSpell
- if(auraSpellInfo->Effect[1]!=SPELL_EFFECT_TRIGGER_SPELL)
- {
- sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have wrong effect in RM",triggeredByAura->GetSpellProto()->Id);
- return false;
- }
- triggered_spell_id = auraSpellInfo->EffectTriggerSpell[1];
- break; // fall through to normal cast
- }
- break;
- }
- case SPELLFAMILY_WARLOCK:
- {
- // Pyroclasm
- if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000 && auraSpellInfo->SpellIconID==1137)
- {
- // last case for Hellfire that damage caster also but don't must stun caster
- if( pVictim == this )
- return false;
-
- // custom chnace
- float chance = 0;
- switch (triggeredByAura->GetId())
- {
- case 18096: chance = 13.0f; break;
- case 18073: chance = 26.0f; break;
- }
- if (!roll_chance_f(chance))
- return false;
-
- // Pyroclasm (instead non-existed triggered spell)
- triggered_spell_id = 18093;
- target = pVictim;
- break;
- }
- // Drain Soul
- if(auraSpellInfo->SpellFamilyFlags & 0x0000000000004000)
- {
- bool found = false;
- Unit::AuraList const& mAddFlatModifier = GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER);
- for(Unit::AuraList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i)
- {
- //Improved Drain Soul
- if ((*i)->GetModifier()->m_miscvalue == SPELLMOD_CHANCE_OF_SUCCESS && (*i)->GetSpellProto()->SpellIconID == 113)
- {
- int32 value2 = CalculateSpellDamage((*i)->GetSpellProto(),2,(*i)->GetSpellProto()->EffectBasePoints[2],this);
- basepoints0 = value2 * GetMaxPower(POWER_MANA) / 100;
-
- // Drain Soul
- triggered_spell_id = 18371;
- target = this;
- found = true;
- break;
- }
- }
- if(!found)
- return false;
- break; // fall through to normal cast
- }
- break;
- }
- case SPELLFAMILY_PRIEST:
- {
- //Blessed Recovery
- if(auraSpellInfo->SpellFamilyFlags == 0x00000000LL && auraSpellInfo->SpellIconID==1875)
- {
- switch (triggeredByAura->GetSpellProto()->Id)
- {
- case 27811: triggered_spell_id = 27813; break;
- case 27815: triggered_spell_id = 27817; break;
- case 27816: triggered_spell_id = 27818; break;
- default:
- sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in BR",triggeredByAura->GetSpellProto()->Id);
- return false;
- }
-
- int32 heal_amount = damage * triggeredByAura->GetModifier()->m_amount / 100;
- basepoints0 = heal_amount/3;
- target = this;
- break;
- }
- // Shadowguard
- if((auraSpellInfo->SpellFamilyFlags & 0x80000000LL) && auraSpellInfo->SpellVisual==7958)
- {
- switch(triggeredByAura->GetSpellProto()->Id)
- {
- case 18137:
- triggered_spell_id = 28377; break; // Rank 1
- case 19308:
- triggered_spell_id = 28378; break; // Rank 2
- case 19309:
- triggered_spell_id = 28379; break; // Rank 3
- case 19310:
- triggered_spell_id = 28380; break; // Rank 4
- case 19311:
- triggered_spell_id = 28381; break; // Rank 5
- case 19312:
- triggered_spell_id = 28382; break; // Rank 6
- case 25477:
- triggered_spell_id = 28385; break; // Rank 7
- default:
- sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in SG",triggeredByAura->GetSpellProto()->Id);
- return false;
- }
- target = pVictim;
- break;
- }
- break;
- }
- case SPELLFAMILY_DRUID:
- {
- switch(auraSpellInfo->Id)
- {
- // Leader of the Pack (triggering Improved Leader of the Pack heal)
- case 24932:
- {
- if (triggeredByAura->GetModifier()->m_amount == 0)
- return false;
- basepoints0 = triggeredByAura->GetModifier()->m_amount * GetMaxHealth() / 100;
- triggered_spell_id = 34299;
- break;
- };
- // Druid Forms Trinket (Druid Tier5 Trinket, triggers different spells per Form)
- case 37336:
- {
- switch(m_form)
- {
- case FORM_BEAR:
- case FORM_DIREBEAR:
- triggered_spell_id=37340; break;// Ursine Blessing
- case FORM_CAT:
- triggered_spell_id=37341; break;// Feline Blessing
- case FORM_TREE:
- triggered_spell_id=37342; break;// Slyvan Blessing
- case FORM_MOONKIN:
- triggered_spell_id=37343; break;// Lunar Blessing
- case FORM_NONE:
- triggered_spell_id=37344; break;// Cenarion Blessing (for caster form, except FORM_MOONKIN)
- default:
- return false;
- }
-
- target = this;
- break;
- }
- }
- break;
- }
- case SPELLFAMILY_ROGUE:
- {
- if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000LL)
- {
- switch(auraSpellInfo->SpellIconID)
- {
- // Combat Potency
- case 2260:
- {
- // skip non offhand attacks
- if(attackType!=OFF_ATTACK)
- return false;
- break; // fall through to normal cast
- }
- }
- }
- break;
- }
- case SPELLFAMILY_PALADIN:
- {
- if(auraSpellInfo->SpellFamilyFlags == 0x00000000LL)
- {
- switch(auraSpellInfo->Id)
- {
- // Lightning Capacitor
- case 37657:
- {
- // trinket ProcTriggerSpell but for safe checks for player
- if(!castItem || !pVictim || !pVictim->isAlive() || GetTypeId()!=TYPEID_PLAYER)
- return false;
-
- if(((Player*)this)->HasSpellCooldown(37657))
- return false;
-
- // stacking
- CastSpell(this, 37658, true, castItem, triggeredByAura);
- // 2.5s cooldown before it can stack again, current system allow 1 sec step in cooldown
- ((Player*)this)->AddSpellCooldown(37657,0,time(NULL)+(roll_chance_i(50) ? 2 : 3));
-
- // counting
- uint32 count = 0;
- AuraList const& dummyAura = GetAurasByType(SPELL_AURA_DUMMY);
- for(AuraList::const_iterator itr = dummyAura.begin(); itr != dummyAura.end(); ++itr)
- if((*itr)->GetId()==37658)
- ++count;
-
- // release at 3 aura in stack
- if(count <= 2)
- return true; // main triggered spell casted anyway
-
- RemoveAurasDueToSpell(37658);
- CastSpell(pVictim, 37661, true, castItem, triggeredByAura);
- return true;
- }
- // Healing Discount
- case 37705:
- // Healing Trance (instead non-existed triggered spell)
- triggered_spell_id = 37706;
- target = this;
- break;
- // HoTs on Heals (Fel Reaver's Piston trinket)
- case 38299:
- {
- // at direct heal effect
- if(!procSpell || !IsSpellHaveEffect(procSpell,SPELL_EFFECT_HEAL))
- return false;
-
- // single proc at time
- AuraList const& scAuras = GetSingleCastAuras();
- for(AuraList::const_iterator itr = scAuras.begin(); itr != scAuras.end(); ++itr)
- if((*itr)->GetId()==triggered_spell_id)
- return false;
-
- // positive cast at victim instead self
- target = pVictim;
- break;
- }
- }
- switch(auraSpellInfo->SpellIconID)
- {
- case 241:
- {
- switch(auraSpellInfo->EffectTriggerSpell[0])
- {
- //Illumination
- case 18350:
- {
- if(!procSpell)
- return false;
-
- // procspell is triggered spell but we need mana cost of original casted spell
- uint32 originalSpellId = procSpell->Id;
-
- // Holy Shock
- if(procSpell->SpellFamilyName == SPELLFAMILY_PALADIN)
- {
- if(procSpell->SpellFamilyFlags & 0x0001000000000000LL)
- {
- switch(procSpell->Id)
- {
- case 25914: originalSpellId = 20473; break;
- case 25913: originalSpellId = 20929; break;
- case 25903: originalSpellId = 20930; break;
- case 27175: originalSpellId = 27174; break;
- case 33074: originalSpellId = 33072; break;
- default:
- sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in HShock",procSpell->Id);
- return false;
- }
- }
- }
-
- SpellEntry const *originalSpell = sSpellStore.LookupEntry(originalSpellId);
- if(!originalSpell)
- {
- sLog.outError("Unit::HandleProcTriggerSpell: Spell %u unknown but selected as original in Illu",originalSpellId);
- return false;
- }
-
- // percent stored in effect 1 (class scripts) base points
- int32 percent = auraSpellInfo->EffectBasePoints[1]+1;
-
- basepoints0 = originalSpell->manaCost*percent/100;
- triggered_spell_id = 20272;
- target = this;
- break;
- }
- }
- break;
- }
- }
- }
- if(auraSpellInfo->SpellFamilyFlags & 0x00080000)
- {
- switch(auraSpellInfo->SpellIconID)
- {
- //Judgement of Wisdom (overwrite non existing triggered spell call in spell.dbc
- case 206:
- {
- if(!pVictim || !pVictim->isAlive())
- return false;
-
- uint32 spell = 0;
- switch(triggeredByAura->GetSpellProto()->Id)
- {
- case 20186:
- triggered_spell_id = 20268; // Rank 1
- break;
- case 20354:
- triggered_spell_id = 20352; // Rank 2
- break;
- case 20355:
- triggered_spell_id = 20353; // Rank 3
- break;
- case 27164:
- triggered_spell_id = 27165; // Rank 4
- break;
- default:
- sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in JoW",triggeredByAura->GetSpellProto()->Id);
- return false;
- }
-
- pVictim->CastSpell(pVictim,triggered_spell_id,true,castItem,triggeredByAura,GetGUID());
- return true; // no hidden cooldown
- }
- //Judgement of Light
- case 299:
- {
- if(!pVictim || !pVictim->isAlive())
- return false;
-
- // overwrite non existing triggered spell call in spell.dbc
- uint32 spell = 0;
- switch(triggeredByAura->GetSpellProto()->Id)
- {
- case 20185:
- triggered_spell_id = 20267; // Rank 1
- break;
- case 20344:
- triggered_spell_id = 20341; // Rank 2
- break;
- case 20345:
- triggered_spell_id = 20342; // Rank 3
- break;
- case 20346:
- triggered_spell_id = 20343; // Rank 4
- break;
- case 27162:
- triggered_spell_id = 27163; // Rank 5
- break;
- default:
- sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in JoL",triggeredByAura->GetSpellProto()->Id);
- return false;
- }
- pVictim->CastSpell(pVictim,triggered_spell_id,true,castItem,triggeredByAura,GetGUID());
- return true; // no hidden cooldown
- }
- }
- }
- // custom check for proc spell
- switch(auraSpellInfo->Id)
- {
- // Bonus Healing (item spell)
- case 40971:
- {
- if(!pVictim || !pVictim->isAlive())
- return false;
-
- // bonus if health < 50%
- if(pVictim->GetHealth() >= pVictim->GetMaxHealth()*triggeredByAura->GetModifier()->m_amount/100)
- return false;
-
- // cast at target positive spell
- target = pVictim;
- break;
- }
- }
- switch(triggered_spell_id)
- {
- // Seal of Command
- case 20424:
- // prevent chain of triggered spell from same triggered spell
- if(procSpell && procSpell->Id==20424)
- return false;
- break;
- }
- break;
- }
- case SPELLFAMILY_SHAMAN:
- {
- if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000)
- {
- switch(auraSpellInfo->SpellIconID)
- {
- case 19:
- {
- switch(auraSpellInfo->Id)
- {
- case 23551: // Lightning Shield - Tier2: 8 pieces proc shield
- {
- // Lightning Shield (overwrite non existing triggered spell call in spell.dbc)
- triggered_spell_id = 23552;
- target = pVictim;
- break;
- }
- case 23552: // Lightning Shield - trigger shield damage
- {
- // Lightning Shield (overwrite non existing triggered spell call in spell.dbc)
- triggered_spell_id = 27635;
- target = pVictim;
- break;
- }
- }
- break;
- }
- // Mana Surge (Shaman T1 bonus)
- case 87:
- {
- if(!procSpell)
- return false;
-
- basepoints0 = procSpell->manaCost * 35/100;
- triggered_spell_id = 23571;
- target = this;
- break;
- }
- //Nature's Guardian
- case 2013:
- {
- if(GetTypeId()!=TYPEID_PLAYER)
- return false;
-
- // damage taken that reduces below 30% health
- // does NOT mean you must have been >= 30% before
- if (10*(int32(GetHealth())-int32(damage)) >= 3*GetMaxHealth())
- return false;
-
- triggered_spell_id = 31616;
-
- // need check cooldown now
- if( cooldown && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
- return false;
-
- basepoints0 = triggeredByAura->GetModifier()->m_amount * GetMaxHealth() / 100;
- target = this;
- if(pVictim && pVictim->isAlive())
- pVictim->getThreatManager().modifyThreatPercent(this,-10);
- break;
- }
- }
- }
-
- // Water Shield (we can't set cooldown for main spell - it's player casted spell
- if((auraSpellInfo->SpellFamilyFlags & 0x0000002000000000LL) && auraSpellInfo->SpellVisual==7358)
- {
- target = this;
- break;
- }
-
- // Lightning Shield
- if((auraSpellInfo->SpellFamilyFlags & 0x00000400) && auraSpellInfo->SpellVisual==37)
- {
- // overwrite non existing triggered spell call in spell.dbc
- switch(triggeredByAura->GetSpellProto()->Id)
- {
- case 324:
- triggered_spell_id = 26364; break; // Rank 1
- case 325:
- triggered_spell_id = 26365; break; // Rank 2
- case 905:
- triggered_spell_id = 26366; break; // Rank 3
- case 945:
- triggered_spell_id = 26367; break; // Rank 4
- case 8134:
- triggered_spell_id = 26369; break; // Rank 5
- case 10431:
- triggered_spell_id = 26370; break; // Rank 6
- case 10432:
- triggered_spell_id = 26363; break; // Rank 7
- case 25469:
- triggered_spell_id = 26371; break; // Rank 8
- case 25472:
- triggered_spell_id = 26372; break; // Rank 9
- default:
- sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in LShield",triggeredByAura->GetSpellProto()->Id);
- return false;
- }
-
- target = pVictim;
- break;
- }
- break;
- }
- }
-
- // standard non-dummy case
- if(!triggered_spell_id)
- {
- sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex());
- return false;
- }
-
- SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
-
- if(!triggerEntry)
- {
- sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have not existed EffectTriggered[%d]=%u, not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex(),triggered_spell_id);
- return false;
- }
-
- // not allow proc extra attack spell at extra attack
- if( m_extraAttacks && IsSpellHaveEffect(triggerEntry,SPELL_EFFECT_ADD_EXTRA_ATTACKS) )
- return false;
-
- if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
- return false;
-
- // default case
- if(!target || target!=this && !target->isAlive())
- return false;
-
- if(basepoints0)
- CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
- else
- CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura);
-
- if( cooldown && GetTypeId()==TYPEID_PLAYER )
- ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
-
- return true;
-}
-
-bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, int32 scriptId, uint32 damage, Aura *triggeredByAura, SpellEntry const *procSpell, uint32 cooldown)
-{
- if(!pVictim || !pVictim->isAlive())
- return false;
-
- Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
- ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
-
- uint32 triggered_spell_id = 0;
-
- switch(scriptId)
- {
- case 836: // Improved Blizzard (Rank 1)
- {
- if( !procSpell || procSpell->SpellVisual!=9487 )
- return false;
- triggered_spell_id = 12484;
- break;
- }
- case 988: // Improved Blizzard (Rank 2)
- {
- if( !procSpell || procSpell->SpellVisual!=9487 )
- return false;
- triggered_spell_id = 12485;
- break;
- }
- case 989: // Improved Blizzard (Rank 3)
- {
- if( !procSpell || procSpell->SpellVisual!=9487 )
- return false;
- triggered_spell_id = 12486;
- break;
- }
- case 4086: // Improved Mend Pet (Rank 1)
- case 4087: // Improved Mend Pet (Rank 2)
- {
- int32 chance = triggeredByAura->GetSpellProto()->EffectBasePoints[triggeredByAura->GetEffIndex()];
- if(!roll_chance_i(chance))
- return false;
-
- triggered_spell_id = 24406;
- break;
- }
- case 4533: // Dreamwalker Raiment 2 pieces bonus
- {
- // Chance 50%
- if (!roll_chance_i(50))
- return false;
-
- switch (pVictim->getPowerType())
- {
- case POWER_MANA: triggered_spell_id = 28722; break;
- case POWER_RAGE: triggered_spell_id = 28723; break;
- case POWER_ENERGY: triggered_spell_id = 28724; break;
- default:
- return false;
- }
- break;
- }
- case 4537: // Dreamwalker Raiment 6 pieces bonus
- triggered_spell_id = 28750; // Blessing of the Claw
- break;
- case 5497: // Improved Mana Gems (Serpent-Coil Braid)
- triggered_spell_id = 37445; // Mana Surge
- break;
- }
-
- // not processed
- if(!triggered_spell_id)
- return false;
-
- // standard non-dummy case
- SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
-
- if(!triggerEntry)
- {
- sLog.outError("Unit::HandleOverrideClassScriptAuraProc: Spell %u triggering for class script id %u",triggered_spell_id,scriptId);
- return false;
- }
-
- if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
- return false;
-
- CastSpell(pVictim, triggered_spell_id, true, castItem, triggeredByAura);
-
- if( cooldown && GetTypeId()==TYPEID_PLAYER )
- ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
-
- return true;
-}
-
-void Unit::setPowerType(Powers new_powertype)
-{
- SetByteValue(UNIT_FIELD_BYTES_0, 3, new_powertype);
-
- if(GetTypeId() == TYPEID_PLAYER)
- {
- if(((Player*)this)->GetGroup())
- ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POWER_TYPE);
- }
- else if(((Creature*)this)->isPet())
- {
- Pet *pet = ((Pet*)this);
- if(pet->isControlled())
- {
- Unit *owner = GetOwner();
- if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
- ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_POWER_TYPE);
- }
- }
-
- switch(new_powertype)
- {
- default:
- case POWER_MANA:
- break;
- case POWER_RAGE:
- SetMaxPower(POWER_RAGE,GetCreatePowers(POWER_RAGE));
- SetPower( POWER_RAGE,0);
- break;
- case POWER_FOCUS:
- SetMaxPower(POWER_FOCUS,GetCreatePowers(POWER_FOCUS));
- SetPower( POWER_FOCUS,GetCreatePowers(POWER_FOCUS));
- break;
- case POWER_ENERGY:
- SetMaxPower(POWER_ENERGY,GetCreatePowers(POWER_ENERGY));
- SetPower( POWER_ENERGY,0);
- break;
- case POWER_HAPPINESS:
- SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
- SetPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
- break;
- }
-}
-
-FactionTemplateEntry const* Unit::getFactionTemplateEntry() const
-{
- FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(getFaction());
- if(!entry)
- {
- static uint64 guid = 0; // prevent repeating spam same faction problem
-
- if(GetGUID() != guid)
- {
- if(GetTypeId() == TYPEID_PLAYER)
- sLog.outError("Player %s have invalid faction (faction template id) #%u", ((Player*)this)->GetName(), getFaction());
- else
- sLog.outError("Creature (template id: %u) have invalid faction (faction template id) #%u", ((Creature*)this)->GetCreatureInfo()->Entry, getFaction());
- guid = GetGUID();
- }
- }
- return entry;
-}
-
-bool Unit::IsHostileTo(Unit const* unit) const
-{
- // always non-hostile to self
- if(unit==this)
- return false;
-
- // always non-hostile to GM in GM mode
- if(unit->GetTypeId()==TYPEID_PLAYER && ((Player const*)unit)->isGameMaster())
- return false;
-
- // always hostile to enemy
- if(getVictim()==unit || unit->getVictim()==this)
- return true;
-
- // test pet/charm masters instead pers/charmeds
- Unit const* testerOwner = GetCharmerOrOwner();
- Unit const* targetOwner = unit->GetCharmerOrOwner();
-
- // always hostile to owner's enemy
- if(testerOwner && (testerOwner->getVictim()==unit || unit->getVictim()==testerOwner))
- return true;
-
- // always hostile to enemy owner
- if(targetOwner && (getVictim()==targetOwner || targetOwner->getVictim()==this))
- return true;
-
- // always hostile to owner of owner's enemy
- if(testerOwner && targetOwner && (testerOwner->getVictim()==targetOwner || targetOwner->getVictim()==testerOwner))
- return true;
-
- Unit const* tester = testerOwner ? testerOwner : this;
- Unit const* target = targetOwner ? targetOwner : unit;
-
- // always non-hostile to target with common owner, or to owner/pet
- if(tester==target)
- return false;
-
- // special cases (Duel, etc)
- if(tester->GetTypeId()==TYPEID_PLAYER && target->GetTypeId()==TYPEID_PLAYER)
- {
- Player const* pTester = (Player const*)tester;
- Player const* pTarget = (Player const*)target;
-
- // Duel
- if(pTester->duel && pTester->duel->opponent == pTarget && pTester->duel->startTime != 0)
- return true;
-
- // Group
- if(pTester->GetGroup() && pTester->GetGroup()==pTarget->GetGroup())
- return false;
-
- // Sanctuary
- if(pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY) && pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY))
- return false;
-
- // PvP FFA state
- if(pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP))
- return true;
-
- //= PvP states
- // Green/Blue (can't attack)
- if(pTester->GetTeam()==pTarget->GetTeam())
- return false;
-
- // Red (can attack) if true, Blue/Yellow (can't attack) in another case
- return pTester->IsPvP() && pTarget->IsPvP();
- }
-
- // faction base cases
- FactionTemplateEntry const*tester_faction = tester->getFactionTemplateEntry();
- FactionTemplateEntry const*target_faction = target->getFactionTemplateEntry();
- if(!tester_faction || !target_faction)
- return false;
-
- if(target->isAttackingPlayer() && tester->IsContestedGuard())
- return true;
-
- // PvC forced reaction and reputation case
- if(tester->GetTypeId()==TYPEID_PLAYER)
- {
- // forced reaction
- ForcedReactions::const_iterator forceItr = ((Player*)tester)->m_forcedReactions.find(target_faction->faction);
- if(forceItr!=((Player*)tester)->m_forcedReactions.end())
- return forceItr->second <= REP_HOSTILE;
-
- // if faction have reputation then hostile state for tester at 100% dependent from at_war state
- if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction))
- if(raw_target_faction->reputationListID >=0)
- if(FactionState const* factionState = ((Player*)tester)->GetFactionState(raw_target_faction))
- return (factionState->Flags & FACTION_FLAG_AT_WAR);
- }
- // CvP forced reaction and reputation case
- else if(target->GetTypeId()==TYPEID_PLAYER)
- {
- // forced reaction
- ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction);
- if(forceItr!=((Player const*)target)->m_forcedReactions.end())
- return forceItr->second <= REP_HOSTILE;
-
- // apply reputation state
- FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction);
- if(raw_tester_faction && raw_tester_faction->reputationListID >=0 )
- return ((Player const*)target)->GetReputationRank(raw_tester_faction) <= REP_HOSTILE;
- }
-
- // common faction based case (CvC,PvC,CvP)
- return tester_faction->IsHostileTo(*target_faction);
-}
-
-bool Unit::IsFriendlyTo(Unit const* unit) const
-{
- // always friendly to self
- if(unit==this)
- return true;
-
- // always friendly to GM in GM mode
- if(unit->GetTypeId()==TYPEID_PLAYER && ((Player const*)unit)->isGameMaster())
- return true;
-
- // always non-friendly to enemy
- if(getVictim()==unit || unit->getVictim()==this)
- return false;
-
- // test pet/charm masters instead pers/charmeds
- Unit const* testerOwner = GetCharmerOrOwner();
- Unit const* targetOwner = unit->GetCharmerOrOwner();
-
- // always non-friendly to owner's enemy
- if(testerOwner && (testerOwner->getVictim()==unit || unit->getVictim()==testerOwner))
- return false;
-
- // always non-friendly to enemy owner
- if(targetOwner && (getVictim()==targetOwner || targetOwner->getVictim()==this))
- return false;
-
- // always non-friendly to owner of owner's enemy
- if(testerOwner && targetOwner && (testerOwner->getVictim()==targetOwner || targetOwner->getVictim()==testerOwner))
- return false;
-
- Unit const* tester = testerOwner ? testerOwner : this;
- Unit const* target = targetOwner ? targetOwner : unit;
-
- // always friendly to target with common owner, or to owner/pet
- if(tester==target)
- return true;
-
- // special cases (Duel)
- if(tester->GetTypeId()==TYPEID_PLAYER && target->GetTypeId()==TYPEID_PLAYER)
- {
- Player const* pTester = (Player const*)tester;
- Player const* pTarget = (Player const*)target;
-
- // Duel
- if(pTester->duel && pTester->duel->opponent == target && pTester->duel->startTime != 0)
- return false;
-
- // Group
- if(pTester->GetGroup() && pTester->GetGroup()==pTarget->GetGroup())
- return true;
-
- // Sanctuary
- if(pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY) && pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY))
- return true;
-
- // PvP FFA state
- if(pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP))
- return false;
-
- //= PvP states
- // Green/Blue (non-attackable)
- if(pTester->GetTeam()==pTarget->GetTeam())
- return true;
-
- // Blue (friendly/non-attackable) if not PVP, or Yellow/Red in another case (attackable)
- return !pTarget->IsPvP();
- }
-
- // faction base cases
- FactionTemplateEntry const*tester_faction = tester->getFactionTemplateEntry();
- FactionTemplateEntry const*target_faction = target->getFactionTemplateEntry();
- if(!tester_faction || !target_faction)
- return false;
-
- if(target->isAttackingPlayer() && tester->IsContestedGuard())
- return false;
-
- // PvC forced reaction and reputation case
- if(tester->GetTypeId()==TYPEID_PLAYER)
- {
- // forced reaction
- ForcedReactions::const_iterator forceItr = ((Player const*)tester)->m_forcedReactions.find(target_faction->faction);
- if(forceItr!=((Player const*)tester)->m_forcedReactions.end())
- return forceItr->second >= REP_FRIENDLY;
-
- // if faction have reputation then friendly state for tester at 100% dependent from at_war state
- if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction))
- if(raw_target_faction->reputationListID >=0)
- if(FactionState const* FactionState = ((Player*)tester)->GetFactionState(raw_target_faction))
- return !(FactionState->Flags & FACTION_FLAG_AT_WAR);
- }
- // CvP forced reaction and reputation case
- else if(target->GetTypeId()==TYPEID_PLAYER)
- {
- // forced reaction
- ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction);
- if(forceItr!=((Player const*)target)->m_forcedReactions.end())
- return forceItr->second >= REP_FRIENDLY;
-
- // apply reputation state
- if(FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction))
- if(raw_tester_faction->reputationListID >=0 )
- return ((Player const*)target)->GetReputationRank(raw_tester_faction) >= REP_FRIENDLY;
- }
-
- // common faction based case (CvC,PvC,CvP)
- return tester_faction->IsFriendlyTo(*target_faction);
-}
-
-bool Unit::IsHostileToPlayers() const
-{
- FactionTemplateEntry const* my_faction = getFactionTemplateEntry();
- if(!my_faction)
- return false;
-
- FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction);
- if(raw_faction && raw_faction->reputationListID >=0 )
- return false;
-
- return my_faction->IsHostileToPlayers();
-}
-
-bool Unit::IsNeutralToAll() const
-{
- FactionTemplateEntry const* my_faction = getFactionTemplateEntry();
- if(!my_faction)
- return true;
-
- FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction);
- if(raw_faction && raw_faction->reputationListID >=0 )
- return false;
-
- return my_faction->IsNeutralToAll();
-}
-
-bool Unit::Attack(Unit *victim, bool meleeAttack)
-{
- if(!victim || victim == this)
- return false;
-
- // dead units can neither attack nor be attacked
- if(!isAlive() || !victim->isAlive())
- return false;
-
- // player cannot attack in mount state
- if(GetTypeId()==TYPEID_PLAYER && IsMounted())
- return false;
-
- // nobody can attack GM in GM-mode
- if(victim->GetTypeId()==TYPEID_PLAYER)
- {
- if(((Player*)victim)->isGameMaster())
- return false;
- }
- else
- {
- if(((Creature*)victim)->IsInEvadeMode())
- return false;
- }
-
- // remove SPELL_AURA_MOD_UNATTACKABLE at attack (in case non-interruptible spells stun aura applied also that not let attack)
- if(HasAuraType(SPELL_AURA_MOD_UNATTACKABLE))
- RemoveSpellsCausingAura(SPELL_AURA_MOD_UNATTACKABLE);
-
- if (m_attacking)
- {
- if (m_attacking == victim)
- {
- // switch to melee attack from ranged/magic
- if( meleeAttack && !hasUnitState(UNIT_STAT_MELEE_ATTACKING) )
- {
- addUnitState(UNIT_STAT_MELEE_ATTACKING);
- SendAttackStart(victim);
- return true;
- }
- return false;
- }
- AttackStop();
- }
-
- //Set our target
- SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID());
-
- if(meleeAttack)
- addUnitState(UNIT_STAT_MELEE_ATTACKING);
- m_attacking = victim;
- m_attacking->_addAttacker(this);
-
- if(m_attacking->GetTypeId()==TYPEID_UNIT && ((Creature*)m_attacking)->AI())
- ((Creature*)m_attacking)->AI()->AttackedBy(this);
-
- if(GetTypeId()==TYPEID_UNIT)
- {
- WorldPacket data(SMSG_AI_REACTION, 12);
- data << GetGUID();
- data << uint32(AI_REACTION_AGGRO); // Aggro sound
- ((WorldObject*)this)->SendMessageToSet(&data, true);
-
- ((Creature*)this)->CallAssistence();
- ((Creature*)this)->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ());
- }
-
- // delay offhand weapon attack to next attack time
- if(haveOffhandWeapon())
- resetAttackTimer(OFF_ATTACK);
-
- if(meleeAttack)
- SendAttackStart(victim);
-
- return true;
-}
-
-bool Unit::AttackStop()
-{
- if (!m_attacking)
- return false;
-
- Unit* victim = m_attacking;
-
- m_attacking->_removeAttacker(this);
- m_attacking = NULL;
-
- //Clear our target
- SetUInt64Value(UNIT_FIELD_TARGET, 0);
-
- clearUnitState(UNIT_STAT_MELEE_ATTACKING);
-
- InterruptSpell(CURRENT_MELEE_SPELL);
-
- if( GetTypeId()==TYPEID_UNIT )
- {
- // reset call assistance
- ((Creature*)this)->SetNoCallAssistence(false);
- }
-
- SendAttackStop(victim);
-
- return true;
-}
-
-void Unit::CombatStop(bool cast)
-{
- if(cast& IsNonMeleeSpellCasted(false))
- InterruptNonMeleeSpells(false);
-
- AttackStop();
- RemoveAllAttackers();
- if( GetTypeId()==TYPEID_PLAYER )
- ((Player*)this)->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel
- ClearInCombat();
-}
-
-void Unit::CombatStopWithPets(bool cast)
-{
- CombatStop(cast);
- if(Pet* pet = GetPet())
- pet->CombatStop(cast);
- if(Unit* charm = GetCharm())
- charm->CombatStop(cast);
- if(GetTypeId()==TYPEID_PLAYER)
- {
- GuardianPetList const& guardians = ((Player*)this)->GetGuardians();
- for(GuardianPetList::const_iterator itr = guardians.begin(); itr != guardians.end(); ++itr)
- if(Unit* guardian = Unit::GetUnit(*this,*itr))
- guardian->CombatStop(cast);
- }
-}
-
-bool Unit::isAttackingPlayer() const
-{
- if(hasUnitState(UNIT_STAT_ATTACK_PLAYER))
- return true;
-
- Pet* pet = GetPet();
- if(pet && pet->isAttackingPlayer())
- return true;
-
- Unit* charmed = GetCharm();
- if(charmed && charmed->isAttackingPlayer())
- return true;
-
- for (int8 i = 0; i < MAX_TOTEM; i++)
- {
- if(m_TotemSlot[i])
- {
- Creature *totem = ObjectAccessor::GetCreature(*this, m_TotemSlot[i]);
- if(totem && totem->isAttackingPlayer())
- return true;
- }
- }
-
- return false;
-}
-
-void Unit::RemoveAllAttackers()
-{
- while (!m_attackers.empty())
- {
- AttackerSet::iterator iter = m_attackers.begin();
- if(!(*iter)->AttackStop())
- {
- sLog.outError("WORLD: Unit has an attacker that isnt attacking it!");
- m_attackers.erase(iter);
- }
- }
-}
-
-void Unit::ModifyAuraState(AuraState flag, bool apply)
-{
- if (apply)
- {
- if (!HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)))
- {
- SetFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1));
- if(GetTypeId() == TYPEID_PLAYER)
- {
- const PlayerSpellMap& sp_list = ((Player*)this)->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 || !IsPassiveSpell(itr->first)) continue;
- if (spellInfo->CasterAuraState == flag)
- CastSpell(this, itr->first, true, NULL);
- }
- }
- }
- }
- else
- {
- if (HasFlag(UNIT_FIELD_AURASTATE,1<<(flag-1)))
- {
- RemoveFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1));
- Unit::AuraMap& tAuras = GetAuras();
- for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();)
- {
- SpellEntry const* spellProto = (*itr).second->GetSpellProto();
- if (spellProto->CasterAuraState == flag)
- {
- // exceptions (applied at state but not removed at state change)
- // Rampage
- if(spellProto->SpellIconID==2006 && spellProto->SpellFamilyName==SPELLFAMILY_WARRIOR && spellProto->SpellFamilyFlags==0x100000)
- {
- ++itr;
- continue;
- }
-
- RemoveAura(itr);
- }
- else
- ++itr;
- }
- }
- }
-}
-
-Unit *Unit::GetOwner() const
-{
- uint64 ownerid = GetOwnerGUID();
- if(!ownerid)
- return NULL;
- return ObjectAccessor::GetUnit(*this, ownerid);
-}
-
-Unit *Unit::GetCharmer() const
-{
- if(uint64 charmerid = GetCharmerGUID())
- return ObjectAccessor::GetUnit(*this, charmerid);
- return NULL;
-}
-
-Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself()
-{
- uint64 guid = GetCharmerOrOwnerGUID();
- if(IS_PLAYER_GUID(guid))
- return ObjectAccessor::GetPlayer(*this, guid);
-
- return GetTypeId()==TYPEID_PLAYER ? (Player*)this : NULL;
-}
-
-Pet* Unit::GetPet() const
-{
- if(uint64 pet_guid = GetPetGUID())
- {
- if(Pet* pet = ObjectAccessor::GetPet(pet_guid))
- return pet;
-
- sLog.outError("Unit::GetPet: Pet %u not exist.",GUID_LOPART(pet_guid));
- const_cast<Unit*>(this)->SetPet(0);
- }
-
- return NULL;
-}
-
-Unit* Unit::GetCharm() const
-{
- if(uint64 charm_guid = GetCharmGUID())
- {
- if(Unit* pet = ObjectAccessor::GetUnit(*this, charm_guid))
- return pet;
-
- sLog.outError("Unit::GetCharm: Charmed creature %u not exist.",GUID_LOPART(charm_guid));
- const_cast<Unit*>(this)->SetCharm(0);
- }
-
- return NULL;
-}
-
-void Unit::SetPet(Pet* pet)
-{
- SetUInt64Value(UNIT_FIELD_SUMMON,pet ? pet->GetGUID() : 0);
-
- // FIXME: hack, speed must be set only at follow
- if(pet)
- for(int i = 0; i < MAX_MOVE_TYPE; ++i)
- pet->SetSpeed(UnitMoveType(i),m_speed_rate[i],true);
-}
-
-void Unit::SetCharm(Unit* charmed)
-{
- SetUInt64Value(UNIT_FIELD_CHARM,charmed ? charmed->GetGUID() : 0);
-}
-
-void Unit::UnsummonAllTotems()
-{
- for (int8 i = 0; i < MAX_TOTEM; ++i)
- {
- if(!m_TotemSlot[i])
- continue;
-
- Creature *OldTotem = ObjectAccessor::GetCreature(*this, m_TotemSlot[i]);
- if (OldTotem && OldTotem->isTotem())
- ((Totem*)OldTotem)->UnSummon();
- }
-}
-
-void Unit::SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, bool critical)
-{
- // we guess size
- WorldPacket data(SMSG_SPELLHEALLOG, (8+8+4+4+1));
- data.append(pVictim->GetPackGUID());
- data.append(GetPackGUID());
- data << uint32(SpellID);
- data << uint32(Damage);
- data << uint8(critical ? 1 : 0);
- data << uint8(0); // unused in client?
- SendMessageToSet(&data, true);
-}
-
-void Unit::SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype, bool critical)
-{
- WorldPacket data(SMSG_SPELLENERGIZELOG, (8+8+4+4+4+1));
- data.append(pVictim->GetPackGUID());
- data.append(GetPackGUID());
- data << uint32(SpellID);
- data << uint32(powertype);
- data << uint32(Damage);
- //data << uint8(critical ? 1 : 0); // removed in 2.4.0
- SendMessageToSet(&data, true);
-}
-
-uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype)
-{
- if(!spellProto || !pVictim || damagetype==DIRECT_DAMAGE )
- return pdamage;
-
- int32 BonusDamage = 0;
- if( GetTypeId()==TYPEID_UNIT )
- {
- // Pets just add their bonus damage to their spell damage
- // note that their spell damage is just gain of their own auras
- if (((Creature*)this)->isPet())
- {
- BonusDamage = ((Pet*)this)->GetBonusDamage();
- }
- // For totems get damage bonus from owner (statue isn't totem in fact)
- else if (((Creature*)this)->isTotem() && ((Totem*)this)->GetTotemType()!=TOTEM_STATUE)
- {
- if(Unit* owner = GetOwner())
- return owner->SpellDamageBonus(pVictim, spellProto, pdamage, damagetype);
- }
- }
-
- // Damage Done
- uint32 CastingTime = !IsChanneledSpell(spellProto) ? GetSpellCastTime(spellProto) : GetSpellDuration(spellProto);
-
- // Taken/Done fixed damage bonus auras
- int32 DoneAdvertisedBenefit = SpellBaseDamageBonus(GetSpellSchoolMask(spellProto))+BonusDamage;
- int32 TakenAdvertisedBenefit = SpellBaseDamageBonusForVictim(GetSpellSchoolMask(spellProto), pVictim);
-
- // Damage over Time spells bonus calculation
- float DotFactor = 1.0f;
- if(damagetype == DOT)
- {
- int32 DotDuration = GetSpellDuration(spellProto);
- // 200% limit
- if(DotDuration > 0)
- {
- if(DotDuration > 30000) DotDuration = 30000;
- if(!IsChanneledSpell(spellProto)) DotFactor = DotDuration / 15000.0f;
- int x = 0;
- for(int j = 0; j < 3; j++)
- {
- if( spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && (
- spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_DAMAGE ||
- spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) )
- {
- x = j;
- break;
- }
- }
- int DotTicks = 6;
- if(spellProto->EffectAmplitude[x] != 0)
- DotTicks = DotDuration / spellProto->EffectAmplitude[x];
- if(DotTicks)
- {
- DoneAdvertisedBenefit /= DotTicks;
- TakenAdvertisedBenefit /= DotTicks;
- }
- }
- }
-
- // Taken/Done total percent damage auras
- float DoneTotalMod = 1.0f;
- float TakenTotalMod = 1.0f;
-
- // ..done
- AuraList const& mModDamagePercentDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
- for(AuraList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i)
- {
- if( ((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto)) &&
- (*i)->GetSpellProto()->EquippedItemClass == -1 &&
- // -1 == any item class (not wand then)
- (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0 )
- // 0 == any inventory type (not wand then)
- {
- DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
- }
- }
-
- uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
- AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS);
- for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
- if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
- DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
-
- // ..taken
- AuraList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
- for(AuraList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i)
- if( (*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto) )
- TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
-
- // .. taken pct: scripted (increases damage of * against targets *)
- AuraList const& mOverrideClassScript = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
- for(AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i)
- {
- switch((*i)->GetModifier()->m_miscvalue)
- {
- //Molten Fury
- case 4920: case 4919:
- if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_20_PERCENT))
- TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f; break;
- }
- }
- // .. taken pct: dummy auras
- AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
- for(AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
- {
- switch((*i)->GetSpellProto()->SpellIconID)
- {
- //Cheat Death
- case 2109:
- if( ((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto)) )
- {
- if(pVictim->GetTypeId() != TYPEID_PLAYER)
- continue;
- float mod = -((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2*4;
- if (mod < (*i)->GetModifier()->m_amount)
- mod = (*i)->GetModifier()->m_amount;
- TakenTotalMod *= (mod+100.0f)/100.0f;
- }
- break;
- //Mangle
- case 2312:
- for(int j=0;j<3;j++)
- {
- if(GetEffectMechanic(spellProto, j)==MECHANIC_BLEED)
- {
- TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f;
- break;
- }
- }
- break;
- }
- }
-
- // Distribute Damage over multiple effects, reduce by AoE
- CastingTime = GetCastingTimeForBonus( spellProto, damagetype, CastingTime );
-
- // 50% for damage and healing spells for leech spells from damage bonus and 0% from healing
- for(int j = 0; j < 3; ++j)
- {
- if( spellProto->Effect[j] == SPELL_EFFECT_HEALTH_LEECH ||
- spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH )
- {
- CastingTime /= 2;
- break;
- }
- }
-
- switch(spellProto->SpellFamilyName)
- {
- case SPELLFAMILY_MAGE:
- // Ignite - do not modify, it is (8*Rank)% damage of procing Spell
- if(spellProto->Id==12654)
- {
- return pdamage;
- }
- // Ice Lance
- else if((spellProto->SpellFamilyFlags & 0x20000LL) && spellProto->SpellIconID == 186)
- {
- CastingTime /= 3; // applied 1/3 bonuses in case generic target
- if(pVictim->isFrozen()) // and compensate this for frozen target.
- TakenTotalMod *= 3.0f;
- }
- // Pyroblast - 115% of Fire Damage, DoT - 20% of Fire Damage
- else if((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 184 )
- {
- DotFactor = damagetype == DOT ? 0.2f : 1.0f;
- CastingTime = damagetype == DOT ? 3500 : 4025;
- }
- // Fireball - 100% of Fire Damage, DoT - 0% of Fire Damage
- else if((spellProto->SpellFamilyFlags & 0x1LL) && spellProto->SpellIconID == 185)
- {
- CastingTime = 3500;
- DotFactor = damagetype == DOT ? 0.0f : 1.0f;
- }
- // Molten armor
- else if (spellProto->SpellFamilyFlags & 0x0000000800000000LL)
- {
- CastingTime = 0;
- }
- // Arcane Missiles triggered spell
- else if ((spellProto->SpellFamilyFlags & 0x200000LL) && spellProto->SpellIconID == 225)
- {
- CastingTime = 1000;
- }
- // Blizzard triggered spell
- else if ((spellProto->SpellFamilyFlags & 0x80080LL) && spellProto->SpellIconID == 285)
- {
- CastingTime = 500;
- }
- break;
- case SPELLFAMILY_WARLOCK:
- // Life Tap
- if((spellProto->SpellFamilyFlags & 0x40000LL) && spellProto->SpellIconID == 208)
- {
- CastingTime = 2800; // 80% from +shadow damage
- DoneTotalMod = 1.0f;
- TakenTotalMod = 1.0f;
- }
- // Dark Pact
- else if((spellProto->SpellFamilyFlags & 0x80000000LL) && spellProto->SpellIconID == 154 && GetPetGUID())
- {
- CastingTime = 3360; // 96% from +shadow damage
- DoneTotalMod = 1.0f;
- TakenTotalMod = 1.0f;
- }
- // Soul Fire - 115% of Fire Damage
- else if((spellProto->SpellFamilyFlags & 0x8000000000LL) && spellProto->SpellIconID == 184)
- {
- CastingTime = 4025;
- }
- // Curse of Agony - 120% of Shadow Damage
- else if((spellProto->SpellFamilyFlags & 0x0000000400LL) && spellProto->SpellIconID == 544)
- {
- DotFactor = 1.2f;
- }
- // Drain Mana - 0% of Shadow Damage
- else if((spellProto->SpellFamilyFlags & 0x10LL) && spellProto->SpellIconID == 548)
- {
- CastingTime = 0;
- }
- // Drain Soul 214.3%
- else if ((spellProto->SpellFamilyFlags & 0x4000LL) && spellProto->SpellIconID == 113 )
- {
- CastingTime = 7500;
- }
- // Hellfire
- else if ((spellProto->SpellFamilyFlags & 0x40LL) && spellProto->SpellIconID == 937)
- {
- CastingTime = damagetype == DOT ? 5000 : 500; // self damage seems to be so
- }
- // Unstable Affliction - 180%
- else if (spellProto->Id == 31117 && spellProto->SpellIconID == 232)
- {
- CastingTime = 6300;
- }
- // Corruption 93%
- else if ((spellProto->SpellFamilyFlags & 0x2LL) && spellProto->SpellIconID == 313)
- {
- DotFactor = 0.93f;
- }
- break;
- case SPELLFAMILY_PALADIN:
- // Consecration - 95% of Holy Damage
- if((spellProto->SpellFamilyFlags & 0x20LL) && spellProto->SpellIconID == 51)
- {
- DotFactor = 0.95f;
- CastingTime = 3500;
- }
- // Seal of Righteousness - 10.2%/9.8% ( based on weapon type ) of Holy Damage, multiplied by weapon speed
- else if((spellProto->SpellFamilyFlags & 0x8000000LL) && spellProto->SpellIconID == 25)
- {
- Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
- float wspeed = GetAttackTime(BASE_ATTACK)/1000.0f;
-
- if( item && item->GetProto()->InventoryType == INVTYPE_2HWEAPON)
- CastingTime = uint32(wspeed*3500*0.102f);
- else
- CastingTime = uint32(wspeed*3500*0.098f);
- }
- // Judgement of Righteousness - 73%
- else if ((spellProto->SpellFamilyFlags & 1024) && spellProto->SpellIconID == 25)
- {
- CastingTime = 2555;
- }
- // Seal of Vengeance - 17% per Fully Stacked Tick - 5 Applications
- else if ((spellProto->SpellFamilyFlags & 0x80000000000LL) && spellProto->SpellIconID == 2292)
- {
- DotFactor = 0.17f;
- CastingTime = 3500;
- }
- // Holy shield - 5% of Holy Damage
- else if ((spellProto->SpellFamilyFlags & 0x4000000000LL) && spellProto->SpellIconID == 453)
- {
- CastingTime = 175;
- }
- // Blessing of Sanctuary - 0%
- else if ((spellProto->SpellFamilyFlags & 0x10000000LL) && spellProto->SpellIconID == 29)
- {
- CastingTime = 0;
- }
- // Seal of Righteousness trigger - already computed for parent spell
- else if ( spellProto->SpellFamilyName==SPELLFAMILY_PALADIN && spellProto->SpellIconID==25 && spellProto->AttributesEx4 & 0x00800000LL )
- {
- return pdamage;
- }
- break;
- case SPELLFAMILY_SHAMAN:
- // totem attack
- if (spellProto->SpellFamilyFlags & 0x000040000000LL)
- {
- if (spellProto->SpellIconID == 33) // Fire Nova totem attack must be 21.4%(untested)
- CastingTime = 749; // ignore CastingTime and use as modifier
- else if (spellProto->SpellIconID == 680) // Searing Totem attack 8%
- CastingTime = 280; // ignore CastingTime and use as modifier
- else if (spellProto->SpellIconID == 37) // Magma totem attack must be 6.67%(untested)
- CastingTime = 234; // ignore CastingTimePenalty and use as modifier
- }
- // Lightning Shield (and proc shield from T2 8 pieces bonus ) 33% per charge
- else if( (spellProto->SpellFamilyFlags & 0x00000000400LL) || spellProto->Id == 23552)
- CastingTime = 1155; // ignore CastingTimePenalty and use as modifier
- break;
- case SPELLFAMILY_PRIEST:
- // Mana Burn - 0% of Shadow Damage
- if((spellProto->SpellFamilyFlags & 0x10LL) && spellProto->SpellIconID == 212)
- {
- CastingTime = 0;
- }
- // Mind Flay - 59% of Shadow Damage
- else if((spellProto->SpellFamilyFlags & 0x800000LL) && spellProto->SpellIconID == 548)
- {
- CastingTime = 2065;
- }
- // Holy Fire - 86.71%, DoT - 16.5%
- else if ((spellProto->SpellFamilyFlags & 0x100000LL) && spellProto->SpellIconID == 156)
- {
- DotFactor = damagetype == DOT ? 0.165f : 1.0f;
- CastingTime = damagetype == DOT ? 3500 : 3035;
- }
- // Shadowguard - 28% per charge
- else if ((spellProto->SpellFamilyFlags & 0x2000000LL) && spellProto->SpellIconID == 19)
- {
- CastingTime = 980;
- }
- // Touch of Weakeness - 10%
- else if ((spellProto->SpellFamilyFlags & 0x80000LL) && spellProto->SpellIconID == 1591)
- {
- CastingTime = 350;
- }
- // Reflective Shield (back damage) - 0% (other spells fit to check not have damage effects/auras)
- else if (spellProto->SpellFamilyFlags == 0 && spellProto->SpellIconID == 566)
- {
- CastingTime = 0;
- }
- // Holy Nova - 14%
- else if ((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 1874)
- {
- CastingTime = 500;
- }
- break;
- case SPELLFAMILY_DRUID:
- // Hurricane triggered spell
- if((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 220)
- {
- CastingTime = 500;
- }
- break;
- case SPELLFAMILY_WARRIOR:
- case SPELLFAMILY_HUNTER:
- case SPELLFAMILY_ROGUE:
- CastingTime = 0;
- break;
- default:
- break;
- }
-
- float LvlPenalty = CalculateLevelPenalty(spellProto);
-
- // Spellmod SpellDamage
- float SpellModSpellDamage = 100.0f;
-
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_SPELL_BONUS_DAMAGE,SpellModSpellDamage);
-
- SpellModSpellDamage /= 100.0f;
-
- float DoneActualBenefit = DoneAdvertisedBenefit * (CastingTime / 3500.0f) * DotFactor * SpellModSpellDamage * LvlPenalty;
- float TakenActualBenefit = TakenAdvertisedBenefit * (CastingTime / 3500.0f) * DotFactor * LvlPenalty;
-
- float tmpDamage = (float(pdamage)+DoneActualBenefit)*DoneTotalMod;
-
- // Add flat bonus from spell damage versus
- tmpDamage += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS, creatureTypeMask);
-
- // apply spellmod to Done damage
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage);
-
- tmpDamage = (tmpDamage+TakenActualBenefit)*TakenTotalMod;
-
- if( GetTypeId() == TYPEID_UNIT && !((Creature*)this)->isPet() )
- tmpDamage *= ((Creature*)this)->GetSpellDamageMod(((Creature*)this)->GetCreatureInfo()->rank);
-
- return tmpDamage > 0 ? uint32(tmpDamage) : 0;
-}
-
-int32 Unit::SpellBaseDamageBonus(SpellSchoolMask schoolMask)
-{
- int32 DoneAdvertisedBenefit = 0;
-
- // ..done
- AuraList const& mDamageDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE);
- for(AuraList::const_iterator i = mDamageDone.begin();i != mDamageDone.end(); ++i)
- if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0 &&
- (*i)->GetSpellProto()->EquippedItemClass == -1 &&
- // -1 == any item class (not wand then)
- (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0 )
- // 0 == any inventory type (not wand then)
- DoneAdvertisedBenefit += (*i)->GetModifier()->m_amount;
-
- if (GetTypeId() == TYPEID_PLAYER)
- {
- // Damage bonus from stats
- AuraList const& mDamageDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT);
- for(AuraList::const_iterator i = mDamageDoneOfStatPercent.begin();i != mDamageDoneOfStatPercent.end(); ++i)
- {
- if((*i)->GetModifier()->m_miscvalue & schoolMask)
- {
- SpellEntry const* iSpellProto = (*i)->GetSpellProto();
- uint8 eff = (*i)->GetEffIndex();
-
- // stat used dependent from next effect aura SPELL_AURA_MOD_SPELL_HEALING presence and misc value (stat index)
- Stats usedStat = STAT_INTELLECT;
- if(eff < 2 && iSpellProto->EffectApplyAuraName[eff+1]==SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT)
- usedStat = Stats(iSpellProto->EffectMiscValue[eff+1]);
-
- DoneAdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetModifier()->m_amount / 100.0f);
- }
- }
- // ... and attack power
- AuraList const& mDamageDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER);
- for(AuraList::const_iterator i =mDamageDonebyAP.begin();i != mDamageDonebyAP.end(); ++i)
- if ((*i)->GetModifier()->m_miscvalue & schoolMask)
- DoneAdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetModifier()->m_amount / 100.0f);
-
- }
- return DoneAdvertisedBenefit;
-}
-
-int32 Unit::SpellBaseDamageBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim)
-{
- uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
-
- int32 TakenAdvertisedBenefit = 0;
- // ..done (for creature type by mask) in taken
- AuraList const& mDamageDoneCreature = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE);
- for(AuraList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i)
- if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
- TakenAdvertisedBenefit += (*i)->GetModifier()->m_amount;
-
- // ..taken
- AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN);
- for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
- if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0)
- TakenAdvertisedBenefit += (*i)->GetModifier()->m_amount;
-
- return TakenAdvertisedBenefit;
-}
-
-bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType)
-{
- // not criting spell
- if((spellProto->AttributesEx2 & SPELL_ATTR_EX2_CANT_CRIT))
- return false;
-
- float crit_chance = 0.0f;
- switch(spellProto->DmgClass)
- {
- case SPELL_DAMAGE_CLASS_NONE:
- return false;
- case SPELL_DAMAGE_CLASS_MAGIC:
- {
- if (schoolMask & SPELL_SCHOOL_MASK_NORMAL)
- crit_chance = 0.0f;
- // For other schools
- else if (GetTypeId() == TYPEID_PLAYER)
- crit_chance = GetFloatValue( PLAYER_SPELL_CRIT_PERCENTAGE1 + GetFirstSchoolInMask(schoolMask));
- else
- {
- crit_chance = m_baseSpellCritChance;
- crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask);
- }
- // taken
- if (pVictim && !IsPositiveSpell(spellProto->Id))
- {
- // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE
- crit_chance += pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE, schoolMask);
- // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE
- crit_chance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
- // Modify by player victim resilience
- if (pVictim->GetTypeId() == TYPEID_PLAYER)
- crit_chance -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL);
- // scripted (increase crit chance ... against ... target by x%
- if(pVictim->isFrozen()) // Shatter
- {
- AuraList const& mOverrideClassScript = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
- for(AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i)
- {
- switch((*i)->GetModifier()->m_miscvalue)
- {
- case 849: crit_chance+= 10.0f; break; //Shatter Rank 1
- case 910: crit_chance+= 20.0f; break; //Shatter Rank 2
- case 911: crit_chance+= 30.0f; break; //Shatter Rank 3
- case 912: crit_chance+= 40.0f; break; //Shatter Rank 4
- case 913: crit_chance+= 50.0f; break; //Shatter Rank 5
- }
- }
- }
- }
- break;
- }
- case SPELL_DAMAGE_CLASS_MELEE:
- case SPELL_DAMAGE_CLASS_RANGED:
- {
- if (pVictim)
- {
- crit_chance = GetUnitCriticalChance(attackType, pVictim);
- crit_chance+= (int32(GetMaxSkillValueForLevel(pVictim)) - int32(pVictim->GetDefenseSkillValue(this))) * 0.04f;
- crit_chance+= GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask);
- }
- break;
- }
- default:
- return false;
- }
- // percent done
- // only players use intelligence for critical chance computations
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance);
-
- crit_chance = crit_chance > 0.0f ? crit_chance : 0.0f;
- if (roll_chance_f(crit_chance))
- return true;
- return false;
-}
-
-uint32 Unit::SpellCriticalBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim)
-{
- // Calculate critical bonus
- int32 crit_bonus;
- switch(spellProto->DmgClass)
- {
- case SPELL_DAMAGE_CLASS_MELEE: // for melee based spells is 100%
- case SPELL_DAMAGE_CLASS_RANGED:
- // TODO: write here full calculation for melee/ranged spells
- crit_bonus = damage;
- break;
- default:
- crit_bonus = damage / 2; // for spells is 50%
- break;
- }
-
- // adds additional damage to crit_bonus (from talents)
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
-
- if(pVictim)
- {
- uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
- crit_bonus = int32(crit_bonus * GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, creatureTypeMask));
- }
-
- if(crit_bonus > 0)
- damage += crit_bonus;
-
- return damage;
-}
-
-uint32 Unit::SpellHealingBonus(SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, Unit *pVictim)
-{
- // For totems get healing bonus from owner (statue isn't totem in fact)
- if( GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem() && ((Totem*)this)->GetTotemType()!=TOTEM_STATUE)
- if(Unit* owner = GetOwner())
- return owner->SpellHealingBonus(spellProto, healamount, damagetype, pVictim);
-
- // Healing Done
-
- // These Spells are doing fixed amount of healing (TODO found less hack-like check)
- if(spellProto->Id == 15290 || spellProto->Id == 39373 || spellProto->Id == 33778 || spellProto->Id == 379 || spellProto->Id == 38395)
- return healamount;
-
-
- int32 AdvertisedBenefit = SpellBaseHealingBonus(GetSpellSchoolMask(spellProto));
- uint32 CastingTime = GetSpellCastTime(spellProto);
-
- // Healing Taken
- AdvertisedBenefit += SpellBaseHealingBonusForVictim(GetSpellSchoolMask(spellProto), pVictim);
-
- // Blessing of Light dummy effects healing taken from Holy Light and Flash of Light
- if (spellProto->SpellFamilyName == SPELLFAMILY_PALADIN && (spellProto->SpellFamilyFlags & 0x00000000C0000000LL))
- {
- AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
- for(AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
- {
- if((*i)->GetSpellProto()->SpellVisual == 9180)
- {
- // Flash of Light
- if ((spellProto->SpellFamilyFlags & 0x0000000040000000LL) && (*i)->GetEffIndex() == 1)
- AdvertisedBenefit += (*i)->GetModifier()->m_amount;
- // Holy Light
- else if ((spellProto->SpellFamilyFlags & 0x0000000080000000LL) && (*i)->GetEffIndex() == 0)
- AdvertisedBenefit += (*i)->GetModifier()->m_amount;
- }
- }
- }
-
- float ActualBenefit = 0.0f;
-
- if (AdvertisedBenefit != 0)
- {
- // Healing over Time spells
- float DotFactor = 1.0f;
- if(damagetype == DOT)
- {
- int32 DotDuration = GetSpellDuration(spellProto);
- if(DotDuration > 0)
- {
- // 200% limit
- if(DotDuration > 30000) DotDuration = 30000;
- if(!IsChanneledSpell(spellProto)) DotFactor = DotDuration / 15000.0f;
- int x = 0;
- for(int j = 0; j < 3; j++)
- {
- if( spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && (
- spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_HEAL ||
- spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) )
- {
- x = j;
- break;
- }
- }
- int DotTicks = 6;
- if(spellProto->EffectAmplitude[x] != 0)
- DotTicks = DotDuration / spellProto->EffectAmplitude[x];
- if(DotTicks)
- AdvertisedBenefit /= DotTicks;
- }
- }
-
- // distribute healing to all effects, reduce AoE damage
- CastingTime = GetCastingTimeForBonus( spellProto, damagetype, CastingTime );
-
- // 0% bonus for damage and healing spells for leech spells from healing bonus
- for(int j = 0; j < 3; ++j)
- {
- if( spellProto->Effect[j] == SPELL_EFFECT_HEALTH_LEECH ||
- spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH )
- {
- CastingTime = 0;
- break;
- }
- }
-
- // Exception
- switch (spellProto->SpellFamilyName)
- {
- case SPELLFAMILY_SHAMAN:
- // Healing stream from totem (add 6% per tick from hill bonus owner)
- if (spellProto->SpellFamilyFlags & 0x000000002000LL)
- CastingTime = 210;
- // Earth Shield 30% per charge
- else if (spellProto->SpellFamilyFlags & 0x40000000000LL)
- CastingTime = 1050;
- break;
- case SPELLFAMILY_DRUID:
- // Lifebloom
- if (spellProto->SpellFamilyFlags & 0x1000000000LL)
- {
- CastingTime = damagetype == DOT ? 3500 : 1200;
- DotFactor = damagetype == DOT ? 0.519f : 1.0f;
- }
- // Tranquility triggered spell
- else if (spellProto->SpellFamilyFlags & 0x80LL)
- CastingTime = 667;
- // Rejuvenation
- else if (spellProto->SpellFamilyFlags & 0x10LL)
- DotFactor = 0.845f;
- // Regrowth
- else if (spellProto->SpellFamilyFlags & 0x40LL)
- {
- DotFactor = damagetype == DOT ? 0.705f : 1.0f;
- CastingTime = damagetype == DOT ? 3500 : 1010;
- }
- break;
- case SPELLFAMILY_PRIEST:
- // Holy Nova - 14%
- if ((spellProto->SpellFamilyFlags & 0x8000000LL) && spellProto->SpellIconID == 1874)
- CastingTime = 500;
- break;
- case SPELLFAMILY_PALADIN:
- // Seal and Judgement of Light
- if ( spellProto->SpellFamilyFlags & 0x100040000LL )
- CastingTime = 0;
- break;
- case SPELLFAMILY_WARRIOR:
- case SPELLFAMILY_ROGUE:
- case SPELLFAMILY_HUNTER:
- CastingTime = 0;
- break;
- }
-
- float LvlPenalty = CalculateLevelPenalty(spellProto);
-
- // Spellmod SpellDamage
- float SpellModSpellDamage = 100.0f;
-
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_SPELL_BONUS_DAMAGE,SpellModSpellDamage);
-
- SpellModSpellDamage /= 100.0f;
-
- ActualBenefit = (float)AdvertisedBenefit * ((float)CastingTime / 3500.0f) * DotFactor * SpellModSpellDamage * LvlPenalty;
- }
-
- // use float as more appropriate for negative values and percent applying
- float heal = healamount + ActualBenefit;
-
- // TODO: check for ALL/SPELLS type
- // Healing done percent
- AuraList const& mHealingDonePct = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT);
- for(AuraList::const_iterator i = mHealingDonePct.begin();i != mHealingDonePct.end(); ++i)
- heal *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f;
-
- // apply spellmod to Done amount
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal);
-
- // Healing Wave cast
- if (spellProto->SpellFamilyName == SPELLFAMILY_SHAMAN && spellProto->SpellFamilyFlags & 0x0000000000000040LL)
- {
- // Search for Healing Way on Victim (stack up to 3 time)
- int32 pctMod = 0;
- Unit::AuraList const& auraDummy = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
- for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr!=auraDummy.end(); ++itr)
- if((*itr)->GetId() == 29203)
- pctMod += (*itr)->GetModifier()->m_amount;
- // Apply bonus
- if (pctMod)
- heal = heal * (100 + pctMod) / 100;
- }
-
- // Healing taken percent
- float minval = pVictim->GetMaxNegativeAuraModifier(SPELL_AURA_MOD_HEALING_PCT);
- if(minval)
- heal *= (100.0f + minval) / 100.0f;
-
- float maxval = pVictim->GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HEALING_PCT);
- if(maxval)
- heal *= (100.0f + maxval) / 100.0f;
-
- if (heal < 0) heal = 0;
-
- return uint32(heal);
-}
-
-int32 Unit::SpellBaseHealingBonus(SpellSchoolMask schoolMask)
-{
- int32 AdvertisedBenefit = 0;
-
- AuraList const& mHealingDone = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE);
- for(AuraList::const_iterator i = mHealingDone.begin();i != mHealingDone.end(); ++i)
- if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0)
- AdvertisedBenefit += (*i)->GetModifier()->m_amount;
-
- // Healing bonus of spirit, intellect and strength
- if (GetTypeId() == TYPEID_PLAYER)
- {
- // Healing bonus from stats
- AuraList const& mHealingDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT);
- for(AuraList::const_iterator i = mHealingDoneOfStatPercent.begin();i != mHealingDoneOfStatPercent.end(); ++i)
- {
- // stat used dependent from misc value (stat index)
- Stats usedStat = Stats((*i)->GetSpellProto()->EffectMiscValue[(*i)->GetEffIndex()]);
- AdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetModifier()->m_amount / 100.0f);
- }
-
- // ... and attack power
- AuraList const& mHealingDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER);
- for(AuraList::const_iterator i = mHealingDonebyAP.begin();i != mHealingDonebyAP.end(); ++i)
- if ((*i)->GetModifier()->m_miscvalue & schoolMask)
- AdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetModifier()->m_amount / 100.0f);
- }
- return AdvertisedBenefit;
-}
-
-int32 Unit::SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim)
-{
- int32 AdvertisedBenefit = 0;
- AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_HEALING);
- for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
- if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0)
- AdvertisedBenefit += (*i)->GetModifier()->m_amount;
- return AdvertisedBenefit;
-}
-
-bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask, bool useCharges)
-{
- // no charges dependent checks
- SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
- for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
- if(itr->type & shoolMask)
- return true;
-
- // charges dependent checks
- SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
- for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
- {
- if(itr->type & shoolMask)
- {
- if(useCharges)
- {
- AuraList const& auraDamageImmunity = GetAurasByType(SPELL_AURA_DAMAGE_IMMUNITY);
- for(AuraList::const_iterator auraItr = auraDamageImmunity.begin(); auraItr != auraDamageImmunity.end(); ++auraItr)
- {
- if((*auraItr)->GetId()==itr->spellId)
- {
- if((*auraItr)->m_procCharges > 0)
- {
- --(*auraItr)->m_procCharges;
- if((*auraItr)->m_procCharges==0)
- RemoveAurasDueToSpell(itr->spellId);
- }
- break;
- }
- }
- }
- return true;
- }
- }
-
- return false;
-}
-
-bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
-{
- if (!spellInfo)
- return false;
-
- // no charges first
-
- //FIX ME this hack: don't get feared if stunned
- if (spellInfo->Mechanic == MECHANIC_FEAR )
- {
- if ( hasUnitState(UNIT_STAT_STUNNED) )
- return true;
- }
-
- // not have spells with charges currently
- SpellImmuneList const& dispelList = m_spellImmune[IMMUNITY_DISPEL];
- for(SpellImmuneList::const_iterator itr = dispelList.begin(); itr != dispelList.end(); ++itr)
- if(itr->type == spellInfo->Dispel)
- return true;
-
- if( !(spellInfo->AttributesEx & SPELL_ATTR_EX_UNAFFECTED_BY_SCHOOL_IMMUNE)) // unaffected by school immunity
- {
- // not have spells with charges currently
- SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
- for(SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
- if( !(IsPositiveSpell(itr->spellId) && IsPositiveSpell(spellInfo->Id)) &&
- (itr->type & GetSpellSchoolMask(spellInfo)) )
- return true;
- }
-
- // charges dependent checks
-
- SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
- for(SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
- {
- if(itr->type == spellInfo->Mechanic)
- {
- if(useCharges)
- {
- AuraList const& auraMechImmunity = GetAurasByType(SPELL_AURA_MECHANIC_IMMUNITY);
- for(AuraList::const_iterator auraItr = auraMechImmunity.begin(); auraItr != auraMechImmunity.end(); ++auraItr)
- {
- if((*auraItr)->GetId()==itr->spellId)
- {
- if((*auraItr)->m_procCharges > 0)
- {
- --(*auraItr)->m_procCharges;
- if((*auraItr)->m_procCharges==0)
- RemoveAurasDueToSpell(itr->spellId);
- }
- break;
- }
- }
- }
- return true;
- }
- }
-
- return false;
-}
-
-bool Unit::IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const
-{
- //If m_immuneToEffect type contain this effect type, IMMUNE effect.
- SpellImmuneList const& effectList = m_spellImmune[IMMUNITY_EFFECT];
- for (SpellImmuneList::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr)
- if(itr->type == effect)
- return true;
-
- SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
- for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
- if(itr->type == mechanic)
- return true;
-
- return false;
-}
-
-bool Unit::IsDamageToThreatSpell(SpellEntry const * spellInfo) const
-{
- if(!spellInfo)
- return false;
-
- uint32 family = spellInfo->SpellFamilyName;
- uint64 flags = spellInfo->SpellFamilyFlags;
-
- if((family == 5 && flags == 256) || //Searing Pain
- (family == 6 && flags == 8192) || //Mind Blast
- (family == 11 && flags == 1048576)) //Earth Shock
- return true;
-
- return false;
-}
-
-void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attType, SpellEntry const *spellProto)
-{
- if(!pVictim)
- return;
-
- if(*pdamage == 0)
- return;
-
- uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
-
- // Taken/Done fixed damage bonus auras
- int32 DoneFlatBenefit = 0;
- int32 TakenFlatBenefit = 0;
-
- // ..done (for creature type by mask) in taken
- AuraList const& mDamageDoneCreature = this->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE);
- for(AuraList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i)
- if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
- DoneFlatBenefit += (*i)->GetModifier()->m_amount;
-
- // ..done
- // SPELL_AURA_MOD_DAMAGE_DONE included in weapon damage
-
- // ..done (base at attack power for marked target and base at attack power for creature type)
- int32 APbonus = 0;
- if(attType == RANGED_ATTACK)
- {
- APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS);
-
- // ..done (base at attack power and creature type)
- AuraList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS);
- for(AuraList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i)
- if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
- APbonus += (*i)->GetModifier()->m_amount;
- }
- else
- {
- APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS);
-
- // ..done (base at attack power and creature type)
- AuraList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS);
- for(AuraList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i)
- if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
- APbonus += (*i)->GetModifier()->m_amount;
- }
-
- if (APbonus!=0) // Can be negative
- {
- bool normalized = false;
- if(spellProto)
- {
- for (uint8 i = 0; i<3;i++)
- {
- if (spellProto->Effect[i] == SPELL_EFFECT_NORMALIZED_WEAPON_DMG)
- {
- normalized = true;
- break;
- }
- }
- }
-
- DoneFlatBenefit += int32(APbonus/14.0f * GetAPMultiplier(attType,normalized));
- }
-
- // ..taken
- AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN);
- for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
- if((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
- TakenFlatBenefit += (*i)->GetModifier()->m_amount;
-
- if(attType!=RANGED_ATTACK)
- TakenFlatBenefit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN);
- else
- TakenFlatBenefit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN);
-
- // Done/Taken total percent damage auras
- float DoneTotalMod = 1;
- float TakenTotalMod = 1;
-
- // ..done
- // SPELL_AURA_MOD_DAMAGE_PERCENT_DONE included in weapon damage
- // SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT included in weapon damage
-
- AuraList const& mDamageDoneVersus = this->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS);
- for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
- if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
- DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
-
- // ..taken
- AuraList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
- for(AuraList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i)
- if((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
- TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
-
- // .. taken pct: dummy auras
- AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
- for(AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
- {
- switch((*i)->GetSpellProto()->SpellIconID)
- {
- //Cheat Death
- case 2109:
- if((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
- {
- if(pVictim->GetTypeId() != TYPEID_PLAYER)
- continue;
- float mod = ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE)*(-8.0f);
- if (mod < (*i)->GetModifier()->m_amount)
- mod = (*i)->GetModifier()->m_amount;
- TakenTotalMod *= (mod+100.0f)/100.0f;
- }
- break;
- //Mangle
- case 2312:
- if(spellProto==NULL)
- break;
- // Should increase Shred (initial Damage of Lacerate and Rake handled in Spell::EffectSchoolDMG)
- if(spellProto->SpellFamilyName==SPELLFAMILY_DRUID && (spellProto->SpellFamilyFlags==0x00008000LL))
- TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f;
- break;
- }
- }
-
- // .. taken pct: class scripts
- AuraList const& mclassScritAuras = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
- for(AuraList::const_iterator i = mclassScritAuras.begin(); i != mclassScritAuras.end(); ++i)
- {
- switch((*i)->GetMiscValue())
- {
- case 6427: case 6428: // Dirty Deeds
- if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT))
- {
- Aura* eff0 = GetAura((*i)->GetId(),0);
- if(!eff0 || (*i)->GetEffIndex()!=1)
- {
- sLog.outError("Spell structure of DD (%u) changed.",(*i)->GetId());
- continue;
- }
-
- // effect 0 have expected value but in negative state
- TakenTotalMod *= (-eff0->GetModifier()->m_amount+100.0f)/100.0f;
- }
- break;
- }
- }
-
- if(attType != RANGED_ATTACK)
- {
- AuraList const& mModMeleeDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT);
- for(AuraList::const_iterator i = mModMeleeDamageTakenPercent.begin(); i != mModMeleeDamageTakenPercent.end(); ++i)
- TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
- }
- else
- {
- AuraList const& mModRangedDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT);
- for(AuraList::const_iterator i = mModRangedDamageTakenPercent.begin(); i != mModRangedDamageTakenPercent.end(); ++i)
- TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
- }
-
- float tmpDamage = float(int32(*pdamage) + DoneFlatBenefit) * DoneTotalMod;
-
- // apply spellmod to Done damage
- if(spellProto)
- {
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, tmpDamage);
- }
-
- tmpDamage = (tmpDamage + TakenFlatBenefit)*TakenTotalMod;
-
- // bonus result can be negative
- *pdamage = tmpDamage > 0 ? uint32(tmpDamage) : 0;
-}
-
-void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply)
-{
- if (apply)
- {
- for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(), next; itr != m_spellImmune[op].end(); itr = next)
- {
- next = itr; ++next;
- if(itr->type == type)
- {
- m_spellImmune[op].erase(itr);
- next = m_spellImmune[op].begin();
- }
- }
- SpellImmune Immune;
- Immune.spellId = spellId;
- Immune.type = type;
- m_spellImmune[op].push_back(Immune);
- }
- else
- {
- for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(); itr != m_spellImmune[op].end(); ++itr)
- {
- if(itr->spellId == spellId)
- {
- m_spellImmune[op].erase(itr);
- break;
- }
- }
- }
-
-}
-
-void Unit::ApplySpellDispelImmunity(const SpellEntry * spellProto, DispelType type, bool apply)
-{
- ApplySpellImmune(spellProto->Id,IMMUNITY_DISPEL, type, apply);
-
- if (apply && spellProto->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
- RemoveAurasWithDispelType(type);
-}
-
-float Unit::GetWeaponProcChance() const
-{
- // normalized proc chance for weapon attack speed
- // (odd formulae...)
- if(isAttackReady(BASE_ATTACK))
- return (GetAttackTime(BASE_ATTACK) * 1.8f / 1000.0f);
- else if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
- return (GetAttackTime(OFF_ATTACK) * 1.6f / 1000.0f);
- return 0;
-}
-
-float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM) const
-{
- // proc per minute chance calculation
- if (PPM <= 0) return 0.0f;
- uint32 result = uint32((WeaponSpeed * PPM) / 600.0f); // result is chance in percents (probability = Speed_in_sec * (PPM / 60))
- return result;
-}
-
-void Unit::Mount(uint32 mount)
-{
- if(!mount)
- return;
-
- RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOUNTING);
-
- SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, mount);
-
- SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT );
-
- // unsummon pet
- if(GetTypeId() == TYPEID_PLAYER)
- {
- Pet* pet = GetPet();
- if(pet)
- {
- if(pet->isControlled())
- {
- ((Player*)this)->SetTemporaryUnsummonedPetNumber(pet->GetCharmInfo()->GetPetNumber());
- ((Player*)this)->SetOldPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL));
- }
-
- ((Player*)this)->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT);
- }
- else
- ((Player*)this)->SetTemporaryUnsummonedPetNumber(0);
- }
-}
-
-void Unit::Unmount()
-{
- if(!IsMounted())
- return;
-
- RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_MOUNTED);
-
- SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
- RemoveFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT );
-
- // only resummon old pet if the player is already added to a map
- // this prevents adding a pet to a not created map which would otherwise cause a crash
- // (it could probably happen when logging in after a previous crash)
- if(GetTypeId() == TYPEID_PLAYER && IsInWorld() && ((Player*)this)->GetTemporaryUnsummonedPetNumber() && isAlive())
- {
- Pet* NewPet = new Pet;
- if(!NewPet->LoadPetFromDB(this, 0, ((Player*)this)->GetTemporaryUnsummonedPetNumber(), true))
- delete NewPet;
-
- ((Player*)this)->SetTemporaryUnsummonedPetNumber(0);
- }
-}
-
-void Unit::SetInCombatWith(Unit* enemy)
-{
- Unit* eOwner = enemy->GetCharmerOrOwnerOrSelf();
- if(eOwner->IsPvP())
- {
- SetInCombatState(true);
- return;
- }
-
- //check for duel
- if(eOwner->GetTypeId() == TYPEID_PLAYER && ((Player*)eOwner)->duel)
- {
- Unit const* myOwner = GetCharmerOrOwnerOrSelf();
- if(((Player const*)eOwner)->duel->opponent == myOwner)
- {
- SetInCombatState(true);
- return;
- }
- }
- SetInCombatState(false);
-}
-
-void Unit::SetInCombatState(bool PvP)
-{
- // only alive units can be in combat
- if(!isAlive())
- return;
-
- if(PvP)
- m_CombatTimer = 5000;
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
-
- if(isCharmed() || (GetTypeId()!=TYPEID_PLAYER && ((Creature*)this)->isPet()))
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
-}
-
-void Unit::ClearInCombat()
-{
- m_CombatTimer = 0;
- RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
-
- if(isCharmed() || (GetTypeId()!=TYPEID_PLAYER && ((Creature*)this)->isPet()))
- RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
-
- // Player's state will be cleared in Player::UpdateContestedPvP
- if(GetTypeId()!=TYPEID_PLAYER)
- clearUnitState(UNIT_STAT_ATTACK_PLAYER);
-}
-
-bool Unit::isTargetableForAttack() const
-{
- if (GetTypeId()==TYPEID_PLAYER && ((Player *)this)->isGameMaster())
- return false;
-
- if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE))
- return false;
-
- return isAlive() && !hasUnitState(UNIT_STAT_DIED)&& !isInFlight() /*&& !isStealth()*/;
-}
-
-int32 Unit::ModifyHealth(int32 dVal)
-{
- int32 gain = 0;
-
- if(dVal==0)
- return 0;
-
- int32 curHealth = (int32)GetHealth();
-
- int32 val = dVal + curHealth;
- if(val <= 0)
- {
- SetHealth(0);
- return -curHealth;
- }
-
- int32 maxHealth = (int32)GetMaxHealth();
-
- if(val < maxHealth)
- {
- SetHealth(val);
- gain = val - curHealth;
- }
- else if(curHealth != maxHealth)
- {
- SetHealth(maxHealth);
- gain = maxHealth - curHealth;
- }
-
- return gain;
-}
-
-int32 Unit::ModifyPower(Powers power, int32 dVal)
-{
- int32 gain = 0;
-
- if(dVal==0)
- return 0;
-
- int32 curPower = (int32)GetPower(power);
-
- int32 val = dVal + curPower;
- if(val <= 0)
- {
- SetPower(power,0);
- return -curPower;
- }
-
- int32 maxPower = (int32)GetMaxPower(power);
-
- if(val < maxPower)
- {
- SetPower(power,val);
- gain = val - curPower;
- }
- else if(curPower != maxPower)
- {
- SetPower(power,maxPower);
- gain = maxPower - curPower;
- }
-
- return gain;
-}
-
-bool Unit::isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList) const
-{
- if(!u)
- return false;
-
- // Always can see self
- if (u==this)
- return true;
-
- // player visible for other player if not logout and at same transport
- // including case when player is out of world
- bool at_same_transport =
- GetTypeId() == TYPEID_PLAYER && u->GetTypeId()==TYPEID_PLAYER &&
- !((Player*)this)->GetSession()->PlayerLogout() && !((Player*)u)->GetSession()->PlayerLogout() &&
- !((Player*)this)->GetSession()->PlayerLoading() && !((Player*)u)->GetSession()->PlayerLoading() &&
- ((Player*)this)->GetTransport() && ((Player*)this)->GetTransport() == ((Player*)u)->GetTransport();
-
- // not in world
- if(!at_same_transport && (!IsInWorld() || !u->IsInWorld()))
- return false;
-
- // forbidden to seen (at GM respawn command)
- if(m_Visibility==VISIBILITY_RESPAWN)
- return false;
-
- // always seen by owner
- if(GetCharmerOrOwnerGUID()==u->GetGUID())
- return true;
-
- // Grid dead/alive checks
- if( u->GetTypeId()==TYPEID_PLAYER)
- {
- // non visible at grid for any stealth state
- if(!IsVisibleInGridForPlayer((Player *)u))
- return false;
-
- // if player is dead then he can't detect anyone in anycases
- if(!u->isAlive())
- detect = false;
- }
- else
- {
- // all dead creatures/players not visible for any creatures
- if(!u->isAlive() || !isAlive())
- return false;
- }
-
- // different visible distance checks
- if(u->isInFlight()) // what see player in flight
- {
- // use object grey distance for all (only see objects any way)
- if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceInFlight()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f)))
- return false;
- }
- else if(!isAlive()) // distance for show body
- {
- if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f)))
- return false;
- }
- else if(GetTypeId()==TYPEID_PLAYER) // distance for show player
- {
- if(u->GetTypeId()==TYPEID_PLAYER)
- {
- // Players far than max visible distance for player or not in our map are not visible too
- if (!at_same_transport && !IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
- return false;
- }
- else
- {
- // Units far than max visible distance for creature or not in our map are not visible too
- if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForCreature()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
- return false;
- }
- }
- else if(GetCharmerOrOwnerGUID()) // distance for show pet/charmed
- {
- // Pet/charmed far than max visible distance for player or not in our map are not visible too
- if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
- return false;
- }
- else // distance for show creature
- {
- // Units far than max visible distance for creature or not in our map are not visible too
- if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForCreature()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
- return false;
- }
-
- // Visible units, always are visible for all units, except for units under invisibility
- if (m_Visibility == VISIBILITY_ON && u->m_invisibilityMask==0)
- return true;
-
- // GMs see any players, not higher GMs and all units
- if (u->GetTypeId() == TYPEID_PLAYER && ((Player *)u)->isGameMaster())
- {
- if(GetTypeId() == TYPEID_PLAYER)
- return ((Player *)this)->GetSession()->GetSecurity() <= ((Player *)u)->GetSession()->GetSecurity();
- else
- return true;
- }
-
- // non faction visibility non-breakable for non-GMs
- if (m_Visibility == VISIBILITY_OFF)
- return false;
-
- // raw invisibility
- bool invisible = (m_invisibilityMask != 0 || u->m_invisibilityMask !=0);
-
- // detectable invisibility case
- if( invisible && (
- // Invisible units, always are visible for units under same invisibility type
- (m_invisibilityMask & u->m_invisibilityMask)!=0 ||
- // Invisible units, always are visible for unit that can detect this invisibility (have appropriate level for detect)
- u->canDetectInvisibilityOf(this) ||
- // Units that can detect invisibility always are visible for units that can be detected
- canDetectInvisibilityOf(u) ))
- {
- invisible = false;
- }
-
- // special cases for always overwrite invisibility/stealth
- if(invisible || m_Visibility == VISIBILITY_GROUP_STEALTH)
- {
- // non-hostile case
- if (!u->IsHostileTo(this))
- {
- // player see other player with stealth/invisibility only if he in same group or raid or same team (raid/team case dependent from conf setting)
- if(GetTypeId()==TYPEID_PLAYER && u->GetTypeId()==TYPEID_PLAYER)
- {
- if(((Player*)this)->IsGroupVisibleFor(((Player*)u)))
- return true;
-
- // else apply same rules as for hostile case (detecting check for stealth)
- }
- }
- // hostile case
- else
- {
- // Hunter mark functionality
- AuraList const& auras = GetAurasByType(SPELL_AURA_MOD_STALKED);
- for(AuraList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
- if((*iter)->GetCasterGUID()==u->GetGUID())
- return true;
-
- // else apply detecting check for stealth
- }
-
- // none other cases for detect invisibility, so invisible
- if(invisible)
- return false;
-
- // else apply stealth detecting check
- }
-
- // unit got in stealth in this moment and must ignore old detected state
- if (m_Visibility == VISIBILITY_GROUP_NO_DETECT)
- return false;
-
- // GM invisibility checks early, invisibility if any detectable, so if not stealth then visible
- if (m_Visibility != VISIBILITY_GROUP_STEALTH)
- return true;
-
- // NOW ONLY STEALTH CASE
-
- // stealth and detected and visible for some seconds
- if (u->GetTypeId() == TYPEID_PLAYER && ((Player*)u)->m_DetectInvTimer > 300 && ((Player*)u)->HaveAtClient(this))
- return true;
-
- //if in non-detect mode then invisible for unit
- if (!detect)
- return false;
-
- // Special cases
-
- // If is attacked then stealth is lost, some creature can use stealth too
- if( !getAttackers().empty() )
- return true;
-
- // If there is collision rogue is seen regardless of level difference
- // TODO: check sizes in DB
- float distance = GetDistance(u);
- if (distance < 0.24f)
- return true;
-
- //If a mob or player is stunned he will not be able to detect stealth
- if (u->hasUnitState(UNIT_STAT_STUNNED) && (u != this))
- return false;
-
- // Creature can detect target only in aggro radius
- if(u->GetTypeId() != TYPEID_PLAYER)
- {
- //Always invisible from back and out of aggro range
- bool isInFront = u->isInFront(this,((Creature const*)u)->GetAttackDistance(this));
- if(!isInFront)
- return false;
- }
- else
- {
- //Always invisible from back
- bool isInFront = u->isInFront(this,(GetTypeId()==TYPEID_PLAYER || GetCharmerOrOwnerGUID()) ? World::GetMaxVisibleDistanceForPlayer() : World::GetMaxVisibleDistanceForCreature());
- if(!isInFront)
- return false;
- }
-
- // if doesn't have stealth detection (Shadow Sight), then check how stealthy the unit is, otherwise just check los
- if(!u->HasAuraType(SPELL_AURA_DETECT_STEALTH))
- {
- //Calculation if target is in front
-
- //Visible distance based on stealth value (stealth rank 4 300MOD, 10.5 - 3 = 7.5)
- float visibleDistance = 10.5f - (GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH)/100.0f);
-
- //Visible distance is modified by
- //-Level Diff (every level diff = 1.0f in visible distance)
- visibleDistance += int32(u->getLevelForTarget(this)) - int32(this->getLevelForTarget(u));
-
- //This allows to check talent tree and will add addition stealth dependent on used points)
- int32 stealthMod = GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH_LEVEL);
- if(stealthMod < 0)
- stealthMod = 0;
-
- //-Stealth Mod(positive like Master of Deception) and Stealth Detection(negative like paranoia)
- //based on wowwiki every 5 mod we have 1 more level diff in calculation
- visibleDistance += (int32(u->GetTotalAuraModifier(SPELL_AURA_MOD_DETECT)) - stealthMod)/5.0f;
-
- if(distance > visibleDistance)
- return false;
- }
-
- // Now check is target visible with LoS
- float ox,oy,oz;
- u->GetPosition(ox,oy,oz);
- return IsWithinLOS(ox,oy,oz);
-}
-
-void Unit::SetVisibility(UnitVisibility x)
-{
- m_Visibility = x;
-
- if(IsInWorld())
- {
- Map *m = MapManager::Instance().GetMap(GetMapId(), this);
-
- if(GetTypeId()==TYPEID_PLAYER)
- m->PlayerRelocation((Player*)this,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
- else
- m->CreatureRelocation((Creature*)this,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
- }
-}
-
-bool Unit::canDetectInvisibilityOf(Unit const* u) const
-{
- if(uint32 mask = (m_detectInvisibilityMask & u->m_invisibilityMask))
- {
- for(uint32 i = 0; i < 10; ++i)
- {
- if(((1 << i) & mask)==0)
- continue;
-
- // find invisibility level
- uint32 invLevel = 0;
- Unit::AuraList const& iAuras = u->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY);
- for(Unit::AuraList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr)
- if(((*itr)->GetModifier()->m_miscvalue)==i && invLevel < (*itr)->GetModifier()->m_amount)
- invLevel = (*itr)->GetModifier()->m_amount;
-
- // find invisibility detect level
- uint32 detectLevel = 0;
- Unit::AuraList const& dAuras = GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION);
- for(Unit::AuraList::const_iterator itr = dAuras.begin(); itr != dAuras.end(); ++itr)
- if(((*itr)->GetModifier()->m_miscvalue)==i && detectLevel < (*itr)->GetModifier()->m_amount)
- detectLevel = (*itr)->GetModifier()->m_amount;
-
- if(i==6 && GetTypeId()==TYPEID_PLAYER) // special drunk detection case
- {
- detectLevel = ((Player*)this)->GetDrunkValue();
- }
-
- if(invLevel <= detectLevel)
- return true;
- }
- }
-
- return false;
-}
-
-void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
-{
- int32 main_speed_mod = 0;
- float stack_bonus = 1.0f;
- float non_stack_bonus = 1.0f;
-
- switch(mtype)
- {
- case MOVE_WALK:
- return;
- case MOVE_RUN:
- {
- if (IsMounted()) // Use on mount auras
- {
- main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED);
- stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS);
- non_stack_bonus = (100.0f + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK))/100.0f;
- }
- else
- {
- main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_SPEED);
- stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_SPEED_ALWAYS);
- non_stack_bonus = (100.0f + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_SPEED_NOT_STACK))/100.0f;
- }
- break;
- }
- case MOVE_WALKBACK:
- return;
- case MOVE_SWIM:
- {
- main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_SWIM_SPEED);
- break;
- }
- case MOVE_SWIMBACK:
- return;
- case MOVE_FLY:
- {
- if (IsMounted()) // Use on mount auras
- main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED);
- else // Use not mount (shapeshift for example) auras (should stack)
- main_speed_mod = GetTotalAuraModifier(SPELL_AURA_MOD_SPEED_FLIGHT);
- stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS);
- non_stack_bonus = (100.0 + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK))/100.0f;
- break;
- }
- case MOVE_FLYBACK:
- return;
- default:
- sLog.outError("Unit::UpdateSpeed: Unsupported move type (%d)", mtype);
- return;
- }
-
- float bonus = non_stack_bonus > stack_bonus ? non_stack_bonus : stack_bonus;
- // now we ready for speed calculation
- float speed = main_speed_mod ? bonus*(100.0f + main_speed_mod)/100.0f : bonus;
-
- switch(mtype)
- {
- case MOVE_RUN:
- case MOVE_SWIM:
- case MOVE_FLY:
- {
- // Normalize speed by 191 aura SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED if need
- // TODO: possible affect only on MOVE_RUN
- if(int32 normalization = GetMaxPositiveAuraModifier(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED))
- {
- // Use speed from aura
- float max_speed = normalization / baseMoveSpeed[mtype];
- if (speed > max_speed)
- speed = max_speed;
- }
- break;
- }
- default:
- break;
- }
-
- // Apply strongest slow aura mod to speed
- int32 slow = GetMaxNegativeAuraModifier(SPELL_AURA_MOD_DECREASE_SPEED);
- if (slow)
- speed *=(100.0f + slow)/100.0f;
- SetSpeed(mtype, speed, forced);
-}
-
-float Unit::GetSpeed( UnitMoveType mtype ) const
-{
- return m_speed_rate[mtype]*baseMoveSpeed[mtype];
-}
-
-void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
-{
- if (rate < 0)
- rate = 0.0f;
-
- // Update speed only on change
- if (m_speed_rate[mtype] == rate)
- return;
-
- m_speed_rate[mtype] = rate;
-
- propagateSpeedChange();
-
- // Send speed change packet only for player
- if (GetTypeId()!=TYPEID_PLAYER)
- return;
-
- WorldPacket data;
- if(!forced)
- {
- switch(mtype)
- {
- case MOVE_WALK:
- data.Initialize(MSG_MOVE_SET_WALK_SPEED, 8+4+1+4+4+4+4+4+4+4);
- break;
- case MOVE_RUN:
- data.Initialize(MSG_MOVE_SET_RUN_SPEED, 8+4+1+4+4+4+4+4+4+4);
- break;
- case MOVE_WALKBACK:
- data.Initialize(MSG_MOVE_SET_RUN_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4);
- break;
- case MOVE_SWIM:
- data.Initialize(MSG_MOVE_SET_SWIM_SPEED, 8+4+1+4+4+4+4+4+4+4);
- break;
- case MOVE_SWIMBACK:
- data.Initialize(MSG_MOVE_SET_SWIM_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4);
- break;
- case MOVE_TURN:
- data.Initialize(MSG_MOVE_SET_TURN_RATE, 8+4+1+4+4+4+4+4+4+4);
- break;
- case MOVE_FLY:
- data.Initialize(MSG_MOVE_SET_FLIGHT_SPEED, 8+4+1+4+4+4+4+4+4+4);
- break;
- case MOVE_FLYBACK:
- data.Initialize(MSG_MOVE_SET_FLIGHT_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4);
- break;
- default:
- sLog.outError("Unit::SetSpeed: Unsupported move type (%d), data not sent to client.",mtype);
- return;
- }
-
- data.append(GetPackGUID());
- data << uint32(0); //movement flags
- data << uint8(0); //unk
- data << uint32(getMSTime());
- data << float(GetPositionX());
- data << float(GetPositionY());
- data << float(GetPositionZ());
- data << float(GetOrientation());
- data << uint32(0); //flag unk
- data << float(GetSpeed(mtype));
- SendMessageToSet( &data, true );
- }
- else
- {
- // register forced speed changes for WorldSession::HandleForceSpeedChangeAck
- // and do it only for real sent packets and use run for run/mounted as client expected
- ++((Player*)this)->m_forced_speed_changes[mtype];
- switch(mtype)
- {
- case MOVE_WALK:
- data.Initialize(SMSG_FORCE_WALK_SPEED_CHANGE, 16);
- break;
- case MOVE_RUN:
- data.Initialize(SMSG_FORCE_RUN_SPEED_CHANGE, 17);
- break;
- case MOVE_WALKBACK:
- data.Initialize(SMSG_FORCE_RUN_BACK_SPEED_CHANGE, 16);
- break;
- case MOVE_SWIM:
- data.Initialize(SMSG_FORCE_SWIM_SPEED_CHANGE, 16);
- break;
- case MOVE_SWIMBACK:
- data.Initialize(SMSG_FORCE_SWIM_BACK_SPEED_CHANGE, 16);
- break;
- case MOVE_TURN:
- data.Initialize(SMSG_FORCE_TURN_RATE_CHANGE, 16);
- break;
- case MOVE_FLY:
- data.Initialize(SMSG_FORCE_FLIGHT_SPEED_CHANGE, 16);
- break;
- case MOVE_FLYBACK:
- data.Initialize(SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, 16);
- break;
- default:
- sLog.outError("Unit::SetSpeed: Unsupported move type (%d), data not sent to client.",mtype);
- return;
- }
- data.append(GetPackGUID());
- data << (uint32)0;
- if (mtype == MOVE_RUN)
- data << uint8(0); // new 2.1.0
- data << float(GetSpeed(mtype));
- SendMessageToSet( &data, true );
- }
- if(Pet* pet = GetPet())
- pet->SetSpeed(MOVE_RUN, m_speed_rate[mtype],forced);
-}
-
-void Unit::SetHover(bool on)
-{
- if(on)
- CastSpell(this,11010,true);
- else
- RemoveAurasDueToSpell(11010);
-}
-
-void Unit::setDeathState(DeathState s)
-{
- if (s != ALIVE && s!= JUST_ALIVED)
- {
- CombatStop();
- DeleteThreatList();
- ClearComboPointHolders(); // any combo points pointed to unit lost at it death
-
- if(IsNonMeleeSpellCasted(false))
- InterruptNonMeleeSpells(false);
- }
-
- if (s == JUST_DIED)
- {
- RemoveAllAurasOnDeath();
- UnsummonAllTotems();
-
- ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false);
- ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false);
- // remove aurastates allowing special moves
- ClearAllReactives();
- ClearDiminishings();
- }
- else if(s == JUST_ALIVED)
- {
- RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); // clear skinnable for creature and player (at battleground)
- }
-
- if (m_deathState != ALIVE && s == ALIVE)
- {
- //_ApplyAllAuraMods();
- }
- m_deathState = s;
-}
-
-/*########################################
-######## ########
-######## AGGRO SYSTEM ########
-######## ########
-########################################*/
-bool Unit::CanHaveThreatList() const
-{
- // only creatures can have threat list
- if( GetTypeId() != TYPEID_UNIT )
- return false;
-
- // only alive units can have threat list
- if( !isAlive() )
- return false;
-
- // pets and totems can not have threat list
- if( ((Creature*)this)->isPet() || ((Creature*)this)->isTotem() )
- return false;
-
- return true;
-}
-
-//======================================================================
-
-float Unit::ApplyTotalThreatModifier(float threat, SpellSchoolMask schoolMask)
-{
- if(!HasAuraType(SPELL_AURA_MOD_THREAT))
- return threat;
-
- SpellSchools school = GetFirstSchoolInMask(schoolMask);
-
- return threat * m_threatModifier[school];
-}
-
-//======================================================================
-
-void Unit::AddThreat(Unit* pVictim, float threat, SpellSchoolMask schoolMask, SpellEntry const *threatSpell)
-{
- // Only mobs can manage threat lists
- if(CanHaveThreatList())
- m_ThreatManager.addThreat(pVictim, threat, schoolMask, threatSpell);
-}
-
-//======================================================================
-
-void Unit::DeleteThreatList()
-{
- m_ThreatManager.clearReferences();
-}
-
-//======================================================================
-
-void Unit::TauntApply(Unit* taunter)
-{
- assert(GetTypeId()== TYPEID_UNIT);
-
- if(!taunter || (taunter->GetTypeId() == TYPEID_PLAYER && ((Player*)taunter)->isGameMaster()))
- return;
-
- if(!CanHaveThreatList())
- return;
-
- Unit *target = getVictim();
- if(target && target == taunter)
- return;
-
- SetInFront(taunter);
- if (((Creature*)this)->AI())
- ((Creature*)this)->AI()->AttackStart(taunter);
-
- m_ThreatManager.tauntApply(taunter);
-}
-
-//======================================================================
-
-void Unit::TauntFadeOut(Unit *taunter)
-{
- assert(GetTypeId()== TYPEID_UNIT);
-
- if(!taunter || (taunter->GetTypeId() == TYPEID_PLAYER && ((Player*)taunter)->isGameMaster()))
- return;
-
- if(!CanHaveThreatList())
- return;
-
- Unit *target = getVictim();
- if(!target || target != taunter)
- return;
-
- if(m_ThreatManager.isThreatListEmpty())
- {
- if(((Creature*)this)->AI())
- ((Creature*)this)->AI()->EnterEvadeMode();
- return;
- }
-
- m_ThreatManager.tauntFadeOut(taunter);
- target = m_ThreatManager.getHostilTarget();
-
- if (target && target != taunter)
- {
- SetInFront(target);
- if (((Creature*)this)->AI())
- ((Creature*)this)->AI()->AttackStart(target);
- }
-}
-
-//======================================================================
-
-bool Unit::SelectHostilTarget()
-{
- //function provides main threat functionality
- //next-victim-selection algorithm and evade mode are called
- //threat list sorting etc.
-
- assert(GetTypeId()== TYPEID_UNIT);
- Unit* target = NULL;
-
- //This function only useful once AI has been initilazied
- if (!((Creature*)this)->AI())
- return false;
-
- if(!m_ThreatManager.isThreatListEmpty())
- {
- if(!HasAuraType(SPELL_AURA_MOD_TAUNT))
- {
- target = m_ThreatManager.getHostilTarget();
- }
- }
-
- if(target)
- {
- if(!hasUnitState(UNIT_STAT_STUNNED))
- SetInFront(target);
- ((Creature*)this)->AI()->AttackStart(target);
- return true;
- }
-
- // no target but something prevent go to evade mode
- if( !isInCombat() || HasAuraType(SPELL_AURA_MOD_TAUNT) )
- return false;
-
- // last case when creature don't must go to evade mode:
- // it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list
- // for example at owner command to pet attack some far away creature
- // Note: creature not have targeted movement generator but have attacker in this case
- if( GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE )
- {
- for(AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr)
- {
- if( (*itr)->IsInMap(this) && (*itr)->isTargetableForAttack() && (*itr)->isInAccessablePlaceFor((Creature*)this) )
- return false;
- }
- }
-
- // enter in evade mode in other case
- ((Creature*)this)->AI()->EnterEvadeMode();
-
- return false;
-}
-
-//======================================================================
-//======================================================================
-//======================================================================
-
-int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_index, int32 effBasePoints, Unit const* target)
-{
- Player* unitPlayer = (GetTypeId() == TYPEID_PLAYER) ? (Player*)this : NULL;
-
- uint8 comboPoints = unitPlayer ? unitPlayer->GetComboPoints() : 0;
-
- int32 level = int32(getLevel()) - int32(spellProto->spellLevel);
- if (level > spellProto->maxLevel && spellProto->maxLevel > 0)
- level = spellProto->maxLevel;
-
- float basePointsPerLevel = spellProto->EffectRealPointsPerLevel[effect_index];
- float randomPointsPerLevel = spellProto->EffectDicePerLevel[effect_index];
- int32 basePoints = int32(effBasePoints + level * basePointsPerLevel);
- int32 randomPoints = int32(spellProto->EffectDieSides[effect_index] + level * randomPointsPerLevel);
- float comboDamage = spellProto->EffectPointsPerComboPoint[effect_index];
-
- // prevent random generator from getting confused by spells casted with Unit::CastCustomSpell
- int32 randvalue = spellProto->EffectBaseDice[effect_index] >= randomPoints ? spellProto->EffectBaseDice[effect_index]:irand(spellProto->EffectBaseDice[effect_index], randomPoints);
- int32 value = basePoints + randvalue;
- //random damage
- if(comboDamage != 0 && unitPlayer && target && (target->GetGUID() == unitPlayer->GetComboTarget()))
- value += (int32)(comboDamage * comboPoints);
-
- if(Player* modOwner = GetSpellModOwner())
- {
- modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_ALL_EFFECTS, value);
- switch(effect_index)
- {
- case 0:
- modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_EFFECT1, value);
- break;
- case 1:
- modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_EFFECT2, value);
- break;
- case 2:
- modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_EFFECT3, value);
- break;
- }
- }
-
- if(spellProto->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION && spellProto->spellLevel &&
- spellProto->Effect[effect_index] != SPELL_EFFECT_WEAPON_PERCENT_DAMAGE &&
- spellProto->Effect[effect_index] != SPELL_EFFECT_KNOCK_BACK)
- value = int32(value*0.25f*exp(getLevel()*(70-spellProto->spellLevel)/1000.0f));
-
- return value;
-}
-
-int32 Unit::CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_index, Unit const* target)
-{
- Player* unitPlayer = (GetTypeId() == TYPEID_PLAYER) ? (Player*)this : NULL;
-
- uint8 comboPoints = unitPlayer ? unitPlayer->GetComboPoints() : 0;
-
- int32 minduration = GetSpellDuration(spellProto);
- int32 maxduration = GetSpellMaxDuration(spellProto);
-
- int32 duration;
-
- if( minduration != -1 && minduration != maxduration )
- duration = minduration + int32((maxduration - minduration) * comboPoints / 5);
- else
- duration = minduration;
-
- if (duration > 0)
- {
- int32 mechanic = GetEffectMechanic(spellProto, effect_index);
- // Find total mod value (negative bonus)
- int32 durationMod_always = target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD, mechanic);
- // Find max mod (negative bonus)
- int32 durationMod_not_stack = target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK, mechanic);
-
- int32 durationMod = 0;
- // Select strongest negative mod
- if (durationMod_always > durationMod_not_stack)
- durationMod = durationMod_not_stack;
- else
- durationMod = durationMod_always;
-
- if (durationMod != 0)
- duration = int32(int64(duration) * (100+durationMod) /100);
-
- if (duration < 0) duration = 0;
- }
-
- return duration;
-}
-
-DiminishingLevels Unit::GetDiminishing(DiminishingGroup group)
-{
- for(Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
- {
- if(i->DRGroup != group)
- continue;
-
- if(!i->hitCount)
- return DIMINISHING_LEVEL_1;
-
- if(!i->hitTime)
- return DIMINISHING_LEVEL_1;
-
- // If last spell was casted more than 15 seconds ago - reset the count.
- if(i->stack==0 && getMSTimeDiff(i->hitTime,getMSTime()) > 15000)
- {
- i->hitCount = DIMINISHING_LEVEL_1;
- return DIMINISHING_LEVEL_1;
- }
- // or else increase the count.
- else
- {
- return DiminishingLevels(i->hitCount);
- }
- }
- return DIMINISHING_LEVEL_1;
-}
-
-void Unit::IncrDiminishing(DiminishingGroup group)
-{
- // Checking for existing in the table
- bool IsExist = false;
- for(Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
- {
- if(i->DRGroup != group)
- continue;
-
- IsExist = true;
- if(i->hitCount < DIMINISHING_LEVEL_IMMUNE)
- i->hitCount += 1;
-
- break;
- }
-
- if(!IsExist)
- m_Diminishing.push_back(DiminishingReturn(group,getMSTime(),DIMINISHING_LEVEL_2));
-}
-
-void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster,DiminishingLevels Level)
-{
- if(duration == -1 || group == DIMINISHING_NONE || caster->IsFriendlyTo(this) )
- return;
-
- // Duration of crowd control abilities on pvp target is limited by 10 sec. (2.2.0)
- if(duration > 10000 && IsDiminishingReturnsGroupDurationLimited(group))
- {
- // test pet/charm masters instead pets/charmeds
- Unit const* targetOwner = GetCharmerOrOwner();
- Unit const* casterOwner = caster->GetCharmerOrOwner();
-
- Unit const* target = targetOwner ? targetOwner : this;
- Unit const* source = casterOwner ? casterOwner : caster;
-
- if(target->GetTypeId() == TYPEID_PLAYER && source->GetTypeId() == TYPEID_PLAYER)
- duration = 10000;
- }
-
- float mod = 1.0f;
-
- // Some diminishings applies to mobs too (for example, Stun)
- if((GetDiminishingReturnsGroupType(group) == DRTYPE_PLAYER && GetTypeId() == TYPEID_PLAYER) || GetDiminishingReturnsGroupType(group) == DRTYPE_ALL)
- {
- DiminishingLevels diminish = Level;
- switch(diminish)
- {
- case DIMINISHING_LEVEL_1: break;
- case DIMINISHING_LEVEL_2: mod = 0.5f; break;
- case DIMINISHING_LEVEL_3: mod = 0.25f; break;
- case DIMINISHING_LEVEL_IMMUNE: mod = 0.0f;break;
- default: break;
- }
- }
-
- duration = int32(duration * mod);
-}
-
-void Unit::ApplyDiminishingAura( DiminishingGroup group, bool apply )
-{
- // Checking for existing in the table
- for(Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
- {
- if(i->DRGroup != group)
- continue;
-
- i->hitTime = getMSTime();
-
- if(apply)
- i->stack += 1;
- else if(i->stack)
- i->stack -= 1;
-
- break;
- }
-}
-
-Unit* Unit::GetUnit(WorldObject& object, uint64 guid)
-{
- return ObjectAccessor::GetUnit(object,guid);
-}
-
-bool Unit::isVisibleForInState( Player const* u, bool inVisibleList ) const
-{
- return isVisibleForOrDetect(u,false,inVisibleList);
-}
-
-uint32 Unit::GetCreatureType() const
-{
- if(GetTypeId() == TYPEID_PLAYER)
- {
- SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(((Player*)this)->m_form);
- if(ssEntry && ssEntry->creatureType > 0)
- return ssEntry->creatureType;
- else
- return CREATURE_TYPE_HUMANOID;
- }
- else
- return ((Creature*)this)->GetCreatureInfo()->type;
-}
-
-/*#######################################
-######## ########
-######## STAT SYSTEM ########
-######## ########
-#######################################*/
-
-bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply)
-{
- if(unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END)
- {
- sLog.outError("ERROR in HandleStatModifier(): nonexisted UnitMods or wrong UnitModifierType!");
- return false;
- }
-
- float val = 1.0f;
-
- switch(modifierType)
- {
- case BASE_VALUE:
- case TOTAL_VALUE:
- m_auraModifiersGroup[unitMod][modifierType] += apply ? amount : -amount;
- break;
- case BASE_PCT:
- case TOTAL_PCT:
- if(amount <= -100.0f) //small hack-fix for -100% modifiers
- amount = -200.0f;
-
- val = (100.0f + amount) / 100.0f;
- m_auraModifiersGroup[unitMod][modifierType] *= apply ? val : (1.0f/val);
- break;
-
- default:
- break;
- }
-
- if(!CanModifyStats())
- return false;
-
- switch(unitMod)
- {
- case UNIT_MOD_STAT_STRENGTH:
- case UNIT_MOD_STAT_AGILITY:
- case UNIT_MOD_STAT_STAMINA:
- case UNIT_MOD_STAT_INTELLECT:
- case UNIT_MOD_STAT_SPIRIT: UpdateStats(GetStatByAuraGroup(unitMod)); break;
-
- case UNIT_MOD_ARMOR: UpdateArmor(); break;
- case UNIT_MOD_HEALTH: UpdateMaxHealth(); break;
-
- case UNIT_MOD_MANA:
- case UNIT_MOD_RAGE:
- case UNIT_MOD_FOCUS:
- case UNIT_MOD_ENERGY:
- case UNIT_MOD_HAPPINESS: UpdateMaxPower(GetPowerTypeByAuraGroup(unitMod)); break;
-
- case UNIT_MOD_RESISTANCE_HOLY:
- case UNIT_MOD_RESISTANCE_FIRE:
- case UNIT_MOD_RESISTANCE_NATURE:
- case UNIT_MOD_RESISTANCE_FROST:
- case UNIT_MOD_RESISTANCE_SHADOW:
- case UNIT_MOD_RESISTANCE_ARCANE: UpdateResistances(GetSpellSchoolByAuraGroup(unitMod)); break;
-
- case UNIT_MOD_ATTACK_POWER: UpdateAttackPowerAndDamage(); break;
- case UNIT_MOD_ATTACK_POWER_RANGED: UpdateAttackPowerAndDamage(true); break;
-
- case UNIT_MOD_DAMAGE_MAINHAND: UpdateDamagePhysical(BASE_ATTACK); break;
- case UNIT_MOD_DAMAGE_OFFHAND: UpdateDamagePhysical(OFF_ATTACK); break;
- case UNIT_MOD_DAMAGE_RANGED: UpdateDamagePhysical(RANGED_ATTACK); break;
-
- default:
- break;
- }
-
- return true;
-}
-
-float Unit::GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const
-{
- if( unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END)
- {
- sLog.outError("ERROR: trial to access nonexisted modifier value from UnitMods!");
- return 0.0f;
- }
-
- if(modifierType == TOTAL_PCT && m_auraModifiersGroup[unitMod][modifierType] <= 0.0f)
- return 0.0f;
-
- return m_auraModifiersGroup[unitMod][modifierType];
-}
-
-float Unit::GetTotalStatValue(Stats stat) const
-{
- UnitMods unitMod = UnitMods(UNIT_MOD_STAT_START + stat);
-
- if(m_auraModifiersGroup[unitMod][TOTAL_PCT] <= 0.0f)
- return 0.0f;
-
- // value = ((base_value * base_pct) + total_value) * total_pct
- float value = m_auraModifiersGroup[unitMod][BASE_VALUE] + GetCreateStat(stat);
- value *= m_auraModifiersGroup[unitMod][BASE_PCT];
- value += m_auraModifiersGroup[unitMod][TOTAL_VALUE];
- value *= m_auraModifiersGroup[unitMod][TOTAL_PCT];
-
- return value;
-}
-
-float Unit::GetTotalAuraModValue(UnitMods unitMod) const
-{
- if(unitMod >= UNIT_MOD_END)
- {
- sLog.outError("ERROR: trial to access nonexisted UnitMods in GetTotalAuraModValue()!");
- return 0.0f;
- }
-
- if(m_auraModifiersGroup[unitMod][TOTAL_PCT] <= 0.0f)
- return 0.0f;
-
- float value = m_auraModifiersGroup[unitMod][BASE_VALUE];
- value *= m_auraModifiersGroup[unitMod][BASE_PCT];
- value += m_auraModifiersGroup[unitMod][TOTAL_VALUE];
- value *= m_auraModifiersGroup[unitMod][TOTAL_PCT];
-
- return value;
-}
-
-SpellSchools Unit::GetSpellSchoolByAuraGroup(UnitMods unitMod) const
-{
- SpellSchools school = SPELL_SCHOOL_NORMAL;
-
- switch(unitMod)
- {
- case UNIT_MOD_RESISTANCE_HOLY: school = SPELL_SCHOOL_HOLY; break;
- case UNIT_MOD_RESISTANCE_FIRE: school = SPELL_SCHOOL_FIRE; break;
- case UNIT_MOD_RESISTANCE_NATURE: school = SPELL_SCHOOL_NATURE; break;
- case UNIT_MOD_RESISTANCE_FROST: school = SPELL_SCHOOL_FROST; break;
- case UNIT_MOD_RESISTANCE_SHADOW: school = SPELL_SCHOOL_SHADOW; break;
- case UNIT_MOD_RESISTANCE_ARCANE: school = SPELL_SCHOOL_ARCANE; break;
-
- default:
- break;
- }
-
- return school;
-}
-
-Stats Unit::GetStatByAuraGroup(UnitMods unitMod) const
-{
- Stats stat = STAT_STRENGTH;
-
- switch(unitMod)
- {
- case UNIT_MOD_STAT_STRENGTH: stat = STAT_STRENGTH; break;
- case UNIT_MOD_STAT_AGILITY: stat = STAT_AGILITY; break;
- case UNIT_MOD_STAT_STAMINA: stat = STAT_STAMINA; break;
- case UNIT_MOD_STAT_INTELLECT: stat = STAT_INTELLECT; break;
- case UNIT_MOD_STAT_SPIRIT: stat = STAT_SPIRIT; break;
-
- default:
- break;
- }
-
- return stat;
-}
-
-Powers Unit::GetPowerTypeByAuraGroup(UnitMods unitMod) const
-{
- Powers power = POWER_MANA;
-
- switch(unitMod)
- {
- case UNIT_MOD_MANA: power = POWER_MANA; break;
- case UNIT_MOD_RAGE: power = POWER_RAGE; break;
- case UNIT_MOD_FOCUS: power = POWER_FOCUS; break;
- case UNIT_MOD_ENERGY: power = POWER_ENERGY; break;
- case UNIT_MOD_HAPPINESS: power = POWER_HAPPINESS; break;
-
- default:
- break;
- }
-
- return power;
-}
-
-float Unit::GetTotalAttackPowerValue(WeaponAttackType attType) const
-{
- UnitMods unitMod = (attType == RANGED_ATTACK) ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
-
- float val = GetTotalAuraModValue(unitMod);
- if(val < 0.0f)
- val = 0.0f;
-
- return val;
-}
-
-float Unit::GetWeaponDamageRange(WeaponAttackType attType ,WeaponDamageRange type) const
-{
- if (attType == OFF_ATTACK && !haveOffhandWeapon())
- return 0.0f;
-
- return m_weaponDamage[attType][type];
-}
-
-void Unit::SetLevel(uint32 lvl)
-{
- SetUInt32Value(UNIT_FIELD_LEVEL, lvl);
-
- // group update
- if ((GetTypeId() == TYPEID_PLAYER) && ((Player*)this)->GetGroup())
- ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_LEVEL);
-}
-
-void Unit::SetHealth(uint32 val)
-{
- uint32 maxHealth = GetMaxHealth();
- if(maxHealth < val)
- val = maxHealth;
-
- SetUInt32Value(UNIT_FIELD_HEALTH, val);
-
- // group update
- if(GetTypeId() == TYPEID_PLAYER)
- {
- if(((Player*)this)->GetGroup())
- ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_HP);
- }
- else if(((Creature*)this)->isPet())
- {
- Pet *pet = ((Pet*)this);
- if(pet->isControlled())
- {
- Unit *owner = GetOwner();
- if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
- ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_CUR_HP);
- }
- }
-}
-
-void Unit::SetMaxHealth(uint32 val)
-{
- uint32 health = GetHealth();
- SetUInt32Value(UNIT_FIELD_MAXHEALTH, val);
-
- // group update
- if(GetTypeId() == TYPEID_PLAYER)
- {
- if(((Player*)this)->GetGroup())
- ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_HP);
- }
- else if(((Creature*)this)->isPet())
- {
- Pet *pet = ((Pet*)this);
- if(pet->isControlled())
- {
- Unit *owner = GetOwner();
- if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
- ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_HP);
- }
- }
-
- if(val < health)
- SetHealth(val);
-}
-
-void Unit::SetPower(Powers power, uint32 val)
-{
- uint32 maxPower = GetMaxPower(power);
- if(maxPower < val)
- val = maxPower;
-
- SetStatInt32Value(UNIT_FIELD_POWER1 + power, val);
-
- // group update
- if(GetTypeId() == TYPEID_PLAYER)
- {
- if(((Player*)this)->GetGroup())
- ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_POWER);
- }
- else if(((Creature*)this)->isPet())
- {
- Pet *pet = ((Pet*)this);
- if(pet->isControlled())
- {
- Unit *owner = GetOwner();
- if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
- ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_CUR_POWER);
- }
-
- // Update the pet's character sheet with happiness damage bonus
- if(pet->getPetType() == HUNTER_PET && power == POWER_HAPPINESS)
- {
- pet->UpdateDamagePhysical(BASE_ATTACK);
- }
- }
-}
-
-void Unit::SetMaxPower(Powers power, uint32 val)
-{
- uint32 cur_power = GetPower(power);
- SetStatInt32Value(UNIT_FIELD_MAXPOWER1 + power, val);
-
- // group update
- if(GetTypeId() == TYPEID_PLAYER)
- {
- if(((Player*)this)->GetGroup())
- ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_POWER);
- }
- else if(((Creature*)this)->isPet())
- {
- Pet *pet = ((Pet*)this);
- if(pet->isControlled())
- {
- Unit *owner = GetOwner();
- if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
- ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_POWER);
- }
- }
-
- if(val < cur_power)
- SetPower(power, val);
-}
-
-void Unit::ApplyPowerMod(Powers power, uint32 val, bool apply)
-{
- ApplyModUInt32Value(UNIT_FIELD_POWER1+power, val, apply);
-
- // group update
- if(GetTypeId() == TYPEID_PLAYER)
- {
- if(((Player*)this)->GetGroup())
- ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_POWER);
- }
- else if(((Creature*)this)->isPet())
- {
- Pet *pet = ((Pet*)this);
- if(pet->isControlled())
- {
- Unit *owner = GetOwner();
- if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
- ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_CUR_POWER);
- }
- }
-}
-
-void Unit::ApplyMaxPowerMod(Powers power, uint32 val, bool apply)
-{
- ApplyModUInt32Value(UNIT_FIELD_MAXPOWER1+power, val, apply);
-
- // group update
- if(GetTypeId() == TYPEID_PLAYER)
- {
- if(((Player*)this)->GetGroup())
- ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_POWER);
- }
- else if(((Creature*)this)->isPet())
- {
- Pet *pet = ((Pet*)this);
- if(pet->isControlled())
- {
- Unit *owner = GetOwner();
- if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
- ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_POWER);
- }
- }
-}
-
-void Unit::ApplyAuraProcTriggerDamage( Aura* aura, bool apply )
-{
- AuraList& tAuraProcTriggerDamage = m_modAuras[SPELL_AURA_PROC_TRIGGER_DAMAGE];
- if(apply)
- tAuraProcTriggerDamage.push_back(aura);
- else
- tAuraProcTriggerDamage.remove(aura);
-}
-
-uint32 Unit::GetCreatePowers( Powers power ) const
-{
- // POWER_FOCUS and POWER_HAPPINESS only have hunter pet
- switch(power)
- {
- case POWER_MANA: return GetCreateMana();
- case POWER_RAGE: return 1000;
- case POWER_FOCUS: return (GetTypeId()==TYPEID_PLAYER || !((Creature const*)this)->isPet() || ((Pet const*)this)->getPetType()!=HUNTER_PET ? 0 : 100);
- case POWER_ENERGY: return 100;
- case POWER_HAPPINESS: return (GetTypeId()==TYPEID_PLAYER || !((Creature const*)this)->isPet() || ((Pet const*)this)->getPetType()!=HUNTER_PET ? 0 : 1050000);
- }
-
- return 0;
-}
-
-void Unit::AddToWorld()
-{
- Object::AddToWorld();
-}
-
-void Unit::RemoveFromWorld()
-{
- // cleanup
- if(IsInWorld())
- {
- RemoveNotOwnSingleTargetAuras();
- }
-
- Object::RemoveFromWorld();
-}
-
-void Unit::CleanupsBeforeDelete()
-{
- if(m_uint32Values) // only for fully created object
- {
- InterruptNonMeleeSpells(true);
- m_Events.KillAllEvents();
- CombatStop();
- ClearComboPointHolders();
- DeleteThreatList();
- getHostilRefManager().setOnlineOfflineState(false);
- RemoveAllAuras();
- RemoveAllGameObjects();
- RemoveAllDynObjects();
- GetMotionMaster()->Clear(false); // remove different non-standard movement generators.
- }
- RemoveFromWorld();
-}
-
-CharmInfo* Unit::InitCharmInfo(Unit *charm)
-{
- if(!m_charmInfo)
- m_charmInfo = new CharmInfo(charm);
- return m_charmInfo;
-}
-
-CharmInfo::CharmInfo(Unit* unit)
-: m_unit(unit), m_CommandState(COMMAND_FOLLOW), m_ReactSate(REACT_PASSIVE), m_petnumber(0)
-{
- for(int i =0; i<4; ++i)
- {
- m_charmspells[i].spellId = 0;
- m_charmspells[i].active = ACT_DISABLED;
- }
-}
-
-void CharmInfo::InitPetActionBar()
-{
- // the first 3 SpellOrActions are attack, follow and stay
- for(uint32 i = 0; i < 3; i++)
- {
- PetActionBar[i].Type = ACT_COMMAND;
- PetActionBar[i].SpellOrAction = COMMAND_ATTACK - i;
-
- PetActionBar[i + 7].Type = ACT_REACTION;
- PetActionBar[i + 7].SpellOrAction = COMMAND_ATTACK - i;
- }
- for(uint32 i=0; i < 4; i++)
- {
- PetActionBar[i + 3].Type = ACT_DISABLED;
- PetActionBar[i + 3].SpellOrAction = 0;
- }
-}
-
-void CharmInfo::InitEmptyActionBar()
-{
- for(uint32 x = 1; x < 10; ++x)
- {
- PetActionBar[x].Type = ACT_CAST;
- PetActionBar[x].SpellOrAction = 0;
- }
- PetActionBar[0].Type = ACT_COMMAND;
- PetActionBar[0].SpellOrAction = COMMAND_ATTACK;
-}
-
-void CharmInfo::InitPossessCreateSpells()
-{
- if(m_unit->GetTypeId() == TYPEID_PLAYER)
- return;
-
- InitEmptyActionBar(); //charm action bar
-
- for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x)
- {
- if (IsPassiveSpell(((Creature*)m_unit)->m_spells[x]))
- m_unit->CastSpell(m_unit, ((Creature*)m_unit)->m_spells[x], true);
- else
- AddSpellToAB(0, ((Creature*)m_unit)->m_spells[x], ACT_CAST);
- }
-}
-
-void CharmInfo::InitCharmCreateSpells()
-{
- if(m_unit->GetTypeId() == TYPEID_PLAYER) //charmed players don't have spells
- {
- InitEmptyActionBar();
- return;
- }
-
- InitPetActionBar();
-
- for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x)
- {
- uint32 spellId = ((Creature*)m_unit)->m_spells[x];
- m_charmspells[x].spellId = spellId;
-
- if(!spellId)
- continue;
-
- if (IsPassiveSpell(spellId))
- {
- m_unit->CastSpell(m_unit, spellId, true);
- m_charmspells[x].active = ACT_PASSIVE;
- }
- else
- {
- ActiveStates newstate;
- bool onlyselfcast = true;
- SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
-
- if(!spellInfo) onlyselfcast = false;
- for(uint32 i = 0;i<3 && onlyselfcast;++i) //non existent spell will not make any problems as onlyselfcast would be false -> break right away
- {
- if(spellInfo->EffectImplicitTargetA[i] != TARGET_SELF && spellInfo->EffectImplicitTargetA[i] != 0)
- onlyselfcast = false;
- }
-
- if(onlyselfcast || !IsPositiveSpell(spellId)) //only self cast and spells versus enemies are autocastable
- newstate = ACT_DISABLED;
- else
- newstate = ACT_CAST;
-
- AddSpellToAB(0, spellId, newstate);
- }
- }
-}
-
-bool CharmInfo::AddSpellToAB(uint32 oldid, uint32 newid, ActiveStates newstate)
-{
- for(uint8 i = 0; i < 10; i++)
- {
- if((PetActionBar[i].Type == ACT_DISABLED || PetActionBar[i].Type == ACT_ENABLED || PetActionBar[i].Type == ACT_CAST) && PetActionBar[i].SpellOrAction == oldid)
- {
- PetActionBar[i].SpellOrAction = newid;
- if(!oldid)
- {
- if(newstate == ACT_DECIDE)
- PetActionBar[i].Type = ACT_DISABLED;
- else
- PetActionBar[i].Type = newstate;
- }
-
- return true;
- }
- }
- return false;
-}
-
-void CharmInfo::ToggleCreatureAutocast(uint32 spellid, bool apply)
-{
- if(IsPassiveSpell(spellid))
- return;
-
- for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x)
- {
- if(spellid == m_charmspells[x].spellId)
- {
- m_charmspells[x].active = apply ? ACT_ENABLED : ACT_DISABLED;
- }
- }
-}
-
-void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow)
-{
- m_petnumber = petnumber;
- if(statwindow)
- m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, m_petnumber);
- else
- m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, 0);
-}
-
-bool Unit::isFrozen() const
-{
- AuraList const& mRoot = GetAurasByType(SPELL_AURA_MOD_ROOT);
- for(AuraList::const_iterator i = mRoot.begin(); i != mRoot.end(); ++i)
- if( GetSpellSchoolMask((*i)->GetSpellProto()) & SPELL_SCHOOL_MASK_FROST)
- return true;
- return false;
-}
-
-struct ProcTriggeredData
-{
- ProcTriggeredData(SpellEntry const * _spellInfo, uint32 _spellParam, Aura* _triggeredByAura, uint32 _cooldown)
- : spellInfo(_spellInfo), spellParam(_spellParam), triggeredByAura(_triggeredByAura),
- triggeredByAura_SpellPair(Unit::spellEffectPair(triggeredByAura->GetId(),triggeredByAura->GetEffIndex())),
- cooldown(_cooldown)
- {}
-
- SpellEntry const * spellInfo;
- uint32 spellParam;
- Aura* triggeredByAura;
- Unit::spellEffectPair triggeredByAura_SpellPair;
- uint32 cooldown;
-};
-
-typedef std::list< ProcTriggeredData > ProcTriggeredList;
-
-void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, AuraTypeSet const& procAuraTypes, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage, SpellSchoolMask damageSchoolMask )
-{
- for(AuraTypeSet::const_iterator aur = procAuraTypes.begin(); aur != procAuraTypes.end(); ++aur)
- {
- // List of spells (effects) that proceed. Spell prototype and aura-specific value (damage for TRIGGER_DAMAGE)
- ProcTriggeredList procTriggered;
-
- AuraList const& auras = GetAurasByType(*aur);
- for(AuraList::const_iterator i = auras.begin(), next; i != auras.end(); i = next)
- {
- next = i; ++next;
-
- SpellEntry const *spellProto = (*i)->GetSpellProto();
- if(!spellProto)
- continue;
-
- SpellProcEventEntry const *spellProcEvent = spellmgr.GetSpellProcEvent(spellProto->Id);
- if(!spellProcEvent)
- {
- // used to prevent spam in log about same non-handled spells
- static std::set<uint32> nonHandledSpellProcSet;
-
- if(spellProto->procFlags != 0 && nonHandledSpellProcSet.find(spellProto->Id)==nonHandledSpellProcSet.end())
- {
- sLog.outError("ProcDamageAndSpell: spell %u (%s aura source) not have record in `spell_proc_event`)",spellProto->Id,(isVictim?"a victim's":"an attacker's"));
- nonHandledSpellProcSet.insert(spellProto->Id);
- }
-
- // spell.dbc use totally different flags, that only can create problems if used.
- continue;
- }
-
- // Check spellProcEvent data requirements
- if(!SpellMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, procSpell,procFlag))
- continue;
-
- // Check if current equipment allows aura to proc
- if(!isVictim && GetTypeId() == TYPEID_PLAYER )
- {
- if(spellProto->EquippedItemClass == ITEM_CLASS_WEAPON)
- {
- Item *item = ((Player*)this)->GetWeaponForAttack(attType,true);
-
- if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
- continue;
- }
- else if(spellProto->EquippedItemClass == ITEM_CLASS_ARMOR)
- {
- // Check if player is wearing shield
- Item *item = ((Player*)this)->GetShield(true);
- if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
- continue;
- }
- }
-
- float chance = (float)spellProto->procChance;
-
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance);
-
- if(!isVictim && spellProcEvent->ppmRate != 0)
- {
- uint32 WeaponSpeed = GetAttackTime(attType);
- chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate);
- }
-
- if(roll_chance_f(chance))
- {
- uint32 cooldown = spellProcEvent->cooldown;
-
- uint32 i_spell_eff = (*i)->GetEffIndex();
-
- int32 i_spell_param;
- switch(*aur)
- {
- case SPELL_AURA_PROC_TRIGGER_SPELL:
- i_spell_param = procFlag;
- break;
- case SPELL_AURA_DUMMY:
- case SPELL_AURA_PRAYER_OF_MENDING:
- case SPELL_AURA_MOD_HASTE:
- i_spell_param = i_spell_eff;
- break;
- case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
- i_spell_param = (*i)->GetModifier()->m_miscvalue;
- break;
- default:
- i_spell_param = (*i)->GetModifier()->m_amount;
- break;
- }
-
- procTriggered.push_back( ProcTriggeredData(spellProto,i_spell_param,*i, cooldown) );
- }
- }
-
- // Handle effects proceed this time
- for(ProcTriggeredList::iterator i = procTriggered.begin(); i != procTriggered.end(); ++i)
- {
- // Some auras can be deleted in function called in this loop (except first, ofc)
- // Until storing auras in std::multimap to hard check deleting by another way
- if(i != procTriggered.begin())
- {
- bool found = false;
- AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair);
- AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair);
- for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr)
- {
- if(itr->second==i->triggeredByAura)
- {
- found = true;
- break;
- }
- }
-
- if(!found)
- {
- sLog.outError("Spell aura %u (id:%u effect:%u) has been deleted before call spell proc event handler",*aur,i->triggeredByAura_SpellPair.first,i->triggeredByAura_SpellPair.second);
- sLog.outError("It can be deleted one from early proccesed auras:");
- for(ProcTriggeredList::iterator i2 = procTriggered.begin(); i != i2; ++i2)
- sLog.outError(" Spell aura %u (id:%u effect:%u)",*aur,i2->triggeredByAura_SpellPair.first,i2->triggeredByAura_SpellPair.second);
- sLog.outError(" <end of list>");
- continue;
- }
- }
-
- // save charges existence before processing to prevent crash at access to deleted triggered aura after
- bool triggeredByAuraWithCharges = i->triggeredByAura->m_procCharges > 0;
-
- bool casted = false;
- switch(*aur)
- {
- case SPELL_AURA_PROC_TRIGGER_SPELL:
- {
- sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
- casted = HandleProcTriggerSpell(pTarget, damage, i->triggeredByAura, procSpell,i->spellParam,attType,i->cooldown);
- break;
- }
- case SPELL_AURA_PROC_TRIGGER_DAMAGE:
- {
- sLog.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", i->spellParam, i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
- uint32 damage = i->spellParam;
- SpellNonMeleeDamageLog(pTarget, i->spellInfo->Id, damage, true, true);
- casted = true;
- break;
- }
- case SPELL_AURA_DUMMY:
- {
- sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
- casted = HandleDummyAuraProc(pTarget, i->spellInfo, i->spellParam, damage, i->triggeredByAura, procSpell, procFlag,i->cooldown);
- break;
- }
- case SPELL_AURA_PRAYER_OF_MENDING:
- {
- sLog.outDebug("ProcDamageAndSpell(mending): casting spell id %u (triggered by %s dummy aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
-
- // aura can be deleted at casts
- int32 heal = i->triggeredByAura->GetModifier()->m_amount;
- uint64 caster_guid = i->triggeredByAura->GetCasterGUID();
-
- // jumps
- int32 jumps = i->triggeredByAura->m_procCharges-1;
-
- // current aura expire
- i->triggeredByAura->m_procCharges = 1; // will removed at next charges decrease
-
- // next target selection
- if(jumps > 0 && GetTypeId()==TYPEID_PLAYER && IS_PLAYER_GUID(caster_guid))
- {
- Aura* aura = i->triggeredByAura;
-
- SpellEntry const* spellProto = aura->GetSpellProto();
- uint32 effIdx = aura->GetEffIndex();
-
- float radius;
- if (spellProto->EffectRadiusIndex[effIdx])
- radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellProto->EffectRadiusIndex[effIdx]));
- else
- radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(spellProto->rangeIndex));
-
- if(Player* caster = ((Player*)aura->GetCaster()))
- {
- caster->ApplySpellMod(spellProto->Id, SPELLMOD_RADIUS, radius,NULL);
-
- if(Player* target = ((Player*)this)->GetNextRandomRaidMember(radius))
- {
- // aura will applied from caster, but spell casted from current aura holder
- SpellModifier *mod = new SpellModifier;
- mod->op = SPELLMOD_CHARGES;
- mod->value = jumps-5; // negative
- mod->type = SPELLMOD_FLAT;
- mod->spellId = spellProto->Id;
- mod->effectId = effIdx;
- mod->lastAffected = NULL;
- mod->mask = spellProto->SpellFamilyFlags;
- mod->charges = 0;
-
- caster->AddSpellMod(mod, true);
- CastCustomSpell(target,spellProto->Id,&heal,NULL,NULL,true,NULL,aura,caster->GetGUID());
- caster->AddSpellMod(mod, false);
- }
- }
- }
-
- // heal
- CastCustomSpell(this,33110,&heal,NULL,NULL,true,NULL,NULL,caster_guid);
- casted = true;
- break;
- }
- case SPELL_AURA_MOD_HASTE:
- {
- sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s haste aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
- casted = HandleHasteAuraProc(pTarget, i->spellInfo, i->spellParam, damage, i->triggeredByAura, procSpell, procFlag,i->cooldown);
- break;
- }
- case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
- {
- // nothing do, just charges counter
- // but count only in case appropriate school damage
- casted = i->triggeredByAura->GetModifier()->m_miscvalue & damageSchoolMask;
- break;
- }
- case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
- {
- sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
- casted = HandleOverrideClassScriptAuraProc(pTarget, i->spellParam, damage, i->triggeredByAura, procSpell,i->cooldown);
- break;
- }
- default:
- {
- // nothing do, just charges counter
- casted = true;
- break;
- }
- }
-
- // Update charge (aura can be removed by triggers)
- if(casted && triggeredByAuraWithCharges)
- {
- // need found aura (can be dropped by triggers)
- AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair);
- AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair);
- for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr)
- {
- if(itr->second == i->triggeredByAura)
- {
- if(i->triggeredByAura->m_procCharges > 0)
- i->triggeredByAura->m_procCharges -= 1;
-
- i->triggeredByAura->UpdateAuraCharges();
- break;
- }
- }
- }
- }
-
- // Safely remove auras with zero charges
- for(AuraList::const_iterator i = auras.begin(), next; i != auras.end(); i = next)
- {
- next = i; ++next;
- if((*i)->m_procCharges == 0)
- {
- RemoveAurasDueToSpell((*i)->GetId());
- next = auras.begin();
- }
- }
- }
-}
-
-SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const
-{
- return SPELL_SCHOOL_MASK_NORMAL;
-}
-
-Player* Unit::GetSpellModOwner()
-{
- if(GetTypeId()==TYPEID_PLAYER)
- return (Player*)this;
- if(((Creature*)this)->isPet() || ((Creature*)this)->isTotem())
- {
- Unit* owner = GetOwner();
- if(owner && owner->GetTypeId()==TYPEID_PLAYER)
- return (Player*)owner;
- }
- return NULL;
-}
-
-///----------Pet responses methods-----------------
-void Unit::SendPetCastFail(uint32 spellid, uint8 msg)
-{
- Unit *owner = GetCharmerOrOwner();
- if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
- return;
-
- WorldPacket data(SMSG_PET_CAST_FAILED, (4+1));
- data << uint32(spellid);
- data << uint8(msg);
- ((Player*)owner)->GetSession()->SendPacket(&data);
-}
-
-void Unit::SendPetActionFeedback (uint8 msg)
-{
- Unit* owner = GetOwner();
- if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
- return;
-
- WorldPacket data(SMSG_PET_ACTION_FEEDBACK, 1);
- data << uint8(msg);
- ((Player*)owner)->GetSession()->SendPacket(&data);
-}
-
-void Unit::SendPetTalk (uint32 pettalk)
-{
- Unit* owner = GetOwner();
- if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
- return;
-
- WorldPacket data(SMSG_PET_ACTION_SOUND, 8+4);
- data << uint64(GetGUID());
- data << uint32(pettalk);
- ((Player*)owner)->GetSession()->SendPacket(&data);
-}
-
-void Unit::SendPetSpellCooldown (uint32 spellid, time_t cooltime)
-{
- Unit* owner = GetOwner();
- if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
- return;
-
- WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+4+4);
- data << uint64(GetGUID());
- data << uint8(0x0);
- data << uint32(spellid);
- data << uint32(cooltime);
-
- ((Player*)owner)->GetSession()->SendPacket(&data);
-}
-
-void Unit::SendPetClearCooldown (uint32 spellid)
-{
- Unit* owner = GetOwner();
- if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
- return;
-
- WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
- data << uint32(spellid);
- data << uint64(GetGUID());
- ((Player*)owner)->GetSession()->SendPacket(&data);
-}
-
-void Unit::SendPetAIReaction(uint64 guid)
-{
- Unit* owner = GetOwner();
- if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
- return;
-
- WorldPacket data(SMSG_AI_REACTION, 12);
- data << uint64(guid) << uint32(00000002);
- ((Player*)owner)->GetSession()->SendPacket(&data);
-}
-
-///----------End of Pet responses methods----------
-
-void Unit::StopMoving()
-{
- clearUnitState(UNIT_STAT_MOVING);
-
- // send explicit stop packet
- // rely on vmaps here because for exemple stormwind is in air
- float z = MapManager::Instance().GetBaseMap(GetMapId())->GetHeight(GetPositionX(), GetPositionY(), GetPositionZ(), true);
- //if (fabs(GetPositionZ() - z) < 2.0f)
- // Relocate(GetPositionX(), GetPositionY(), z);
- Relocate(GetPositionX(), GetPositionY(),GetPositionZ());
-
- SendMonsterMove(GetPositionX(), GetPositionY(), GetPositionZ(), 0, true, 0);
-
- // update position and orientation;
- WorldPacket data;
- BuildHeartBeatMsg(&data);
- SendMessageToSet(&data,false);
-}
-
-void Unit::SetFeared(bool apply, uint64 casterGUID, uint32 spellID)
-{
- if( apply )
- {
- if(HasAuraType(SPELL_AURA_PREVENTS_FLEEING))
- return;
-
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
-
- GetMotionMaster()->MovementExpired(false);
- CastStop(GetGUID()==casterGUID ? spellID : 0);
-
- Unit* caster = ObjectAccessor::GetUnit(*this,casterGUID);
-
- GetMotionMaster()->MoveFleeing(caster); // caster==NULL processed in MoveFleeing
- }
- else
- {
- RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
-
- GetMotionMaster()->MovementExpired(false);
-
- if( GetTypeId() != TYPEID_PLAYER && isAlive() )
- {
- // restore appropriate movement generator
- if(getVictim())
- GetMotionMaster()->MoveChase(getVictim());
- else
- GetMotionMaster()->Initialize();
-
- // attack caster if can
- Unit* caster = ObjectAccessor::GetObjectInWorld(casterGUID, (Unit*)NULL);
- if(caster && caster != getVictim() && ((Creature*)this)->AI())
- ((Creature*)this)->AI()->AttackStart(caster);
- }
- }
-
- if (GetTypeId() == TYPEID_PLAYER)
- ((Player*)this)->SetClientControl(this, !apply);
-}
-
-void Unit::SetConfused(bool apply, uint64 casterGUID, uint32 spellID)
-{
- if( apply )
- {
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
-
- CastStop(GetGUID()==casterGUID ? spellID : 0);
-
- GetMotionMaster()->MoveConfused();
- }
- else
- {
- RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
-
- GetMotionMaster()->MovementExpired(false);
-
- if (GetTypeId() == TYPEID_UNIT)
- {
- // if in combat restore movement generator
- if(getVictim())
- GetMotionMaster()->MoveChase(getVictim());
- }
- }
-
- if(GetTypeId() == TYPEID_PLAYER)
- ((Player*)this)->SetClientControl(this, !apply);
-}
-
-bool Unit::IsSitState() const
-{
- uint8 s = getStandState();
- return s == PLAYER_STATE_SIT_CHAIR || s == PLAYER_STATE_SIT_LOW_CHAIR ||
- s == PLAYER_STATE_SIT_MEDIUM_CHAIR || s == PLAYER_STATE_SIT_HIGH_CHAIR ||
- s == PLAYER_STATE_SIT;
-}
-
-bool Unit::IsStandState() const
-{
- uint8 s = getStandState();
- return !IsSitState() && s != PLAYER_STATE_SLEEP && s != PLAYER_STATE_KNEEL;
-}
-
-void Unit::SetStandState(uint8 state)
-{
- SetByteValue(UNIT_FIELD_BYTES_1, 0, state);
-
- if (IsStandState())
- RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_SEATED);
-
- if(GetTypeId()==TYPEID_PLAYER)
- {
- WorldPacket data(SMSG_STANDSTATE_UPDATE, 1);
- data << (uint8)state;
- ((Player*)this)->GetSession()->SendPacket(&data);
- }
-}
-
-bool Unit::IsPolymorphed() const
-{
- return GetSpellSpecific(getTransForm())==SPELL_MAGE_POLYMORPH;
-}
-
-void Unit::SetDisplayId(uint32 modelId)
-{
- SetUInt32Value(UNIT_FIELD_DISPLAYID, modelId);
-
- if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet())
- {
- Pet *pet = ((Pet*)this);
- if(!pet->isControlled())
- return;
- Unit *owner = GetOwner();
- if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
- ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MODEL_ID);
- }
-}
-
-void Unit::ClearComboPointHolders()
-{
- while(!m_ComboPointHolders.empty())
- {
- uint32 lowguid = *m_ComboPointHolders.begin();
-
- Player* plr = objmgr.GetPlayer(MAKE_NEW_GUID(lowguid, 0, HIGHGUID_PLAYER));
- if(plr && plr->GetComboTarget()==GetGUID()) // recheck for safe
- plr->ClearComboPoints(); // remove also guid from m_ComboPointHolders;
- else
- m_ComboPointHolders.erase(lowguid); // or remove manually
- }
-}
-
-void Unit::ClearAllReactives()
-{
-
- for(int i=0; i < MAX_REACTIVE; ++i)
- m_reactiveTimer[i] = 0;
-
- if (HasAuraState( AURA_STATE_DEFENSE))
- ModifyAuraState(AURA_STATE_DEFENSE, false);
- if (getClass() == CLASS_HUNTER && HasAuraState( AURA_STATE_HUNTER_PARRY))
- ModifyAuraState(AURA_STATE_HUNTER_PARRY, false);
- if (HasAuraState( AURA_STATE_CRIT))
- ModifyAuraState(AURA_STATE_CRIT, false);
- if (getClass() == CLASS_HUNTER && HasAuraState( AURA_STATE_HUNTER_CRIT_STRIKE) )
- ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, false);
-
- if(getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER)
- ((Player*)this)->ClearComboPoints();
-}
-
-void Unit::UpdateReactives( uint32 p_time )
-{
- for(int i = 0; i < MAX_REACTIVE; ++i)
- {
- ReactiveType reactive = ReactiveType(i);
-
- if(!m_reactiveTimer[reactive])
- continue;
-
- if ( m_reactiveTimer[reactive] <= p_time)
- {
- m_reactiveTimer[reactive] = 0;
-
- switch ( reactive )
- {
- case REACTIVE_DEFENSE:
- if (HasAuraState(AURA_STATE_DEFENSE))
- ModifyAuraState(AURA_STATE_DEFENSE, false);
- break;
- case REACTIVE_HUNTER_PARRY:
- if ( getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_PARRY))
- ModifyAuraState(AURA_STATE_HUNTER_PARRY, false);
- break;
- case REACTIVE_CRIT:
- if (HasAuraState(AURA_STATE_CRIT))
- ModifyAuraState(AURA_STATE_CRIT, false);
- break;
- case REACTIVE_HUNTER_CRIT:
- if ( getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_CRIT_STRIKE) )
- ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, false);
- break;
- case REACTIVE_OVERPOWER:
- if(getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER)
- ((Player*)this)->ClearComboPoints();
- break;
- default:
- break;
- }
- }
- else
- {
- m_reactiveTimer[reactive] -= p_time;
- }
- }
-}
-
-Unit* Unit::SelectNearbyTarget() const
-{
- CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
- cell.SetNoCreate();
-
- std::list<Unit *> targets;
-
- {
- MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, this, ATTACK_DISTANCE);
- MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck> searcher(targets, u_check);
-
- TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
- TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
-
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
- cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
- }
-
- // remove current target
- if(getVictim())
- targets.remove(getVictim());
-
- // remove not LoS targets
- for(std::list<Unit *>::iterator tIter = targets.begin(); tIter != targets.end();)
- {
- if(!IsWithinLOSInMap(*tIter))
- {
- std::list<Unit *>::iterator tIter2 = tIter;
- ++tIter;
- targets.erase(tIter2);
- }
- else
- ++tIter;
- }
-
- // no appropriate targets
- if(targets.empty())
- return NULL;
-
- // select random
- uint32 rIdx = urand(0,targets.size()-1);
- std::list<Unit *>::const_iterator tcIter = targets.begin();
- for(uint32 i = 0; i < rIdx; ++i)
- ++tcIter;
-
- return *tcIter;
-}
-
-void Unit::ApplyAttackTimePercentMod( WeaponAttackType att,float val, bool apply )
-{
- if(val > 0)
- {
- ApplyPercentModFloatVar(m_modAttackSpeedPct[att], val, !apply);
- ApplyPercentModFloatValue(UNIT_FIELD_BASEATTACKTIME+att,val,!apply);
- }
- else
- {
- ApplyPercentModFloatVar(m_modAttackSpeedPct[att], -val, apply);
- ApplyPercentModFloatValue(UNIT_FIELD_BASEATTACKTIME+att,-val,apply);
- }
-}
-
-void Unit::ApplyCastTimePercentMod(float val, bool apply )
-{
- if(val > 0)
- ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED,val,!apply);
- else
- ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED,-val,apply);
-}
-
-uint32 Unit::GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectType damagetype, uint32 CastingTime )
-{
- if (CastingTime > 7000) CastingTime = 7000;
- if (CastingTime < 1500) CastingTime = 1500;
-
- if(damagetype == DOT && !IsChanneledSpell(spellProto))
- CastingTime = 3500;
-
- int32 overTime = 0;
- uint8 effects = 0;
- bool DirectDamage = false;
- bool AreaEffect = false;
-
- for ( uint32 i=0; i<3;i++)
- {
- switch ( spellProto->Effect[i] )
- {
- case SPELL_EFFECT_SCHOOL_DAMAGE:
- case SPELL_EFFECT_POWER_DRAIN:
- case SPELL_EFFECT_HEALTH_LEECH:
- case SPELL_EFFECT_ENVIRONMENTAL_DAMAGE:
- case SPELL_EFFECT_POWER_BURN:
- case SPELL_EFFECT_HEAL:
- DirectDamage = true;
- break;
- case SPELL_EFFECT_APPLY_AURA:
- switch ( spellProto->EffectApplyAuraName[i] )
- {
- case SPELL_AURA_PERIODIC_DAMAGE:
- case SPELL_AURA_PERIODIC_HEAL:
- case SPELL_AURA_PERIODIC_LEECH:
- if ( GetSpellDuration(spellProto) )
- overTime = GetSpellDuration(spellProto);
- break;
- default:
- // -5% per additional effect
- ++effects;
- break;
- }
- default:
- break;
- }
-
- if(IsAreaEffectTarget(Targets(spellProto->EffectImplicitTargetA[i])) || IsAreaEffectTarget(Targets(spellProto->EffectImplicitTargetB[i])))
- AreaEffect = true;
- }
-
- // Combined Spells with Both Over Time and Direct Damage
- if ( overTime > 0 && CastingTime > 0 && DirectDamage )
- {
- // mainly for DoTs which are 3500 here otherwise
- uint32 OriginalCastTime = GetSpellCastTime(spellProto);
- if (OriginalCastTime > 7000) OriginalCastTime = 7000;
- if (OriginalCastTime < 1500) OriginalCastTime = 1500;
- // Portion to Over Time
- float PtOT = (overTime / 15000.f) / ((overTime / 15000.f) + (OriginalCastTime / 3500.f));
-
- if ( damagetype == DOT )
- CastingTime = uint32(CastingTime * PtOT);
- else if ( PtOT < 1.0f )
- CastingTime = uint32(CastingTime * (1 - PtOT));
- else
- CastingTime = 0;
- }
-
- // Area Effect Spells receive only half of bonus
- if ( AreaEffect )
- CastingTime /= 2;
-
- // -5% of total per any additional effect
- for ( uint8 i=0; i<effects; ++i)
- {
- if ( CastingTime > 175 )
- {
- CastingTime -= 175;
- }
- else
- {
- CastingTime = 0;
- break;
- }
- }
-
- return CastingTime;
-}
-
-void Unit::UpdateAuraForGroup(uint8 slot)
-{
- if(GetTypeId() == TYPEID_PLAYER)
- {
- Player* player = (Player*)this;
- if(player->GetGroup())
- {
- player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_AURAS);
- player->SetAuraUpdateMask(slot);
- }
- }
- else if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet())
- {
- Pet *pet = ((Pet*)this);
- if(pet->isControlled())
- {
- Unit *owner = GetOwner();
- if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
- {
- ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_AURAS);
- pet->SetAuraUpdateMask(slot);
- }
- }
- }
-}
-
-float Unit::GetAPMultiplier(WeaponAttackType attType, bool normalized)
-{
- if (!normalized || GetTypeId() != TYPEID_PLAYER)
- return float(GetAttackTime(attType))/1000.0f;
-
- Item *Weapon = ((Player*)this)->GetWeaponForAttack(attType);
- if (!Weapon)
- return 2.4; // fist attack
-
- switch (Weapon->GetProto()->InventoryType)
- {
- case INVTYPE_2HWEAPON:
- return 3.3;
- case INVTYPE_RANGED:
- case INVTYPE_RANGEDRIGHT:
- case INVTYPE_THROWN:
- return 2.8;
- case INVTYPE_WEAPON:
- case INVTYPE_WEAPONMAINHAND:
- case INVTYPE_WEAPONOFFHAND:
- default:
- return Weapon->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_DAGGER ? 1.7 : 2.4;
- }
-}
-
-Aura* Unit::GetDummyAura( uint32 spell_id ) const
-{
- Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY);
- for(Unit::AuraList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr)
- if ((*itr)->GetId() == spell_id)
- return *itr;
-
- return NULL;
-}
-
-bool Unit::IsUnderLastManaUseEffect() const
-{
- return getMSTimeDiff(m_lastManaUse,getMSTime()) < 5000;
-}
-
-void Unit::SetContestedPvP(Player *attackedPlayer)
-{
- Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
-
- if(!player || attackedPlayer && (attackedPlayer == player || player->duel && player->duel->opponent == attackedPlayer))
- return;
-
- player->SetContestedPvPTimer(30000);
- if(!player->hasUnitState(UNIT_STAT_ATTACK_PLAYER))
- {
- player->addUnitState(UNIT_STAT_ATTACK_PLAYER);
- player->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
- // call MoveInLineOfSight for nearby contested guards
- SetVisibility(GetVisibility());
- }
- if(!hasUnitState(UNIT_STAT_ATTACK_PLAYER))
- {
- addUnitState(UNIT_STAT_ATTACK_PLAYER);
- // call MoveInLineOfSight for nearby contested guards
- SetVisibility(GetVisibility());
- }
-}
-
-void Unit::AddPetAura(PetAura const* petSpell)
-{
- m_petAuras.insert(petSpell);
- if(Pet* pet = GetPet())
- pet->CastPetAura(petSpell);
-}
-
-void Unit::RemovePetAura(PetAura const* petSpell)
-{
- m_petAuras.erase(petSpell);
- if(Pet* pet = GetPet())
- pet->RemoveAurasDueToSpell(petSpell->GetAura(pet->GetEntry()));
-}
+/*
+ * 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 "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Unit.h"
+#include "QuestDef.h"
+#include "Player.h"
+#include "Creature.h"
+#include "Spell.h"
+#include "Group.h"
+#include "SpellAuras.h"
+#include "MapManager.h"
+#include "ObjectAccessor.h"
+#include "CreatureAI.h"
+#include "Formulas.h"
+#include "Pet.h"
+#include "Util.h"
+#include "Totem.h"
+#include "BattleGround.h"
+#include "InstanceSaveMgr.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "Path.h"
+
+#include <math.h>
+
+float baseMoveSpeed[MAX_MOVE_TYPE] =
+{
+ 2.5f, // MOVE_WALK
+ 7.0f, // MOVE_RUN
+ 1.25f, // MOVE_WALKBACK
+ 4.722222f, // MOVE_SWIM
+ 4.5f, // MOVE_SWIMBACK
+ 3.141594f, // MOVE_TURN
+ 7.0f, // MOVE_FLY
+ 4.5f, // MOVE_FLYBACK
+};
+
+// auraTypes contains attacker auras capable of proc'ing cast auras
+static Unit::AuraTypeSet GenerateAttakerProcCastAuraTypes()
+{
+ static Unit::AuraTypeSet auraTypes;
+ auraTypes.insert(SPELL_AURA_DUMMY);
+ auraTypes.insert(SPELL_AURA_PROC_TRIGGER_SPELL);
+ auraTypes.insert(SPELL_AURA_MOD_HASTE);
+ auraTypes.insert(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ return auraTypes;
+}
+
+// auraTypes contains victim auras capable of proc'ing cast auras
+static Unit::AuraTypeSet GenerateVictimProcCastAuraTypes()
+{
+ static Unit::AuraTypeSet auraTypes;
+ auraTypes.insert(SPELL_AURA_DUMMY);
+ auraTypes.insert(SPELL_AURA_PRAYER_OF_MENDING);
+ auraTypes.insert(SPELL_AURA_PROC_TRIGGER_SPELL);
+ return auraTypes;
+}
+
+// auraTypes contains auras capable of proc effect/damage (but not cast) for attacker
+static Unit::AuraTypeSet GenerateAttakerProcEffectAuraTypes()
+{
+ static Unit::AuraTypeSet auraTypes;
+ auraTypes.insert(SPELL_AURA_MOD_DAMAGE_DONE);
+ auraTypes.insert(SPELL_AURA_PROC_TRIGGER_DAMAGE);
+ auraTypes.insert(SPELL_AURA_MOD_CASTING_SPEED);
+ auraTypes.insert(SPELL_AURA_MOD_RATING);
+ return auraTypes;
+}
+
+// auraTypes contains auras capable of proc effect/damage (but not cast) for victim
+static Unit::AuraTypeSet GenerateVictimProcEffectAuraTypes()
+{
+ static Unit::AuraTypeSet auraTypes;
+ auraTypes.insert(SPELL_AURA_MOD_RESISTANCE);
+ auraTypes.insert(SPELL_AURA_PROC_TRIGGER_DAMAGE);
+ auraTypes.insert(SPELL_AURA_MOD_PARRY_PERCENT);
+ auraTypes.insert(SPELL_AURA_MOD_BLOCK_PERCENT);
+ auraTypes.insert(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ return auraTypes;
+}
+
+static Unit::AuraTypeSet attackerProcCastAuraTypes = GenerateAttakerProcCastAuraTypes();
+static Unit::AuraTypeSet attackerProcEffectAuraTypes = GenerateAttakerProcEffectAuraTypes();
+
+static Unit::AuraTypeSet victimProcCastAuraTypes = GenerateVictimProcCastAuraTypes();
+static Unit::AuraTypeSet victimProcEffectAuraTypes = GenerateVictimProcEffectAuraTypes();
+
+// auraTypes contains auras capable of proc'ing for attacker and victim
+static Unit::AuraTypeSet GenerateProcAuraTypes()
+{
+ Unit::AuraTypeSet auraTypes;
+ auraTypes.insert(attackerProcCastAuraTypes.begin(),attackerProcCastAuraTypes.end());
+ auraTypes.insert(attackerProcEffectAuraTypes.begin(),attackerProcEffectAuraTypes.end());
+ auraTypes.insert(victimProcCastAuraTypes.begin(),victimProcCastAuraTypes.end());
+ auraTypes.insert(victimProcEffectAuraTypes.begin(),victimProcEffectAuraTypes.end());
+ return auraTypes;
+}
+
+static Unit::AuraTypeSet procAuraTypes = GenerateProcAuraTypes();
+
+bool IsPassiveStackableSpell( uint32 spellId )
+{
+ if(!IsPassiveSpell(spellId))
+ return false;
+
+ SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId);
+ if(!spellProto)
+ return false;
+
+ for(int j = 0; j < 3; ++j)
+ {
+ if(std::find(procAuraTypes.begin(),procAuraTypes.end(),spellProto->EffectApplyAuraName[j])!=procAuraTypes.end())
+ return false;
+ }
+
+ return true;
+}
+
+Unit::Unit()
+: WorldObject(), i_motionMaster(this), m_ThreatManager(this), m_HostilRefManager(this)
+{
+ m_objectType |= TYPEMASK_UNIT;
+ m_objectTypeId = TYPEID_UNIT;
+ // 2.3.2 - 0x70
+ m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_LIVING | UPDATEFLAG_HASPOSITION);
+
+ m_attackTimer[BASE_ATTACK] = 0;
+ m_attackTimer[OFF_ATTACK] = 0;
+ m_attackTimer[RANGED_ATTACK] = 0;
+ m_modAttackSpeedPct[BASE_ATTACK] = 1.0f;
+ m_modAttackSpeedPct[OFF_ATTACK] = 1.0f;
+ m_modAttackSpeedPct[RANGED_ATTACK] = 1.0f;
+
+ m_extraAttacks = 0;
+
+ m_state = 0;
+ m_form = FORM_NONE;
+ m_deathState = ALIVE;
+
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ m_currentSpells[i] = NULL;
+
+ m_addDmgOnce = 0;
+
+ for(int i = 0; i < MAX_TOTEM; ++i)
+ m_TotemSlot[i] = 0;
+
+ m_ObjectSlot[0] = m_ObjectSlot[1] = m_ObjectSlot[2] = m_ObjectSlot[3] = 0;
+ //m_Aura = NULL;
+ //m_AurasCheck = 2000;
+ //m_removeAuraTimer = 4;
+ //tmpAura = NULL;
+ waterbreath = false;
+
+ m_Visibility = VISIBILITY_ON;
+
+ m_detectInvisibilityMask = 0;
+ m_invisibilityMask = 0;
+ m_transform = 0;
+ m_ShapeShiftFormSpellId = 0;
+ m_canModifyStats = false;
+
+ for (int i = 0; i < MAX_SPELL_IMMUNITY; i++)
+ m_spellImmune[i].clear();
+ for (int i = 0; i < UNIT_MOD_END; i++)
+ {
+ m_auraModifiersGroup[i][BASE_VALUE] = 0.0f;
+ m_auraModifiersGroup[i][BASE_PCT] = 1.0f;
+ m_auraModifiersGroup[i][TOTAL_VALUE] = 0.0f;
+ m_auraModifiersGroup[i][TOTAL_PCT] = 1.0f;
+ }
+ // implement 50% base damage from offhand
+ m_auraModifiersGroup[UNIT_MOD_DAMAGE_OFFHAND][TOTAL_PCT] = 0.5f;
+
+ for (int i = 0; i < 3; i++)
+ {
+ m_weaponDamage[i][MINDAMAGE] = BASE_MINDAMAGE;
+ m_weaponDamage[i][MAXDAMAGE] = BASE_MAXDAMAGE;
+ }
+ for (int i = 0; i < MAX_STATS; i++)
+ m_createStats[i] = 0.0f;
+
+ m_attacking = NULL;
+ m_modMeleeHitChance = 0.0f;
+ m_modRangedHitChance = 0.0f;
+ m_modSpellHitChance = 0.0f;
+ m_baseSpellCritChance = 5;
+
+ m_CombatTimer = 0;
+ m_lastManaUse = 0;
+
+ //m_victimThreat = 0.0f;
+ for (int i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ m_threatModifier[i] = 1.0f;
+ m_isSorted = true;
+ for (int i = 0; i < MAX_MOVE_TYPE; ++i)
+ m_speed_rate[i] = 1.0f;
+
+ m_removedAuras = 0;
+ m_charmInfo = NULL;
+ m_unit_movement_flags = 0;
+
+ // remove aurastates allowing special moves
+ for(int i=0; i < MAX_REACTIVE; ++i)
+ m_reactiveTimer[i] = 0;
+}
+
+Unit::~Unit()
+{
+ // set current spells as deletable
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ {
+ // spell may be safely deleted now
+ if (m_currentSpells[i]) m_currentSpells[i]->SetDeletable(true);
+ m_currentSpells[i] = NULL;
+ }
+
+ RemoveAllGameObjects();
+ RemoveAllDynObjects();
+
+ if(m_charmInfo) delete m_charmInfo;
+}
+
+void Unit::Update( uint32 p_time )
+{
+ /*if(p_time > m_AurasCheck)
+ {
+ m_AurasCheck = 2000;
+ _UpdateAura();
+ }else
+ m_AurasCheck -= p_time;*/
+
+ // WARNING! Order of execution here is important, do not change.
+ // Spells must be processed with event system BEFORE they go to _UpdateSpells.
+ // Or else we may have some SPELL_STATE_FINISHED spells stalled in pointers, that is bad.
+ m_Events.Update( p_time );
+ _UpdateSpells( p_time );
+
+ // update combat timer only for players and pets
+ if (isInCombat() && (GetTypeId() == TYPEID_PLAYER || ((Creature*)this)->isPet() || ((Creature*)this)->isCharmed()))
+ {
+ // Check UNIT_STAT_MELEE_ATTACKING or UNIT_STAT_CHASE (without UNIT_STAT_FOLLOW in this case) so pets can reach far away
+ // targets without stopping half way there and running off.
+ // These flags are reset after target dies or another command is given.
+ if( m_HostilRefManager.isEmpty() )
+ {
+ // m_CombatTimer set at aura start and it will be freeze until aura removing
+ if ( m_CombatTimer <= p_time )
+ ClearInCombat();
+ else
+ m_CombatTimer -= p_time;
+ }
+ }
+
+ if(uint32 base_att = getAttackTimer(BASE_ATTACK))
+ {
+ setAttackTimer(BASE_ATTACK, (p_time >= base_att ? 0 : base_att - p_time) );
+ }
+
+ // update abilities available only for fraction of time
+ UpdateReactives( p_time );
+
+ ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, GetHealth() < GetMaxHealth()*0.20f);
+ ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, GetHealth() < GetMaxHealth()*0.35f);
+
+ i_motionMaster.UpdateMotion(p_time);
+}
+
+bool Unit::haveOffhandWeapon() const
+{
+ if(GetTypeId() == TYPEID_PLAYER)
+ return ((Player*)this)->GetWeaponForAttack(OFF_ATTACK,true);
+ else
+ return false;
+}
+
+void Unit::SendMonsterMoveWithSpeedToCurrentDestination(Player* player)
+{
+ float x, y, z;
+ if(GetMotionMaster()->GetDestination(x, y, z))
+ SendMonsterMoveWithSpeed(x, y, z, GetUnitMovementFlags(), 0, player);
+}
+
+void Unit::SendMonsterMoveWithSpeed(float x, float y, float z, uint32 MovementFlags, uint32 transitTime, Player* player)
+{
+ if (!transitTime)
+ {
+ float dx = x - GetPositionX();
+ float dy = y - GetPositionY();
+ float dz = z - GetPositionZ();
+
+ float dist = ((dx*dx) + (dy*dy) + (dz*dz));
+ if(dist<0)
+ dist = 0;
+ else
+ dist = sqrt(dist);
+
+ double speed = GetSpeed((MovementFlags & MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN);
+ if(speed<=0)
+ speed = 2.5f;
+ speed *= 0.001f;
+ transitTime = static_cast<uint32>(dist / speed + 0.5);
+ }
+ //float orientation = (float)atan2((double)dy, (double)dx);
+ SendMonsterMove(x, y, z, 0, MovementFlags, transitTime, player);
+}
+
+void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player)
+{
+ WorldPacket data( SMSG_MONSTER_MOVE, (41 + GetPackGUID().size()) );
+ data.append(GetPackGUID());
+
+ // Point A, starting location
+ data << GetPositionX() << GetPositionY() << GetPositionZ();
+ // unknown field - unrelated to orientation
+ // seems to increment about 1000 for every 1.7 seconds
+ // for now, we'll just use mstime
+ data << getMSTime();
+
+ data << uint8(type); // unknown
+ switch(type)
+ {
+ case 0: // normal packet
+ break;
+ case 1: // stop packet
+ SendMessageToSet( &data, true );
+ return;
+ case 3: // not used currently
+ data << uint64(0); // probably target guid
+ break;
+ case 4: // not used currently
+ data << float(0); // probably orientation
+ break;
+ }
+
+ //Movement Flags (0x0 = walk, 0x100 = run, 0x200 = fly/swim)
+ data << uint32(MovementFlags);
+
+ data << Time; // Time in between points
+ data << uint32(1); // 1 single waypoint
+ data << NewPosX << NewPosY << NewPosZ; // the single waypoint Point B
+
+ if(player)
+ player->GetSession()->SendPacket(&data);
+ else
+ SendMessageToSet( &data, true );
+}
+
+void Unit::SendMonsterMoveByPath(Path const& path, uint32 start, uint32 end, uint32 MovementFlags)
+{
+ uint32 traveltime = uint32(path.GetTotalLength(start, end) * 32);
+
+ uint32 pathSize = end-start;
+
+ WorldPacket data( SMSG_MONSTER_MOVE, (GetPackGUID().size()+4+4+4+4+1+4+4+4+pathSize*4*3) );
+ data.append(GetPackGUID());
+ data << GetPositionX( )
+ << GetPositionY( )
+ << GetPositionZ( );
+ data << GetOrientation( );
+ data << uint8( 0 );
+ data << uint32( MovementFlags );
+ data << uint32( traveltime );
+ data << uint32( pathSize );
+ data.append( (char*)path.GetNodes(start), pathSize * 4 * 3 );
+
+ //WPAssert( data.size() == 37 + pathnodes.Size( ) * 4 * 3 );
+ SendMessageToSet(&data, true);
+}
+
+void Unit::resetAttackTimer(WeaponAttackType type)
+{
+ m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]);
+}
+
+bool Unit::canReachWithAttack(Unit *pVictim) const
+{
+ assert(pVictim);
+ float reach = GetFloatValue(UNIT_FIELD_COMBATREACH);
+ if( reach <= 0.0f )
+ reach = 1.0f;
+ return IsWithinDistInMap(pVictim, reach);
+}
+
+void Unit::RemoveSpellsCausingAura(AuraType auraType)
+{
+ if (auraType >= TOTAL_AURAS) return;
+ AuraList::iterator iter, next;
+ for (iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end(); iter = next)
+ {
+ next = iter;
+ ++next;
+
+ if (*iter)
+ {
+ RemoveAurasDueToSpell((*iter)->GetId());
+ if (!m_modAuras[auraType].empty())
+ next = m_modAuras[auraType].begin();
+ else
+ return;
+ }
+ }
+}
+
+bool Unit::HasAuraType(AuraType auraType) const
+{
+ return (!m_modAuras[auraType].empty());
+}
+
+/* Called by DealDamage for auras that have a chance to be dispelled on damage taken. */
+void Unit::RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage)
+{
+ if(!HasAuraType(auraType))
+ return;
+
+ // The chance to dispell an aura depends on the damage taken with respect to the casters level.
+ uint32 max_dmg = getLevel() > 8 ? 25 * getLevel() - 150 : 50;
+ float chance = float(damage) / max_dmg * 100.0f;
+ if (roll_chance_f(chance))
+ RemoveSpellsCausingAura(auraType);
+}
+
+uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss)
+{
+ if (!pVictim->isAlive() || pVictim->isInFlight() || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
+ return 0;
+
+ //You don't lose health from damage taken from another player while in a sanctuary
+ //You still see it in the combat log though
+ if(pVictim != this && GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ const AreaTableEntry *area = GetAreaEntryByAreaID(pVictim->GetAreaId());
+ if(area && area->flags & AREA_FLAG_SANCTUARY) //sanctuary
+ return 0;
+ }
+
+ // remove affects from victim (including from 0 damage and DoTs)
+ if(pVictim != this)
+ pVictim->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+
+ // remove affects from attacker at any non-DoT damage (including 0 damage)
+ if( damagetype != DOT)
+ {
+ RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
+ RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ if(pVictim != this)
+ RemoveSpellsCausingAura(SPELL_AURA_MOD_INVISIBILITY);
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && !pVictim->IsStandState() && !pVictim->hasUnitState(UNIT_STAT_STUNNED))
+ pVictim->SetStandState(PLAYER_STATE_NONE);
+ }
+
+ //Script Event damage Deal
+ if( GetTypeId()== TYPEID_UNIT && ((Creature *)this)->AI())
+ ((Creature *)this)->AI()->DamageDeal(pVictim, damage);
+ //Script Event damage taken
+ if( pVictim->GetTypeId()== TYPEID_UNIT && ((Creature *)pVictim)->AI() )
+ ((Creature *)pVictim)->AI()->DamageTaken(this, damage);
+
+ if(!damage)
+ {
+ // Rage from physical damage received .
+ if(cleanDamage && cleanDamage->damage && (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL) && pVictim->GetTypeId() == TYPEID_PLAYER && (pVictim->getPowerType() == POWER_RAGE))
+ ((Player*)pVictim)->RewardRage(cleanDamage->damage, 0, false);
+
+ return 0;
+ }
+
+ pVictim->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_FEAR, damage);
+ // root type spells do not dispell the root effect
+ if(!spellProto || spellProto->Mechanic != MECHANIC_ROOT)
+ pVictim->RemoveSpellbyDamageTaken(SPELL_AURA_MOD_ROOT, damage);
+
+ if(pVictim->GetTypeId() != TYPEID_PLAYER)
+ {
+ // no xp,health if type 8 /critters/
+ if ( pVictim->GetCreatureType() == CREATURE_TYPE_CRITTER)
+ {
+ pVictim->setDeathState(JUST_DIED);
+ pVictim->SetHealth(0);
+
+ // allow loot only if has loot_id in creature_template
+ CreatureInfo const* cInfo = ((Creature*)pVictim)->GetCreatureInfo();
+ if(cInfo && cInfo->lootid)
+ pVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+
+ // some critters required for quests
+ if(GetTypeId() == TYPEID_PLAYER)
+ ((Player*)this)->KilledMonster(pVictim->GetEntry(),pVictim->GetGUID());
+
+ return damage;
+ }
+
+ if(!pVictim->isInCombat() && ((Creature*)pVictim)->AI())
+ ((Creature*)pVictim)->AI()->AttackStart(this);
+ }
+
+ DEBUG_LOG("DealDamageStart");
+
+ uint32 health = pVictim->GetHealth();
+ sLog.outDetail("deal dmg:%d to health:%d ",damage,health);
+
+ // duel ends when player has 1 or less hp
+ bool duel_hasEnded = false;
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->duel && damage >= (health-1))
+ {
+ // prevent kill only if killed in duel and killed by opponent or opponent controlled creature
+ if(((Player*)pVictim)->duel->opponent==this || ((Player*)pVictim)->duel->opponent->GetGUID() == GetOwnerGUID())
+ damage = health-1;
+
+ duel_hasEnded = true;
+ }
+ //Get in CombatState
+ if(pVictim != this && damagetype != DOT)
+ {
+ SetInCombatWith(pVictim);
+ pVictim->SetInCombatWith(this);
+
+ if(Player* attackedPlayer = pVictim->GetCharmerOrOwnerPlayerOrPlayerItself())
+ SetContestedPvP(attackedPlayer);
+ }
+
+ // Rage from Damage made (only from direct weapon damage)
+ if( cleanDamage && damagetype==DIRECT_DAMAGE && this != pVictim && GetTypeId() == TYPEID_PLAYER && (getPowerType() == POWER_RAGE))
+ {
+ uint32 weaponSpeedHitFactor;
+
+ switch(cleanDamage->attackType)
+ {
+ case BASE_ATTACK:
+ {
+ if(cleanDamage->hitOutCome == MELEE_HIT_CRIT)
+ weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 7);
+ else
+ weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 3.5f);
+
+ ((Player*)this)->RewardRage(damage, weaponSpeedHitFactor, true);
+
+ break;
+ }
+ case OFF_ATTACK:
+ {
+ if(cleanDamage->hitOutCome == MELEE_HIT_CRIT)
+ weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 3.5f);
+ else
+ weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 1.75f);
+
+ ((Player*)this)->RewardRage(damage, weaponSpeedHitFactor, true);
+
+ break;
+ }
+ case RANGED_ATTACK:
+ break;
+ }
+ }
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)pVictim)->InBattleGround())
+ {
+ Player *killer = ((Player*)this);
+ if(killer != ((Player*)pVictim))
+ if(BattleGround *bg = killer->GetBattleGround())
+ bg->UpdatePlayerScore(killer, SCORE_DAMAGE_DONE, damage);
+ }
+ }
+
+ if (pVictim->GetTypeId() == TYPEID_UNIT && !((Creature*)pVictim)->isPet() && !((Creature*)pVictim)->hasLootRecipient())
+ ((Creature*)pVictim)->SetLootRecipient(this);
+ if (health <= damage)
+ {
+ DEBUG_LOG("DealDamage: victim just died");
+
+ // find player: owner of controlled `this` or `this` itself maybe
+ Player *player = GetCharmerOrOwnerPlayerOrPlayerItself();
+
+ if(pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->GetLootRecipient())
+ player = ((Creature*)pVictim)->GetLootRecipient();
+ // Reward player, his pets, and group/raid members
+ // call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
+ if(player && player!=pVictim)
+ if(player->RewardPlayerAndGroupAtKill(pVictim))
+ player->ProcDamageAndSpell(pVictim,PROC_FLAG_KILL_XP_GIVER,PROC_FLAG_NONE);
+
+ DEBUG_LOG("DealDamageAttackStop");
+
+ // stop combat
+ pVictim->CombatStop();
+ pVictim->getHostilRefManager().deleteReferences();
+
+ bool damageFromSpiritOfRedemtionTalent = spellProto && spellProto->Id == 27795;
+
+ // if talent known but not triggered (check priest class for speedup check)
+ Aura* spiritOfRedemtionTalentReady = NULL;
+ if( !damageFromSpiritOfRedemtionTalent && // not called from SPELL_AURA_SPIRIT_OF_REDEMPTION
+ pVictim->GetTypeId()==TYPEID_PLAYER && pVictim->getClass()==CLASS_PRIEST )
+ {
+ AuraList const& vDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator itr = vDummyAuras.begin(); itr != vDummyAuras.end(); ++itr)
+ {
+ if((*itr)->GetSpellProto()->SpellIconID==1654)
+ {
+ spiritOfRedemtionTalentReady = *itr;
+ break;
+ }
+ }
+ }
+
+ DEBUG_LOG("SET JUST_DIED");
+ if(!spiritOfRedemtionTalentReady)
+ pVictim->setDeathState(JUST_DIED);
+
+ DEBUG_LOG("DealDamageHealth1");
+
+ if(spiritOfRedemtionTalentReady)
+ {
+ // save value before aura remove
+ uint32 ressSpellId = pVictim->GetUInt32Value(PLAYER_SELF_RES_SPELL);
+ if(!ressSpellId)
+ ressSpellId = ((Player*)pVictim)->GetResurrectionSpellId();
+
+ //Remove all expected to remove at death auras (most important negative case like DoT or periodic triggers)
+ pVictim->RemoveAllAurasOnDeath();
+
+ // restore for use at real death
+ pVictim->SetUInt32Value(PLAYER_SELF_RES_SPELL,ressSpellId);
+
+ // FORM_SPIRITOFREDEMPTION and related auras
+ pVictim->CastSpell(pVictim,27827,true,NULL,spiritOfRedemtionTalentReady);
+ }
+ else
+ pVictim->SetHealth(0);
+
+ // remember victim PvP death for corpse type and corpse reclaim delay
+ // at original death (not at SpiritOfRedemtionTalent timeout)
+ if( pVictim->GetTypeId()==TYPEID_PLAYER && !damageFromSpiritOfRedemtionTalent )
+ ((Player*)pVictim)->SetPvPDeath(player!=NULL);
+
+ // Call KilledUnit for creatures
+ if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->AI())
+ ((Creature*)this)->AI()->KilledUnit(pVictim);
+
+ // 10% durability loss on death
+ // clean InHateListOf
+ if (pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // only if not player and not controlled by player pet. And not at BG
+ if (durabilityLoss && !player && !((Player*)pVictim)->InBattleGround())
+ {
+ DEBUG_LOG("We are dead, loosing 10 percents durability");
+ ((Player*)pVictim)->DurabilityLossAll(0.10f,false);
+ // durability lost message
+ WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0);
+ ((Player*)pVictim)->GetSession()->SendPacket(&data);
+ }
+ }
+ else // creature died
+ {
+ DEBUG_LOG("DealDamageNotPlayer");
+ Creature *cVictim = (Creature*)pVictim;
+
+ if(!cVictim->isPet())
+ {
+ cVictim->DeleteThreatList();
+ cVictim->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+ }
+ // Call creature just died function
+ if (cVictim->AI())
+ cVictim->AI()->JustDied(this);
+
+ // Dungeon specific stuff, only applies to players killing creatures
+ if(cVictim->GetInstanceId())
+ {
+ Map *m = cVictim->GetMap();
+ Player *creditedPlayer = GetCharmerOrOwnerPlayerOrPlayerItself();
+ // TODO: do instance binding anyway if the charmer/owner is offline
+
+ if(m->IsDungeon() && creditedPlayer)
+ {
+ if(m->IsRaid() || m->IsHeroic())
+ {
+ if(cVictim->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
+ ((InstanceMap *)m)->PermBindAllPlayers(creditedPlayer);
+ }
+ else
+ {
+ // the reset time is set but not added to the scheduler
+ // until the players leave the instance
+ time_t resettime = cVictim->GetRespawnTimeEx() + 2 * HOUR;
+ if(InstanceSave *save = sInstanceSaveManager.GetInstanceSave(cVictim->GetInstanceId()))
+ if(save->GetResetTime() < resettime) save->SetResetTime(resettime);
+ }
+ }
+ }
+ }
+
+ // last damage from non duel opponent or opponent controlled creature
+ if(duel_hasEnded)
+ {
+ assert(pVictim->GetTypeId()==TYPEID_PLAYER);
+ Player *he = (Player*)pVictim;
+
+ assert(he->duel);
+
+ he->duel->opponent->CombatStopWithPets(true);
+ he->CombatStopWithPets(true);
+
+ he->DuelComplete(DUEL_INTERUPTED);
+ }
+
+ // battleground things (do this at the end, so the death state flag will be properly set to handle in the bg->handlekill)
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && (((Player*)pVictim)->InBattleGround()))
+ {
+ Player *killed = ((Player*)pVictim);
+ Player *killer = NULL;
+ if(GetTypeId() == TYPEID_PLAYER)
+ killer = ((Player*)this);
+ else if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet())
+ {
+ Unit *owner = GetOwner();
+ if(owner && owner->GetTypeId() == TYPEID_PLAYER)
+ killer = ((Player*)owner);
+ }
+
+ if(killer)
+ if(BattleGround *bg = killed->GetBattleGround())
+ bg->HandleKillPlayer(killed, killer); // drop flags and etc
+ }
+ }
+ else // if (health <= damage)
+ {
+ DEBUG_LOG("DealDamageAlive");
+
+ pVictim->ModifyHealth(- (int32)damage);
+
+ // Check if health is below 20% (apply damage before to prevent case when after ProcDamageAndSpell health < damage
+ if(pVictim->GetHealth()*5 < pVictim->GetMaxHealth())
+ {
+ uint32 procVictim = PROC_FLAG_NONE;
+
+ // if just dropped below 20% (for CheatDeath)
+ if((pVictim->GetHealth()+damage)*5 > pVictim->GetMaxHealth())
+ procVictim = PROC_FLAG_LOW_HEALTH;
+
+ ProcDamageAndSpell(pVictim,PROC_FLAG_TARGET_LOW_HEALTH,procVictim);
+ }
+
+ if(damagetype != DOT)
+ {
+ if(getVictim())
+ {
+ // if have target and damage pVictim just call AI recation
+ if(pVictim != getVictim() && pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->AI())
+ ((Creature*)pVictim)->AI()->AttackedBy(this);
+ }
+ else
+ {
+ // if not have main target then attack state with target (including AI call)
+ //start melee attacks only after melee hit
+ Attack(pVictim,(damagetype == DIRECT_DAMAGE));
+ }
+ }
+
+ // polymorphed and other negative transformed cases
+ if(pVictim->getTransForm() && pVictim->hasUnitState(UNIT_STAT_CONFUSED))
+ pVictim->RemoveAurasDueToSpell(pVictim->getTransForm());
+
+ if(damagetype == DIRECT_DAMAGE|| damagetype == SPELL_DIRECT_DAMAGE)
+ pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_DIRECT_DAMAGE);
+
+ if (pVictim->GetTypeId() != TYPEID_PLAYER)
+ {
+ if(spellProto && IsDamageToThreatSpell(spellProto))
+ pVictim->AddThreat(this, damage*2, damageSchoolMask, spellProto);
+ else
+ pVictim->AddThreat(this, damage, damageSchoolMask, spellProto);
+ }
+ else // victim is a player
+ {
+ // Rage from damage received
+ if(this != pVictim && pVictim->getPowerType() == POWER_RAGE)
+ {
+ uint32 rage_damage = damage + (cleanDamage ? cleanDamage->damage : 0);
+ ((Player*)pVictim)->RewardRage(rage_damage, 0, false);
+ }
+
+ // random durability for items (HIT TAKEN)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_DAMAGE)))
+ {
+ EquipmentSlots slot = EquipmentSlots(urand(0,EQUIPMENT_SLOT_END-1));
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(slot);
+ }
+ }
+
+ if(GetTypeId()==TYPEID_PLAYER)
+ {
+ // random durability for items (HIT DONE)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_DAMAGE)))
+ {
+ EquipmentSlots slot = EquipmentSlots(urand(0,EQUIPMENT_SLOT_END-1));
+ ((Player*)this)->DurabilityPointLossForEquipSlot(slot);
+ }
+ }
+
+ // TODO: Store auras by interrupt flag to speed this up.
+ AuraMap& vAuras = pVictim->GetAuras();
+ for (AuraMap::iterator i = vAuras.begin(), next; i != vAuras.end(); i = next)
+ {
+ const SpellEntry *se = i->second->GetSpellProto();
+ next = i; ++next;
+ if( se->AuraInterruptFlags & AURA_INTERRUPT_FLAG_DAMAGE )
+ {
+ bool remove = true;
+ if (se->procFlags & (1<<3))
+ {
+ if (!roll_chance_i(se->procChance))
+ remove = false;
+ }
+ if (remove)
+ {
+ pVictim->RemoveAurasDueToSpell(i->second->GetId());
+ // FIXME: this may cause the auras with proc chance to be rerolled several times
+ next = vAuras.begin();
+ }
+ }
+ }
+
+ if (damagetype != NODAMAGE && damage && pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ if( damagetype != DOT )
+ {
+ for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
+ {
+ // skip channeled spell (processed differently below)
+ if (i == CURRENT_CHANNELED_SPELL)
+ continue;
+
+ if(Spell* spell = pVictim->m_currentSpells[i])
+ if(spell->getState() == SPELL_STATE_PREPARING)
+ spell->Delayed();
+ }
+ }
+
+ if(Spell* spell = pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ {
+ if (spell->getState() == SPELL_STATE_CASTING)
+ {
+ uint32 channelInterruptFlags = spell->m_spellInfo->ChannelInterruptFlags;
+ if( channelInterruptFlags & CHANNEL_FLAG_DELAY )
+ {
+ if(pVictim!=this) //don't shorten the duration of channeling if you damage yourself
+ spell->DelayedChannel();
+ }
+ else if( (channelInterruptFlags & (CHANNEL_FLAG_DAMAGE | CHANNEL_FLAG_DAMAGE2)) )
+ {
+ sLog.outDetail("Spell %u canceled at damage!",spell->m_spellInfo->Id);
+ pVictim->InterruptSpell(CURRENT_CHANNELED_SPELL);
+ }
+ }
+ else if (spell->getState() == SPELL_STATE_DELAYED)
+ // break channeled spell in delayed state on damage
+ {
+ sLog.outDetail("Spell %u canceled at damage!",spell->m_spellInfo->Id);
+ pVictim->InterruptSpell(CURRENT_CHANNELED_SPELL);
+ }
+ }
+ }
+
+ // last damage from duel opponent
+ if(duel_hasEnded)
+ {
+ assert(pVictim->GetTypeId()==TYPEID_PLAYER);
+ Player *he = (Player*)pVictim;
+
+ assert(he->duel);
+
+ he->SetHealth(1);
+
+ he->duel->opponent->CombatStopWithPets(true);
+ he->CombatStopWithPets(true);
+
+ he->CastSpell(he, 7267, true); // beg
+ he->DuelComplete(DUEL_WON);
+ }
+ }
+
+ DEBUG_LOG("DealDamageEnd returned %d damage", damage);
+
+ return damage;
+}
+
+void Unit::CastStop(uint32 except_spellid)
+{
+ for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
+ if (m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id!=except_spellid)
+ InterruptSpell(i,false);
+}
+
+void Unit::CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
+
+ if(!spellInfo)
+ {
+ sLog.outError("CastSpell: unknown spell id %i by caster: %s %u)", spellId,(GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
+ return;
+ }
+
+ CastSpell(Victim,spellInfo,triggered,castItem,triggredByAura, originalCaster);
+}
+
+void Unit::CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ if(!spellInfo)
+ {
+ sLog.outError("CastSpell: unknown spell by caster: %s %u)", (GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
+ return;
+ }
+
+ if (castItem)
+ DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
+ if(!originalCaster && triggredByAura)
+ originalCaster = triggredByAura->GetCasterGUID();
+
+ Spell *spell = new Spell(this, spellInfo, triggered, originalCaster );
+
+ SpellCastTargets targets;
+ targets.setUnitTarget( Victim );
+ spell->m_CastItem = castItem;
+ spell->prepare(&targets, triggredByAura);
+}
+
+void Unit::CastCustomSpell(Unit* Victim,uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
+
+ if(!spellInfo)
+ {
+ sLog.outError("CastCustomSpell: unknown spell id %i\n", spellId);
+ return;
+ }
+
+ CastCustomSpell(Victim,spellInfo,bp0,bp1,bp2,triggered,castItem,triggredByAura, originalCaster);
+}
+
+void Unit::CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ if(!spellInfo)
+ {
+ sLog.outError("CastCustomSpell: unknown spell");
+ return;
+ }
+
+ if (castItem)
+ DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
+ if(!originalCaster && triggredByAura)
+ originalCaster = triggredByAura->GetCasterGUID();
+
+ Spell *spell = new Spell(this, spellInfo, triggered, originalCaster);
+
+ if(bp0)
+ spell->m_currentBasePoints[0] = *bp0-int32(spellInfo->EffectBaseDice[0]);
+
+ if(bp1)
+ spell->m_currentBasePoints[1] = *bp1-int32(spellInfo->EffectBaseDice[1]);
+
+ if(bp2)
+ spell->m_currentBasePoints[2] = *bp2-int32(spellInfo->EffectBaseDice[2]);
+
+ SpellCastTargets targets;
+ targets.setUnitTarget( Victim );
+ spell->m_CastItem = castItem;
+ spell->prepare(&targets, triggredByAura);
+}
+
+// used for scripting
+void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
+
+ if(!spellInfo)
+ {
+ sLog.outError("CastSpell(x,y,z): unknown spell id %i by caster: %s %u)", spellId,(GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
+ return;
+ }
+
+ CastSpell(x, y, z,spellInfo,triggered,castItem,triggredByAura, originalCaster);
+}
+
+// used for scripting
+void Unit::CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem, Aura* triggredByAura, uint64 originalCaster)
+{
+ if(!spellInfo)
+ {
+ sLog.outError("CastSpell(x,y,z): unknown spell by caster: %s %u)", (GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
+ return;
+ }
+
+ if (castItem)
+ DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
+ if(!originalCaster && triggredByAura)
+ originalCaster = triggredByAura->GetCasterGUID();
+
+ Spell *spell = new Spell(this, spellInfo, triggered, originalCaster );
+
+ SpellCastTargets targets;
+ targets.setDestination(x, y, z);
+ spell->m_CastItem = castItem;
+ spell->prepare(&targets, triggredByAura);
+}
+
+void Unit::DealFlatDamage(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit, bool isTriggeredSpell)
+{
+ // TODO this in only generic way, check for exceptions
+ DEBUG_LOG("DealFlatDamage (BEFORE) >> DMG:%u", *damage);
+
+ // Per-damage calss calculation
+ switch (spellInfo->DmgClass)
+ {
+ // Melee and Ranged Spells
+ case SPELL_DAMAGE_CLASS_RANGED:
+ case SPELL_DAMAGE_CLASS_MELEE:
+ {
+ // Calculate physical outcome
+ MeleeHitOutcome outcome = RollPhysicalOutcomeAgainst(pVictim, BASE_ATTACK, spellInfo);
+
+ //Used to store the Hit Outcome
+ cleanDamage->hitOutCome = outcome;
+
+ // Return miss/evade first (sends miss message)
+ switch(outcome)
+ {
+ case MELEE_HIT_EVADE:
+ {
+ SendAttackStateUpdate(HITINFO_MISS, pVictim, 1, GetSpellSchoolMask(spellInfo), 0, 0,0,VICTIMSTATE_EVADES,0);
+ *damage = 0;
+ return;
+ }
+ case MELEE_HIT_MISS:
+ {
+ SendAttackStateUpdate(HITINFO_MISS, pVictim, 1, GetSpellSchoolMask(spellInfo), 0, 0,0,VICTIMSTATE_NORMAL,0);
+ *damage = 0;
+
+ if(GetTypeId()== TYPEID_PLAYER)
+ ((Player*)this)->UpdateWeaponSkill(BASE_ATTACK);
+
+ CastMeleeProcDamageAndSpell(pVictim,0,GetSpellSchoolMask(spellInfo),BASE_ATTACK,MELEE_HIT_MISS,spellInfo,isTriggeredSpell);
+ return;
+ }
+ }
+
+ // Hitinfo, Victimstate
+ uint32 hitInfo = HITINFO_NORMALSWING;
+ VictimState victimState = VICTIMSTATE_NORMAL;
+
+ // Physical Damage
+ if ( GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_NORMAL )
+ {
+ uint32 modDamage=*damage;
+
+ // apply spellmod to Done damage
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_DAMAGE, *damage);
+
+ //Calculate armor mitigation
+ uint32 damageAfterArmor = CalcArmorReducedDamage(pVictim, *damage);
+
+ // random durability for main hand weapon (ABSORB)
+ if(damageAfterArmor < *damage)
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_ABSORB)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EquipmentSlots(urand(EQUIPMENT_SLOT_START,EQUIPMENT_SLOT_BACK)));
+
+ cleanDamage->damage += *damage - damageAfterArmor;
+ *damage = damageAfterArmor;
+ }
+ // Magical Damage
+ else
+ {
+ // Calculate damage bonus
+ *damage = SpellDamageBonus(pVictim, spellInfo, *damage, SPELL_DIRECT_DAMAGE);
+ }
+
+ // Classify outcome
+ switch (outcome)
+ {
+ case MELEE_HIT_BLOCK_CRIT:
+ case MELEE_HIT_CRIT:
+ {
+ uint32 bonusDmg = *damage;
+
+ // Apply crit_damage bonus
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, bonusDmg);
+
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+ AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS);
+ for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ bonusDmg = uint32(bonusDmg * ((*i)->GetModifier()->m_amount+100.0f)/100.0f);
+
+ *damage += bonusDmg;
+
+ // Resilience - reduce crit damage
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ {
+ uint32 resilienceReduction = ((Player*)pVictim)->GetMeleeCritDamageReduction(*damage);
+ cleanDamage->damage += resilienceReduction;
+ *damage -= resilienceReduction;
+ }
+
+ *crit = true;
+ hitInfo |= HITINFO_CRITICALHIT;
+
+ ModifyAuraState(AURA_STATE_CRIT, true);
+ StartReactiveTimer( REACTIVE_CRIT );
+
+ if(getClass()==CLASS_HUNTER)
+ {
+ ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, true);
+ StartReactiveTimer( REACTIVE_HUNTER_CRIT );
+ }
+
+ if ( outcome == MELEE_HIT_BLOCK_CRIT )
+ {
+ uint32 blocked_amount = uint32(pVictim->GetShieldBlockValue());
+ if (blocked_amount >= *damage)
+ {
+ hitInfo |= HITINFO_SWINGNOHITSOUND;
+ victimState = VICTIMSTATE_BLOCKS;
+ cleanDamage->damage += *damage; // To Help Calculate Rage
+ *damage = 0;
+ }
+ else
+ {
+ // To Help Calculate Rage
+ cleanDamage->damage += blocked_amount;
+ *damage = *damage - blocked_amount;
+ }
+
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update defense
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (BLOCK)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
+ }
+ }
+ break;
+ }
+ case MELEE_HIT_PARRY:
+ {
+ cleanDamage->damage += *damage; // To Help Calculate Rage
+ *damage = 0;
+ victimState = VICTIMSTATE_PARRY;
+
+ // Counter-attack ( explained in Unit::DoAttackDamage() )
+ if(pVictim->GetTypeId()==TYPEID_PLAYER || !(((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN) )
+ {
+ // Get attack timers
+ float offtime = float(pVictim->getAttackTimer(OFF_ATTACK));
+ float basetime = float(pVictim->getAttackTimer(BASE_ATTACK));
+
+ // Reduce attack time
+ if (pVictim->haveOffhandWeapon() && offtime < basetime)
+ {
+ float percent20 = pVictim->GetAttackTime(OFF_ATTACK) * 0.20;
+ float percent60 = 3 * percent20;
+ if(offtime > percent20 && offtime <= percent60)
+ {
+ pVictim->setAttackTimer(OFF_ATTACK, uint32(percent20));
+ }
+ else if(offtime > percent60)
+ {
+ offtime -= 2 * percent20;
+ pVictim->setAttackTimer(OFF_ATTACK, uint32(offtime));
+ }
+ }
+ else
+ {
+ float percent20 = pVictim->GetAttackTime(BASE_ATTACK) * 0.20;
+ float percent60 = 3 * percent20;
+ if(basetime > percent20 && basetime <= percent60)
+ {
+ pVictim->setAttackTimer(BASE_ATTACK, uint32(percent20));
+ }
+ else if(basetime > percent60)
+ {
+ basetime -= 2 * percent20;
+ pVictim->setAttackTimer(BASE_ATTACK, uint32(basetime));
+ }
+ }
+ }
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update victim defense ?
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (PARRY)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_PARRY)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_MAINHAND);
+ }
+
+ // Set parry flags
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ // Mongoose bite - set only Counterattack here
+ if (pVictim->getClass() == CLASS_HUNTER)
+ {
+ pVictim->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true);
+ pVictim->StartReactiveTimer( REACTIVE_HUNTER_PARRY );
+ }
+ else
+ {
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+ break;
+ }
+ case MELEE_HIT_DODGE:
+ {
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)pVictim)->UpdateDefense();
+
+ cleanDamage->damage += *damage; // To Help Calculate Rage
+ *damage = 0;
+ hitInfo |= HITINFO_SWINGNOHITSOUND;
+ victimState = VICTIMSTATE_DODGE;
+
+ // Set dodge flags
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ // Overpower
+ if (GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR)
+ {
+ ((Player*)this)->AddComboPoints(pVictim, 1);
+ StartReactiveTimer( REACTIVE_OVERPOWER );
+ }
+
+ // Riposte
+ if (pVictim->getClass() != CLASS_ROGUE)
+ {
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+ break;
+ }
+ case MELEE_HIT_BLOCK:
+ {
+ uint32 blocked_amount = uint32(pVictim->GetShieldBlockValue());
+ if (blocked_amount >= *damage)
+ {
+ hitInfo |= HITINFO_SWINGNOHITSOUND;
+ victimState = VICTIMSTATE_BLOCKS;
+ cleanDamage->damage += *damage; // To Help Calculate Rage
+ *damage = 0;
+ }
+ else
+ {
+ // To Help Calculate Rage
+ cleanDamage->damage += blocked_amount;
+ *damage = *damage - blocked_amount;
+ }
+
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update defense
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (BLOCK)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
+ }
+ break;
+ }
+ case MELEE_HIT_EVADE: // already processed early
+ case MELEE_HIT_MISS: // already processed early
+ case MELEE_HIT_GLANCING:
+ case MELEE_HIT_CRUSHING:
+ case MELEE_HIT_NORMAL:
+ break;
+ }
+
+ // do all damage=0 cases here
+ if(*damage == 0)
+ CastMeleeProcDamageAndSpell(pVictim,0,GetSpellSchoolMask(spellInfo),BASE_ATTACK,outcome,spellInfo,isTriggeredSpell);
+
+ break;
+ }
+ // Magical Attacks
+ case SPELL_DAMAGE_CLASS_NONE:
+ case SPELL_DAMAGE_CLASS_MAGIC:
+ {
+ // Calculate damage bonus
+ *damage = SpellDamageBonus(pVictim, spellInfo, *damage, SPELL_DIRECT_DAMAGE);
+
+ *crit = isSpellCrit(pVictim, spellInfo, GetSpellSchoolMask(spellInfo), BASE_ATTACK);
+ if (*crit)
+ {
+ *damage = SpellCriticalBonus(spellInfo, *damage, pVictim);
+
+ // Resilience - reduce crit damage
+ if (pVictim && pVictim->GetTypeId()==TYPEID_PLAYER)
+ {
+ uint32 damage_reduction = ((Player *)pVictim)->GetSpellCritDamageReduction(*damage);
+ if(*damage > damage_reduction)
+ *damage -= damage_reduction;
+ else
+ *damage = 0;
+ }
+
+ cleanDamage->hitOutCome = MELEE_HIT_CRIT;
+ }
+ // spell proc all magic damage==0 case in this function
+ if(*damage == 0)
+ {
+ // Procflags
+ uint32 procAttacker = PROC_FLAG_HIT_SPELL;
+ uint32 procVictim = (PROC_FLAG_STRUCK_SPELL|PROC_FLAG_TAKE_DAMAGE);
+
+ ProcDamageAndSpell(pVictim, procAttacker, procVictim, 0, GetSpellSchoolMask(spellInfo), spellInfo, isTriggeredSpell);
+ }
+
+ break;
+ }
+ }
+
+ // TODO this in only generic way, check for exceptions
+ DEBUG_LOG("DealFlatDamage (AFTER) >> DMG:%u", *damage);
+}
+
+uint32 Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool isTriggeredSpell, bool useSpellDamage)
+{
+ if(!this || !pVictim)
+ return 0;
+ if(!this->isAlive() || !pVictim->isAlive())
+ return 0;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
+ if(!spellInfo)
+ return 0;
+
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL);
+ bool crit = false;
+
+ if (useSpellDamage)
+ DealFlatDamage(pVictim, spellInfo, &damage, &cleanDamage, &crit, isTriggeredSpell);
+
+ // If we actually dealt some damage (spell proc's for 0 damage (normal and magic) called in DealFlatDamage)
+ if(damage > 0)
+ {
+ // Calculate absorb & resists
+ uint32 absorb = 0;
+ uint32 resist = 0;
+
+ CalcAbsorbResist(pVictim,GetSpellSchoolMask(spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
+
+ //No more damage left, target absorbed and/or resisted all damage
+ if (damage > absorb + resist)
+ damage -= absorb + resist; //Remove Absorbed and Resisted from damage actually dealt
+ else
+ {
+ uint32 HitInfo = HITINFO_SWINGNOHITSOUND;
+
+ if (absorb)
+ HitInfo |= HITINFO_ABSORB;
+ if (resist)
+ {
+ HitInfo |= HITINFO_RESIST;
+ ProcDamageAndSpell(pVictim, PROC_FLAG_TARGET_RESISTS, PROC_FLAG_RESIST_SPELL, 0, GetSpellSchoolMask(spellInfo), spellInfo,isTriggeredSpell);
+ }
+
+ //Send resist
+ SendAttackStateUpdate(HitInfo, pVictim, 1, GetSpellSchoolMask(spellInfo), damage, absorb,resist,VICTIMSTATE_NORMAL,0);
+ return 0;
+ }
+
+ // Deal damage done
+ damage = DealDamage(pVictim, damage, &cleanDamage, SPELL_DIRECT_DAMAGE, GetSpellSchoolMask(spellInfo), spellInfo, true);
+
+ // Send damage log
+ sLog.outDetail("SpellNonMeleeDamageLog: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u,absorb is %u,resist is %u",
+ GetGUIDLow(), GetTypeId(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, spellID, absorb,resist);
+
+ // Actual log sent to client
+ SendSpellNonMeleeDamageLog(pVictim, spellID, damage, GetSpellSchoolMask(spellInfo), absorb, resist, false, 0, crit);
+
+ // Procflags
+ uint32 procAttacker = PROC_FLAG_HIT_SPELL;
+ uint32 procVictim = (PROC_FLAG_STRUCK_SPELL|PROC_FLAG_TAKE_DAMAGE);
+
+ if (crit)
+ {
+ procAttacker |= PROC_FLAG_CRIT_SPELL;
+ procVictim |= PROC_FLAG_STRUCK_CRIT_SPELL;
+ }
+
+ ProcDamageAndSpell(pVictim, procAttacker, procVictim, damage, GetSpellSchoolMask(spellInfo), spellInfo, isTriggeredSpell);
+
+ return damage;
+ }
+ else
+ {
+ // all spell proc for 0 normal and magic damage called in DealFlatDamage
+
+ //Check for rage
+ if(cleanDamage.damage)
+ // Rage from damage received.
+ if(pVictim->GetTypeId() == TYPEID_PLAYER && (pVictim->getPowerType() == POWER_RAGE))
+ ((Player*)pVictim)->RewardRage(cleanDamage.damage, 0, false);
+
+ return 0;
+ }
+}
+
+void Unit::HandleEmoteCommand(uint32 anim_id)
+{
+ WorldPacket data( SMSG_EMOTE, 12 );
+ data << anim_id << GetGUID();
+ WPAssert(data.size() == 12);
+
+ SendMessageToSet(&data, true);
+}
+
+uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage)
+{
+ uint32 newdamage = 0;
+ float armor = pVictim->GetArmor();
+ // Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura
+ armor += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, SPELL_SCHOOL_MASK_NORMAL);
+
+ if (armor<0.0f) armor=0.0f;
+
+ float tmpvalue = 0.0f;
+ if(getLevel() <= 59) //Level 1-59
+ tmpvalue = armor / (armor + 400.0f + 85.0f * getLevel());
+ else if(getLevel() < 70) //Level 60-69
+ tmpvalue = armor / (armor - 22167.5f + 467.5f * getLevel());
+ else //Level 70+
+ tmpvalue = armor / (armor + 10557.5f);
+
+ if(tmpvalue < 0.0f)
+ tmpvalue = 0.0f;
+ if(tmpvalue > 0.75f)
+ tmpvalue = 0.75f;
+ newdamage = uint32(damage - (damage * tmpvalue));
+
+ return (newdamage > 1) ? newdamage : 1;
+}
+
+void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist)
+{
+ if(!pVictim || !pVictim->isAlive() || !damage)
+ return;
+
+ // Magic damage, check for resists
+ if ((schoolMask & SPELL_SCHOOL_MASK_NORMAL)==0)
+ {
+ // Get base victim resistance for school
+ float tmpvalue2 = (float)pVictim->GetResistance(GetFirstSchoolInMask(schoolMask));
+ // Ignore resistance by self SPELL_AURA_MOD_TARGET_RESISTANCE aura
+ tmpvalue2 += (float)GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask);
+
+ tmpvalue2 *= (float)(0.15f / getLevel());
+ if (tmpvalue2 < 0.0f)
+ tmpvalue2 = 0.0f;
+ if (tmpvalue2 > 0.75f)
+ tmpvalue2 = 0.75f;
+ uint32 ran = urand(0, 100);
+ uint32 faq[4] = {24,6,4,6};
+ uint8 m = 0;
+ float Binom = 0.0f;
+ for (uint8 i = 0; i < 4; i++)
+ {
+ Binom += 2400 *( powf(tmpvalue2, i) * powf( (1-tmpvalue2), (4-i)))/faq[i];
+ if (ran > Binom )
+ ++m;
+ else
+ break;
+ }
+ if (damagetype == DOT && m == 4)
+ *resist += uint32(damage - 1);
+ else
+ *resist += uint32(damage * m / 4);
+ if(*resist > damage)
+ *resist = damage;
+ }
+ else
+ *resist = 0;
+
+ int32 RemainingDamage = damage - *resist;
+
+ // absorb without mana cost
+ int32 reflectDamage = 0;
+ Aura* reflectAura = NULL;
+ AuraList const& vSchoolAbsorb = pVictim->GetAurasByType(SPELL_AURA_SCHOOL_ABSORB);
+ for(AuraList::const_iterator i = vSchoolAbsorb.begin(), next; i != vSchoolAbsorb.end() && RemainingDamage > 0; i = next)
+ {
+ next = i; ++next;
+
+ if (((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
+ continue;
+
+ // Cheat Death
+ if((*i)->GetSpellProto()->SpellFamilyName==SPELLFAMILY_ROGUE && (*i)->GetSpellProto()->SpellIconID == 2109)
+ {
+ if (((Player*)pVictim)->HasSpellCooldown(31231))
+ continue;
+ if (pVictim->GetHealth() <= RemainingDamage)
+ {
+ int32 chance = (*i)->GetModifier()->m_amount;
+ if (roll_chance_i(chance))
+ {
+ pVictim->CastSpell(pVictim,31231,true);
+ ((Player*)pVictim)->AddSpellCooldown(31231,0,time(NULL)+60);
+
+ // with health > 10% lost health until health==10%, in other case no losses
+ uint32 health10 = pVictim->GetMaxHealth()/10;
+ RemainingDamage = pVictim->GetHealth() > health10 ? pVictim->GetHealth() - health10 : 0;
+ }
+ }
+ continue;
+ }
+
+ int32 currentAbsorb;
+
+ //Reflective Shield
+ if ((pVictim != this) && (*i)->GetSpellProto()->SpellFamilyName == SPELLFAMILY_PRIEST && (*i)->GetSpellProto()->SpellFamilyFlags == 0x1)
+ {
+ if(Unit* caster = (*i)->GetCaster())
+ {
+ AuraList const& vOverRideCS = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(AuraList::const_iterator k = vOverRideCS.begin(); k != vOverRideCS.end(); ++k)
+ {
+ switch((*k)->GetModifier()->m_miscvalue)
+ {
+ case 5065: // Rank 1
+ case 5064: // Rank 2
+ case 5063: // Rank 3
+ case 5062: // Rank 4
+ case 5061: // Rank 5
+ {
+ if(RemainingDamage >= (*i)->GetModifier()->m_amount)
+ reflectDamage = (*i)->GetModifier()->m_amount * (*k)->GetModifier()->m_amount/100;
+ else
+ reflectDamage = (*k)->GetModifier()->m_amount * RemainingDamage/100;
+ reflectAura = *i;
+
+ } break;
+ default: break;
+ }
+
+ if(reflectDamage)
+ break;
+ }
+ }
+ }
+
+ if (RemainingDamage >= (*i)->GetModifier()->m_amount)
+ {
+ currentAbsorb = (*i)->GetModifier()->m_amount;
+ pVictim->RemoveAurasDueToSpell((*i)->GetId());
+ next = vSchoolAbsorb.begin();
+ }
+ else
+ {
+ currentAbsorb = RemainingDamage;
+ (*i)->GetModifier()->m_amount -= RemainingDamage;
+ }
+
+ RemainingDamage -= currentAbsorb;
+ }
+ // do not cast spells while looping auras; auras can get invalid otherwise
+ if (reflectDamage)
+ pVictim->CastCustomSpell(this, 33619, &reflectDamage, NULL, NULL, true, NULL, reflectAura);
+
+ // absorb by mana cost
+ AuraList const& vManaShield = pVictim->GetAurasByType(SPELL_AURA_MANA_SHIELD);
+ for(AuraList::const_iterator i = vManaShield.begin(), next; i != vManaShield.end() && RemainingDamage > 0; i = next)
+ {
+ next = i; ++next;
+
+ // check damage school mask
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
+ continue;
+
+ int32 currentAbsorb;
+ if (RemainingDamage >= (*i)->GetModifier()->m_amount)
+ currentAbsorb = (*i)->GetModifier()->m_amount;
+ else
+ currentAbsorb = RemainingDamage;
+
+ float manaMultiplier = (*i)->GetSpellProto()->EffectMultipleValue[(*i)->GetEffIndex()];
+ if(Player *modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod((*i)->GetId(), SPELLMOD_MULTIPLE_VALUE, manaMultiplier);
+
+ int32 maxAbsorb = int32(pVictim->GetPower(POWER_MANA) / manaMultiplier);
+ if (currentAbsorb > maxAbsorb)
+ currentAbsorb = maxAbsorb;
+
+ (*i)->GetModifier()->m_amount -= currentAbsorb;
+ if((*i)->GetModifier()->m_amount <= 0)
+ {
+ pVictim->RemoveAurasDueToSpell((*i)->GetId());
+ next = vManaShield.begin();
+ }
+
+ int32 manaReduction = int32(currentAbsorb * manaMultiplier);
+ pVictim->ApplyPowerMod(POWER_MANA, manaReduction, false);
+
+ RemainingDamage -= currentAbsorb;
+ }
+
+ AuraList const& vSplitDamageFlat = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_FLAT);
+ for(AuraList::const_iterator i = vSplitDamageFlat.begin(), next; i != vSplitDamageFlat.end() && RemainingDamage >= 0; i = next)
+ {
+ next = i; ++next;
+
+ // check damage school mask
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
+ continue;
+
+ // Damage can be splitted only if aura has an alive caster
+ Unit *caster = (*i)->GetCaster();
+ if(!caster || caster == pVictim || !caster->IsInWorld() || !caster->isAlive())
+ continue;
+
+ int32 currentAbsorb;
+ if (RemainingDamage >= (*i)->GetModifier()->m_amount)
+ currentAbsorb = (*i)->GetModifier()->m_amount;
+ else
+ currentAbsorb = RemainingDamage;
+
+ RemainingDamage -= currentAbsorb;
+
+ SendSpellNonMeleeDamageLog(caster, (*i)->GetSpellProto()->Id, currentAbsorb, schoolMask, 0, 0, false, 0, false);
+
+ CleanDamage cleanDamage = CleanDamage(currentAbsorb, BASE_ATTACK, MELEE_HIT_NORMAL);
+ DealDamage(caster, currentAbsorb, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false);
+ }
+
+ AuraList const& vSplitDamagePct = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_PCT);
+ for(AuraList::const_iterator i = vSplitDamagePct.begin(), next; i != vSplitDamagePct.end() && RemainingDamage >= 0; i = next)
+ {
+ next = i; ++next;
+
+ // check damage school mask
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask)==0)
+ continue;
+
+ // Damage can be splitted only if aura has an alive caster
+ Unit *caster = (*i)->GetCaster();
+ if(!caster || caster == pVictim || !caster->IsInWorld() || !caster->isAlive())
+ continue;
+
+ int32 splitted = int32(RemainingDamage * (*i)->GetModifier()->m_amount / 100.0f);
+
+ RemainingDamage -= splitted;
+
+ SendSpellNonMeleeDamageLog(caster, (*i)->GetSpellProto()->Id, splitted, schoolMask, 0, 0, false, 0, false);
+
+ CleanDamage cleanDamage = CleanDamage(splitted, BASE_ATTACK, MELEE_HIT_NORMAL);
+ DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false);
+ }
+
+ *absorb = damage - RemainingDamage - *resist;
+}
+
+void Unit::DoAttackDamage (Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchoolMask damageSchoolMask, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted, bool isTriggeredSpell)
+{
+ MeleeHitOutcome outcome;
+
+ // If is casted Melee spell, calculate like physical
+ if(!spellCasted)
+ outcome = RollMeleeOutcomeAgainst (pVictim, attType);
+ else
+ outcome = RollPhysicalOutcomeAgainst (pVictim, attType, spellCasted);
+
+ if(outcome == MELEE_HIT_MISS ||outcome == MELEE_HIT_DODGE ||outcome == MELEE_HIT_BLOCK ||outcome == MELEE_HIT_PARRY)
+ pVictim->AddThreat(this, 0.0f);
+ switch(outcome)
+ {
+ case MELEE_HIT_EVADE:
+ {
+ *hitInfo |= HITINFO_MISS;
+ *damage = 0;
+ cleanDamage->damage = 0;
+ return;
+ }
+ case MELEE_HIT_MISS:
+ {
+ *hitInfo |= HITINFO_MISS;
+ *damage = 0;
+ cleanDamage->damage = 0;
+ if(GetTypeId()== TYPEID_PLAYER)
+ ((Player*)this)->UpdateWeaponSkill(attType);
+ return;
+ }
+ }
+
+ /// If this is a creature and it attacks from behind it has a probability to daze it's victim
+ if( (outcome==MELEE_HIT_CRIT || outcome==MELEE_HIT_CRUSHING || outcome==MELEE_HIT_NORMAL || outcome==MELEE_HIT_GLANCING) &&
+ GetTypeId() != TYPEID_PLAYER && !((Creature*)this)->GetCharmerOrOwnerGUID() && !pVictim->HasInArc(M_PI, this) )
+ {
+ // -probability is between 0% and 40%
+ // 20% base chance
+ float Probability = 20;
+
+ //there is a newbie protection, at level 10 just 7% base chance; assuming linear function
+ if( pVictim->getLevel() < 30 )
+ Probability = 0.65f*pVictim->getLevel()+0.5;
+
+ uint32 VictimDefense=pVictim->GetDefenseSkillValue(this);
+ uint32 AttackerMeleeSkill=GetUnitMeleeSkill(pVictim);
+
+ Probability *= AttackerMeleeSkill/(float)VictimDefense;
+
+ if(Probability > 40.0f)
+ Probability = 40.0f;
+
+ if(roll_chance_f(Probability))
+ CastSpell(pVictim, 1604, true);
+ }
+
+ //Calculate the damage after armor mitigation if SPELL_SCHOOL_NORMAL
+ if (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL)
+ {
+ uint32 damageAfterArmor = CalcArmorReducedDamage(pVictim, *damage);
+
+ // random durability for main hand weapon (ABSORB)
+ if(damageAfterArmor < *damage)
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_ABSORB)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EquipmentSlots(urand(EQUIPMENT_SLOT_START,EQUIPMENT_SLOT_BACK)));
+
+ cleanDamage->damage += *damage - damageAfterArmor;
+ *damage = damageAfterArmor;
+ }
+
+ if(GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() != CREATURE_TYPE_CRITTER )
+ ((Player*)this)->UpdateCombatSkills(pVictim, attType, outcome, false);
+
+ if(GetTypeId() != TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)pVictim)->UpdateCombatSkills(this, attType, outcome, true);
+
+ switch (outcome)
+ {
+ case MELEE_HIT_BLOCK_CRIT:
+ case MELEE_HIT_CRIT:
+ {
+ //*hitInfo = 0xEA;
+ // 0xEA
+ *hitInfo = HITINFO_CRITICALHIT | HITINFO_NORMALSWING2 | 0x8;
+
+ // Crit bonus calc
+ uint32 crit_bonus;
+ crit_bonus = *damage;
+
+ // Apply crit_damage bonus for melee spells
+ if (spellCasted)
+ {
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellCasted->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
+
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+ AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS);
+ for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ crit_bonus = uint32(crit_bonus * ((*i)->GetModifier()->m_amount+100.0f)/100.0f);
+ }
+
+ *damage += crit_bonus;
+
+ uint32 resilienceReduction = 0;
+
+ if(attType == RANGED_ATTACK)
+ {
+ int32 mod = pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE);
+ *damage = int32((*damage) * float((100.0f + mod)/100.0f));
+ // Resilience - reduce crit damage
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ resilienceReduction = ((Player*)pVictim)->GetRangedCritDamageReduction(*damage);
+ }
+ else
+ {
+ int32 mod = pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE);
+ mod += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE);
+ *damage = int32((*damage) * float((100.0f + mod)/100.0f));
+ // Resilience - reduce crit damage
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ resilienceReduction = ((Player*)pVictim)->GetMeleeCritDamageReduction(*damage);
+ }
+
+ *damage -= resilienceReduction;
+ cleanDamage->damage += resilienceReduction;
+
+ if(GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() != CREATURE_TYPE_CRITTER )
+ ((Player*)this)->UpdateWeaponSkill(attType);
+
+ ModifyAuraState(AURA_STATE_CRIT, true);
+ StartReactiveTimer( REACTIVE_CRIT );
+
+ if(getClass()==CLASS_HUNTER)
+ {
+ ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, true);
+ StartReactiveTimer( REACTIVE_HUNTER_CRIT );
+ }
+
+ if ( outcome == MELEE_HIT_BLOCK_CRIT )
+ {
+ *blocked_amount = pVictim->GetShieldBlockValue();
+
+ if (pVictim->GetUnitBlockChance())
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD);
+ else
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ //Only set VICTIMSTATE_BLOCK on a full block
+ if (*blocked_amount >= uint32(*damage))
+ {
+ *victimState = VICTIMSTATE_BLOCKS;
+ *blocked_amount = uint32(*damage);
+ }
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update defense
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (BLOCK)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
+ }
+
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE,true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+ break;
+ }
+
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_WOUNDCRITICAL);
+ break;
+ }
+ case MELEE_HIT_PARRY:
+ {
+ if(attType == RANGED_ATTACK) //range attack - no parry
+ {
+ outcome = MELEE_HIT_NORMAL;
+ break;
+ }
+
+ cleanDamage->damage += *damage;
+ *damage = 0;
+ *victimState = VICTIMSTATE_PARRY;
+
+ // instant (maybe with small delay) counter attack
+ {
+ float offtime = float(pVictim->getAttackTimer(OFF_ATTACK));
+ float basetime = float(pVictim->getAttackTimer(BASE_ATTACK));
+
+ // after parry nearest next attack time will reduced at %40 from full attack time.
+ // The delay cannot be reduced to less than 20% of your weapon's base swing delay.
+ if (pVictim->haveOffhandWeapon() && offtime < basetime)
+ {
+ float percent20 = pVictim->GetAttackTime(OFF_ATTACK)*0.20;
+ float percent60 = 3*percent20;
+ // set to 20% if in range 20%...20+40% of full time
+ if(offtime > percent20 && offtime <= percent60)
+ {
+ pVictim->setAttackTimer(OFF_ATTACK,uint32(percent20));
+ }
+ // decrease at %40 from full time
+ else if(offtime > percent60)
+ {
+ offtime -= 2*percent20;
+ pVictim->setAttackTimer(OFF_ATTACK,uint32(offtime));
+ }
+ // ELSE not changed
+ }
+ else
+ {
+ float percent20 = pVictim->GetAttackTime(BASE_ATTACK)*0.20;
+ float percent60 = 3*percent20;
+ // set to 20% if in range 20%...20+40% of full time
+ if(basetime > percent20 && basetime <= percent60)
+ {
+ pVictim->setAttackTimer(BASE_ATTACK,uint32(percent20));
+ }
+ // decrease at %40 from full time
+ else if(basetime > percent60)
+ {
+ basetime -= 2*percent20;
+ pVictim->setAttackTimer(BASE_ATTACK,uint32(basetime));
+ }
+ // ELSE not changed
+ }
+ }
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update victim defense ?
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (PARRY)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_PARRY)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_MAINHAND);
+ }
+
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ if (pVictim->getClass() == CLASS_HUNTER)
+ {
+ pVictim->ModifyAuraState(AURA_STATE_HUNTER_PARRY,true);
+ pVictim->StartReactiveTimer( REACTIVE_HUNTER_PARRY );
+ }
+ else
+ {
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+
+ CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
+ return;
+ }
+ case MELEE_HIT_DODGE:
+ {
+ if(attType == RANGED_ATTACK) //range attack - no dodge
+ {
+ outcome = MELEE_HIT_NORMAL;
+ break;
+ }
+
+ cleanDamage->damage += *damage;
+ *damage = 0;
+ *victimState = VICTIMSTATE_DODGE;
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ ((Player*)pVictim)->UpdateDefense();
+
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ if (pVictim->getClass() != CLASS_ROGUE) // Riposte
+ {
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE, true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+
+ // Overpower
+ if (GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR)
+ {
+ ((Player*)this)->AddComboPoints(pVictim, 1);
+ StartReactiveTimer( REACTIVE_OVERPOWER );
+ }
+
+ CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
+ return;
+ }
+ case MELEE_HIT_BLOCK:
+ {
+ *blocked_amount = pVictim->GetShieldBlockValue();
+
+ if (pVictim->GetUnitBlockChance())
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD);
+ else
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYUNARMED);
+
+ //Only set VICTIMSTATE_BLOCK on a full block
+ if (*blocked_amount >= uint32(*damage))
+ {
+ *victimState = VICTIMSTATE_BLOCKS;
+ *blocked_amount = uint32(*damage);
+ }
+
+ if(pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ // Update defense
+ ((Player*)pVictim)->UpdateDefense();
+
+ // random durability for main hand weapon (BLOCK)
+ if (roll_chance_f(sWorld.getRate(RATE_DURABILITY_LOSS_BLOCK)))
+ ((Player*)pVictim)->DurabilityPointLossForEquipSlot(EQUIPMENT_SLOT_OFFHAND);
+ }
+
+ pVictim->ModifyAuraState(AURA_STATE_DEFENSE,true);
+ pVictim->StartReactiveTimer( REACTIVE_DEFENSE );
+
+ break;
+ }
+ case MELEE_HIT_GLANCING:
+ {
+ float reducePercent = 1.0f; //damage factor
+
+ // calculate base values and mods
+ float baseLowEnd = 1.3;
+ float baseHighEnd = 1.2;
+ switch(getClass()) // lowering base values for casters
+ {
+ case CLASS_SHAMAN:
+ case CLASS_PRIEST:
+ case CLASS_MAGE:
+ case CLASS_WARLOCK:
+ case CLASS_DRUID:
+ baseLowEnd -= 0.7;
+ baseHighEnd -= 0.3;
+ break;
+ }
+
+ float maxLowEnd = 0.6;
+ switch(getClass()) // upper for melee classes
+ {
+ case CLASS_WARRIOR:
+ case CLASS_ROGUE:
+ maxLowEnd = 0.91; //If the attacker is a melee class then instead the lower value of 0.91
+ }
+
+ // calculate values
+ int32 diff = int32(pVictim->GetDefenseSkillValue(this)) - int32(GetWeaponSkillValue(attType,pVictim));
+ float lowEnd = baseLowEnd - ( 0.05f * diff );
+ float highEnd = baseHighEnd - ( 0.03f * diff );
+
+ // apply max/min bounds
+ if ( lowEnd < 0.01f ) //the low end must not go bellow 0.01f
+ lowEnd = 0.01f;
+ else if ( lowEnd > maxLowEnd ) //the smaller value of this and 0.6 is kept as the low end
+ lowEnd = maxLowEnd;
+
+ if ( highEnd < 0.2f ) //high end limits
+ highEnd = 0.2f;
+ if ( highEnd > 0.99f )
+ highEnd = 0.99f;
+
+ if(lowEnd > highEnd) // prevent negative range size
+ lowEnd = highEnd;
+
+ reducePercent = lowEnd + rand_norm() * ( highEnd - lowEnd );
+
+ *damage = uint32(reducePercent * *damage);
+ cleanDamage->damage += *damage;
+ *hitInfo |= HITINFO_GLANCING;
+ break;
+ }
+ case MELEE_HIT_CRUSHING:
+ {
+ // 150% normal damage
+ *damage += (*damage / 2);
+ cleanDamage->damage = *damage;
+ *hitInfo |= HITINFO_CRUSHING;
+ // TODO: victimState, victim animation?
+ break;
+ }
+ default:
+ break;
+ }
+
+ // apply melee damage bonus and absorb only if base damage not fully blocked to prevent negative damage or damage with full block
+ if(*victimState != VICTIMSTATE_BLOCKS)
+ {
+ MeleeDamageBonus(pVictim, damage,attType,spellCasted);
+ CalcAbsorbResist(pVictim, damageSchoolMask, DIRECT_DAMAGE, *damage-*blocked_amount, absorbDamage, resistDamage);
+ }
+
+ if (*absorbDamage) *hitInfo |= HITINFO_ABSORB;
+ if (*resistDamage) *hitInfo |= HITINFO_RESIST;
+
+ cleanDamage->damage += *blocked_amount;
+
+ if (*damage <= *absorbDamage + *resistDamage + *blocked_amount)
+ {
+ //*hitInfo = 0x00010020;
+ //*hitInfo |= HITINFO_SWINGNOHITSOUND;
+ //*damageType = 0;
+ CastMeleeProcDamageAndSpell(pVictim, 0, damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
+ return;
+ }
+
+ // update at damage Judgement aura duration that applied by attacker at victim
+ if(*damage)
+ {
+ AuraMap const& vAuras = pVictim->GetAuras();
+ for(AuraMap::const_iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr)
+ {
+ SpellEntry const *spellInfo = (*itr).second->GetSpellProto();
+ if( (spellInfo->AttributesEx3 & 0x40000) && spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN &&
+ ((*itr).second->GetCasterGUID() == GetGUID() && (!spellCasted || spellCasted->Id == 35395)) )
+ {
+ (*itr).second->SetAuraDuration((*itr).second->GetAuraMaxDuration());
+ (*itr).second->UpdateAuraDuration();
+ }
+ }
+ }
+
+ CastMeleeProcDamageAndSpell(pVictim, (*damage - *absorbDamage - *resistDamage - *blocked_amount), damageSchoolMask, attType, outcome, spellCasted, isTriggeredSpell);
+
+ // victim's damage shield
+ // yet another hack to fix crashes related to the aura getting removed during iteration
+ std::set<Aura*> alreadyDone;
+ uint32 removedAuras = pVictim->m_removedAuras;
+ AuraList const& vDamageShields = pVictim->GetAurasByType(SPELL_AURA_DAMAGE_SHIELD);
+ for(AuraList::const_iterator i = vDamageShields.begin(), next = vDamageShields.begin(); i != vDamageShields.end(); i = next)
+ {
+ ++next;
+ if (alreadyDone.find(*i) == alreadyDone.end())
+ {
+ alreadyDone.insert(*i);
+ pVictim->SpellNonMeleeDamageLog(this, (*i)->GetId(), (*i)->GetModifier()->m_amount, false, false);
+ if (pVictim->m_removedAuras > removedAuras)
+ {
+ removedAuras = pVictim->m_removedAuras;
+ next = vDamageShields.begin();
+ }
+ }
+ }
+}
+
+void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool extra )
+{
+ if(hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) )
+ return;
+
+ if (!pVictim->isAlive())
+ return;
+
+ if(IsNonMeleeSpellCasted(false))
+ return;
+
+ uint32 hitInfo;
+ if (attType == BASE_ATTACK)
+ hitInfo = HITINFO_NORMALSWING2;
+ else if (attType == OFF_ATTACK)
+ hitInfo = HITINFO_LEFTSWING;
+ else
+ return; // ignore ranaged case
+
+ uint32 extraAttacks = m_extraAttacks;
+
+ // melee attack spell casted at main hand attack only
+ if (attType == BASE_ATTACK && m_currentSpells[CURRENT_MELEE_SPELL])
+ {
+ m_currentSpells[CURRENT_MELEE_SPELL]->cast();
+
+ // not recent extra attack only at any non extra attack (melee spell case)
+ if(!extra && extraAttacks)
+ {
+ while(m_extraAttacks)
+ {
+ AttackerStateUpdate(pVictim, BASE_ATTACK, true);
+ if(m_extraAttacks > 0)
+ --m_extraAttacks;
+ }
+ }
+
+ return;
+ }
+
+ VictimState victimState = VICTIMSTATE_NORMAL;
+
+ CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
+ uint32 blocked_dmg = 0;
+ uint32 absorbed_dmg = 0;
+ uint32 resisted_dmg = 0;
+
+ SpellSchoolMask meleeSchoolMask = GetMeleeDamageSchoolMask();
+
+ if(pVictim->IsImmunedToDamage(meleeSchoolMask,true)) // use charges
+ {
+ SendAttackStateUpdate (HITINFO_NORMALSWING, pVictim, 1, meleeSchoolMask, 0, 0, 0, VICTIMSTATE_IS_IMMUNE, 0);
+
+ // not recent extra attack only at any non extra attack (miss case)
+ if(!extra && extraAttacks)
+ {
+ while(m_extraAttacks)
+ {
+ AttackerStateUpdate(pVictim, BASE_ATTACK, true);
+ if(m_extraAttacks > 0)
+ --m_extraAttacks;
+ }
+ }
+
+ return;
+ }
+
+ uint32 damage = CalculateDamage (attType, false);
+
+ DoAttackDamage (pVictim, &damage, &cleanDamage, &blocked_dmg, meleeSchoolMask, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, attType);
+
+ if (hitInfo & HITINFO_MISS)
+ //send miss
+ SendAttackStateUpdate (hitInfo, pVictim, 1, meleeSchoolMask, damage, absorbed_dmg, resisted_dmg, victimState, blocked_dmg);
+ else
+ {
+ //do animation
+ SendAttackStateUpdate (hitInfo, pVictim, 1, meleeSchoolMask, damage, absorbed_dmg, resisted_dmg, victimState, blocked_dmg);
+
+ if (damage > (absorbed_dmg + resisted_dmg + blocked_dmg))
+ damage -= (absorbed_dmg + resisted_dmg + blocked_dmg);
+ else
+ damage = 0;
+
+ DealDamage (pVictim, damage, &cleanDamage, DIRECT_DAMAGE, meleeSchoolMask, NULL, true);
+
+ if(GetTypeId() == TYPEID_PLAYER && pVictim->isAlive())
+ {
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ ((Player*)this)->CastItemCombatSpell(((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0,i),pVictim,attType);
+ }
+ }
+
+ if (GetTypeId() == TYPEID_PLAYER)
+ DEBUG_LOG("AttackerStateUpdate: (Player) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
+ GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, absorbed_dmg, blocked_dmg, resisted_dmg);
+ else
+ DEBUG_LOG("AttackerStateUpdate: (NPC) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
+ GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, absorbed_dmg, blocked_dmg, resisted_dmg);
+
+ // extra attack only at any non extra attack (normal case)
+ if(!extra && extraAttacks)
+ {
+ while(m_extraAttacks)
+ {
+ AttackerStateUpdate(pVictim, BASE_ATTACK, true);
+ if(m_extraAttacks > 0)
+ --m_extraAttacks;
+ }
+ }
+}
+
+MeleeHitOutcome Unit::RollPhysicalOutcomeAgainst (Unit const *pVictim, WeaponAttackType attType, SpellEntry const *spellInfo)
+{
+ // Miss chance based on melee
+ float miss_chance = MeleeMissChanceCalc(pVictim, attType);
+
+ // Critical hit chance
+ float crit_chance = GetUnitCriticalChance(attType, pVictim);
+ // this is to avoid compiler issue when declaring variables inside if
+ float block_chance, parry_chance, dodge_chance;
+
+ // cannot be dodged/parried/blocked
+ if(spellInfo->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK)
+ {
+ block_chance = 0.0f;
+ parry_chance = 0.0f;
+ dodge_chance = 0.0f;
+ }
+ else
+ {
+ // parry can be avoided only by some abilites
+ parry_chance = pVictim->GetUnitParryChance();
+ // block might be bypassed by it as well
+ block_chance = pVictim->GetUnitBlockChance();
+ // stunned target cannot dodge and this is check in GetUnitDodgeChance()
+ dodge_chance = pVictim->GetUnitDodgeChance();
+ }
+
+ // Only players can have Talent&Spell bonuses
+ if (GetTypeId() == TYPEID_PLAYER)
+ {
+ // Increase from SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL aura
+ crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, spellInfo->SchoolMask);
+
+ if( dodge_chance != 0.0f ) // if dodge chance is already 0, ignore talents fpr speed
+ {
+ AuraList const& mCanNotBeDodge = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT);
+ for(AuraList::const_iterator i = mCanNotBeDodge.begin(); i != mCanNotBeDodge.end(); ++i)
+ {
+ // can't be dodged rogue finishing move
+ if((*i)->GetModifier()->m_miscvalue == VICTIMSTATE_DODGE)
+ {
+ if(spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE))
+ {
+ dodge_chance = 0.0f;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Spellmods
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance);
+
+ DEBUG_LOG("PHYSICAL OUTCOME: miss %f crit %f dodge %f parry %f block %f",miss_chance,crit_chance,dodge_chance,parry_chance, block_chance);
+
+ return RollMeleeOutcomeAgainst(pVictim, attType, int32(crit_chance*100), int32(miss_chance*100),int32(dodge_chance*100),int32(parry_chance*100),int32(block_chance*100), true);
+}
+
+MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit *pVictim, WeaponAttackType attType) const
+{
+ // This is only wrapper
+
+ // Miss chance based on melee
+ float miss_chance = MeleeMissChanceCalc(pVictim, attType);
+
+ // Critical hit chance
+ float crit_chance = GetUnitCriticalChance(attType, pVictim);
+
+ // stunned target cannot dodge and this is check in GetUnitDodgeChance() (returned 0 in this case)
+ float dodge_chance = pVictim->GetUnitDodgeChance();
+ float block_chance = pVictim->GetUnitBlockChance();
+ float parry_chance = pVictim->GetUnitParryChance();
+
+ // Useful if want to specify crit & miss chances for melee, else it could be removed
+ DEBUG_LOG("MELEE OUTCOME: miss %f crit %f dodge %f parry %f block %f", miss_chance,crit_chance,dodge_chance,parry_chance,block_chance);
+
+ return RollMeleeOutcomeAgainst(pVictim, attType, int32(crit_chance*100), int32(miss_chance*100), int32(dodge_chance*100),int32(parry_chance*100),int32(block_chance*100), false);
+}
+
+MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance, bool SpellCasted ) const
+{
+ if(pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
+ return MELEE_HIT_EVADE;
+
+ int32 attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(pVictim);
+ int32 victimMaxSkillValueForLevel = pVictim->GetMaxSkillValueForLevel(this);
+
+ int32 attackerWeaponSkill = GetWeaponSkillValue(attType,pVictim);
+ int32 victimDefenseSkill = pVictim->GetDefenseSkillValue(this);
+
+ // bonus from skills is 0.04%
+ int32 skillBonus = 4 * ( attackerWeaponSkill - victimMaxSkillValueForLevel );
+ int32 skillBonus2 = 4 * ( attackerMaxSkillValueForLevel - victimDefenseSkill );
+ int32 sum = 0, tmp = 0;
+ int32 roll = urand (0, 10000);
+
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: skill bonus of %d for attacker", skillBonus);
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: rolled %d, miss %d, dodge %d, parry %d, block %d, crit %d",
+ roll, miss_chance, dodge_chance, parry_chance, block_chance, crit_chance);
+
+ tmp = miss_chance;
+
+ if (tmp > 0 && roll < (sum += tmp ))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: MISS");
+ return MELEE_HIT_MISS;
+ }
+
+ // always crit against a sitting target (except 0 crit chance)
+ if( pVictim->GetTypeId() == TYPEID_PLAYER && crit_chance > 0 && !pVictim->IsStandState() )
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: CRIT (sitting victim)");
+ return MELEE_HIT_CRIT;
+ }
+
+ // Dodge chance
+
+ // only players can't dodge if attacker is behind
+ if (pVictim->GetTypeId() == TYPEID_PLAYER && !pVictim->HasInArc(M_PI,this))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: attack came from behind and victim was a player.");
+ }
+ else
+ {
+ // Reduce dodge chance by attacker expertise rating
+ if (GetTypeId() == TYPEID_PLAYER)
+ dodge_chance -= int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType)*100);
+
+ // Modify dodge chance by attacker SPELL_AURA_MOD_COMBAT_RESULT_CHANCE
+ dodge_chance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE);
+
+ tmp = dodge_chance;
+ if ( (tmp > 0) // check if unit _can_ dodge
+ && ((tmp -= skillBonus) > 0)
+ && roll < (sum += tmp))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: DODGE <%d, %d)", sum-tmp, sum);
+ return MELEE_HIT_DODGE;
+ }
+ }
+
+ // parry & block chances
+
+ // check if attack comes from behind, nobody can parry or block if attacker is behind
+ if (!pVictim->HasInArc(M_PI,this))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: attack came from behind.");
+ }
+ else
+ {
+ // Reduce parry chance by attacker expertise rating
+ if (GetTypeId() == TYPEID_PLAYER)
+ parry_chance-= int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType)*100);
+
+ if(pVictim->GetTypeId()==TYPEID_PLAYER || !(((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY) )
+ {
+ int32 tmp = int32(parry_chance);
+ if ( (tmp > 0) // check if unit _can_ parry
+ && ((tmp -= skillBonus) > 0)
+ && (roll < (sum += tmp)))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: PARRY <%d, %d)", sum-tmp, sum);
+ return MELEE_HIT_PARRY;
+ }
+ }
+
+ if(pVictim->GetTypeId()==TYPEID_PLAYER || !(((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK) )
+ {
+ tmp = block_chance;
+ if ( (tmp > 0) // check if unit _can_ block
+ && ((tmp -= skillBonus) > 0)
+ && (roll < (sum += tmp)))
+ {
+ // Critical chance
+ tmp = crit_chance + skillBonus2;
+ if ( GetTypeId() == TYPEID_PLAYER && SpellCasted && tmp > 0 )
+ {
+ if ( roll_chance_i(tmp/100))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: BLOCKED CRIT");
+ return MELEE_HIT_BLOCK_CRIT;
+ }
+ }
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: BLOCK <%d, %d)", sum-tmp, sum);
+ return MELEE_HIT_BLOCK;
+ }
+ }
+ }
+
+ // Critical chance
+ tmp = crit_chance + skillBonus2;
+
+ if (tmp > 0 && roll < (sum += tmp))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: CRIT <%d, %d)", sum-tmp, sum);
+ return MELEE_HIT_CRIT;
+ }
+
+ // Max 40% chance to score a glancing blow against mobs that are higher level (can do only players and pets and not with ranged weapon)
+ if( attType != RANGED_ATTACK && !SpellCasted &&
+ (GetTypeId() == TYPEID_PLAYER || ((Creature*)this)->isPet()) &&
+ pVictim->GetTypeId() != TYPEID_PLAYER && !((Creature*)pVictim)->isPet() &&
+ getLevel() < pVictim->getLevelForTarget(this) )
+ {
+ // cap possible value (with bonuses > max skill)
+ int32 skill = attackerWeaponSkill;
+ int32 maxskill = attackerMaxSkillValueForLevel;
+ skill = (skill > maxskill) ? maxskill : skill;
+
+ tmp = (10 + (victimDefenseSkill - skill)) * 100;
+ tmp = tmp > 4000 ? 4000 : tmp;
+ if (roll < (sum += tmp))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: GLANCING <%d, %d)", sum-4000, sum);
+ return MELEE_HIT_GLANCING;
+ }
+ }
+
+ if(GetTypeId()!=TYPEID_PLAYER && !(((Creature*)this)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRUSH) && !((Creature*)this)->isPet() )
+ {
+ // mobs can score crushing blows if they're 3 or more levels above victim
+ // or when their weapon skill is 15 or more above victim's defense skill
+ tmp = victimDefenseSkill;
+ int32 tmpmax = victimMaxSkillValueForLevel;
+ // having defense above your maximum (from items, talents etc.) has no effect
+ tmp = tmp > tmpmax ? tmpmax : tmp;
+ // tmp = mob's level * 5 - player's current defense skill
+ tmp = attackerMaxSkillValueForLevel - tmp;
+ if(tmp >= 15)
+ {
+ // add 2% chance per lacking skill point, min. is 15%
+ tmp = tmp * 200 - 1500;
+ if (roll < (sum += tmp))
+ {
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: CRUSHING <%d, %d)", sum-tmp, sum);
+ return MELEE_HIT_CRUSHING;
+ }
+ }
+ }
+
+ DEBUG_LOG ("RollMeleeOutcomeAgainst: NORMAL");
+ return MELEE_HIT_NORMAL;
+}
+
+uint32 Unit::CalculateDamage (WeaponAttackType attType, bool normalized)
+{
+ float min_damage, max_damage;
+
+ if (normalized && GetTypeId()==TYPEID_PLAYER)
+ ((Player*)this)->CalculateMinMaxDamage(attType,normalized,min_damage, max_damage);
+ else
+ {
+ switch (attType)
+ {
+ case RANGED_ATTACK:
+ min_damage = GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE);
+ max_damage = GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE);
+ break;
+ case BASE_ATTACK:
+ min_damage = GetFloatValue(UNIT_FIELD_MINDAMAGE);
+ max_damage = GetFloatValue(UNIT_FIELD_MAXDAMAGE);
+ break;
+ case OFF_ATTACK:
+ min_damage = GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE);
+ max_damage = GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE);
+ break;
+ // Just for good manner
+ default:
+ min_damage = 0.0f;
+ max_damage = 0.0f;
+ break;
+ }
+ }
+
+ if (min_damage > max_damage)
+ {
+ std::swap(min_damage,max_damage);
+ }
+
+ if(max_damage == 0.0f)
+ max_damage = 5.0f;
+
+ return urand((uint32)min_damage, (uint32)max_damage);
+}
+
+float Unit::CalculateLevelPenalty(SpellEntry const* spellProto) const
+{
+ if(spellProto->spellLevel <= 0)
+ return 1.0f;
+
+ float LvlPenalty = 0.0f;
+
+ if(spellProto->spellLevel < 20)
+ LvlPenalty = 20.0f - spellProto->spellLevel * 3.75f;
+ float LvlFactor = (float(spellProto->spellLevel) + 6.0f) / float(getLevel());
+ if(LvlFactor > 1.0f)
+ LvlFactor = 1.0f;
+
+ return (100.0f - LvlPenalty) * LvlFactor / 100.0f;
+}
+
+void Unit::SendAttackStart(Unit* pVictim)
+{
+ WorldPacket data( SMSG_ATTACKSTART, 16 );
+ data << GetGUID();
+ data << pVictim->GetGUID();
+
+ SendMessageToSet(&data, true);
+ DEBUG_LOG( "WORLD: Sent SMSG_ATTACKSTART" );
+}
+
+void Unit::SendAttackStop(Unit* victim)
+{
+ if(!victim)
+ return;
+
+ WorldPacket data( SMSG_ATTACKSTOP, (4+16) ); // we guess size
+ data.append(GetPackGUID());
+ data.append(victim->GetPackGUID()); // can be 0x00...
+ data << uint32(0); // can be 0x1
+ SendMessageToSet(&data, true);
+ sLog.outDetail("%s %u stopped attacking %s %u", (GetTypeId()==TYPEID_PLAYER ? "player" : "creature"), GetGUIDLow(), (victim->GetTypeId()==TYPEID_PLAYER ? "player" : "creature"),victim->GetGUIDLow());
+
+ /*if(victim->GetTypeId() == TYPEID_UNIT)
+ ((Creature*)victim)->AI().EnterEvadeMode(this);*/
+}
+
+/*
+// Melee based spells can be miss, parry or dodge on this step
+// Crit or block - determined on damage calculation phase! (and can be both in some time)
+float Unit::MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell)
+{
+ // Calculate hit chance (more correct for chance mod)
+ int32 HitChance;
+
+ // PvP - PvE melee chances
+ int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 5 : 7;
+ int32 leveldif = pVictim->getLevelForTarget(this) - getLevelForTarget(pVictim);
+ if(leveldif < 3)
+ HitChance = 95 - leveldif;
+ else
+ HitChance = 93 - (leveldif - 2) * lchance;
+
+ // Hit chance depends from victim auras
+ if(attType == RANGED_ATTACK)
+ HitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE);
+ else
+ HitChance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE);
+
+ // Spellmod from SPELLMOD_RESIST_MISS_CHANCE
+ if(Player *modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, HitChance);
+
+ // Miss = 100 - hit
+ float miss_chance= 100.0f - HitChance;
+
+ // Bonuses from attacker aura and ratings
+ if (attType == RANGED_ATTACK)
+ miss_chance -= m_modRangedHitChance;
+ else
+ miss_chance -= m_modMeleeHitChance;
+
+ // bonus from skills is 0.04%
+ miss_chance -= skillDiff * 0.04f;
+
+ // Limit miss chance from 0 to 60%
+ if (miss_chance < 0.0f)
+ return 0.0f;
+ if (miss_chance > 60.0f)
+ return 60.0f;
+ return miss_chance;
+}
+
+// Melee based spells hit result calculations
+SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell)
+{
+ WeaponAttackType attType = BASE_ATTACK;
+
+ if (spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED)
+ attType = RANGED_ATTACK;
+
+ // bonus from skills is 0.04% per skill Diff
+ int32 attackerWeaponSkill = int32(GetWeaponSkillValue(attType,pVictim));
+ int32 skillDiff = attackerWeaponSkill - int32(pVictim->GetMaxSkillValueForLevel(this));
+ int32 fullSkillDiff = attackerWeaponSkill - int32(pVictim->GetDefenseSkillValue(this));
+
+ uint32 roll = urand (0, 10000);
+ uint32 missChance = uint32(MeleeSpellMissChance(pVictim, attType, fullSkillDiff, spell)*100.0f);
+
+ // Roll miss
+ uint32 tmp = missChance;
+ if (roll < tmp)
+ return SPELL_MISS_MISS;
+
+ // Same spells cannot be parry/dodge
+ if (spell->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK)
+ return SPELL_MISS_NONE;
+
+ // Ranged attack can`t miss too
+ if (attType == RANGED_ATTACK)
+ return SPELL_MISS_NONE;
+
+ bool attackFromBehind = !pVictim->HasInArc(M_PI,this);
+
+ // Roll dodge
+ int32 dodgeChance = int32(pVictim->GetUnitDodgeChance()*100.0f) - skillDiff * 4;
+ // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE
+ dodgeChance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE);
+
+ // Reduce dodge chance by attacker expertise rating
+ if (GetTypeId() == TYPEID_PLAYER)
+ dodgeChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f);
+ if (dodgeChance < 0)
+ dodgeChance = 0;
+
+ // Can`t dodge from behind in PvP (but its possible in PvE)
+ if (GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER && attackFromBehind)
+ dodgeChance = 0;
+
+ // Rogue talent`s cant be dodged
+ AuraList const& mCanNotBeDodge = GetAurasByType(SPELL_AURA_IGNORE_COMBAT_RESULT);
+ for(AuraList::const_iterator i = mCanNotBeDodge.begin(); i != mCanNotBeDodge.end(); ++i)
+ {
+ if((*i)->GetModifier()->m_miscvalue == VICTIMSTATE_DODGE) // can't be dodged rogue finishing move
+ {
+ if(spell->SpellFamilyName==SPELLFAMILY_ROGUE && (spell->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE))
+ {
+ dodgeChance = 0;
+ break;
+ }
+ }
+ }
+
+ tmp += dodgeChance;
+ if (roll < tmp)
+ return SPELL_MISS_DODGE;
+
+ // Roll parry
+ int32 parryChance = int32(pVictim->GetUnitParryChance()*100.0f) - skillDiff * 4;
+ // Reduce parry chance by attacker expertise rating
+ if (GetTypeId() == TYPEID_PLAYER)
+ parryChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f);
+ // Can`t parry from behind
+ if (parryChance < 0 || attackFromBehind)
+ parryChance = 0;
+
+ tmp += parryChance;
+ if (roll < tmp)
+ return SPELL_MISS_PARRY;
+
+ return SPELL_MISS_NONE;
+}*/
+
+// TODO need use unit spell resistances in calculations
+SpellMissInfo Unit::MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell)
+{
+ // Can`t miss on dead target (on skinning for example)
+ if (!pVictim->isAlive())
+ return SPELL_MISS_NONE;
+
+ SpellSchoolMask schoolMask = GetSpellSchoolMask(spell);
+ // PvP - PvE spell misschances per leveldif > 2
+ int32 lchance = pVictim->GetTypeId() == TYPEID_PLAYER ? 7 : 11;
+ int32 leveldif = int32(pVictim->getLevelForTarget(this)) - int32(getLevelForTarget(pVictim));
+
+ // Base hit chance from attacker and victim levels
+ int32 modHitChance;
+ if(leveldif < 3)
+ modHitChance = 96 - leveldif;
+ else
+ modHitChance = 94 - (leveldif - 2) * lchance;
+
+ // Spellmod from SPELLMOD_RESIST_MISS_CHANCE
+ if(Player *modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spell->Id, SPELLMOD_RESIST_MISS_CHANCE, modHitChance);
+ // Increase from attacker SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT auras
+ modHitChance+=GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT, schoolMask);
+ // Chance hit from victim SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE auras
+ modHitChance+= pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE, schoolMask);
+ // Reduce spell hit chance for Area of effect spells from victim SPELL_AURA_MOD_AOE_AVOIDANCE aura
+ if (IsAreaOfEffectSpell(spell))
+ modHitChance-=pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_AOE_AVOIDANCE);
+ // Reduce spell hit chance for dispel mechanic spells from victim SPELL_AURA_MOD_DISPEL_RESIST
+ if (IsDispelSpell(spell))
+ modHitChance-=pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_DISPEL_RESIST);
+ // Chance resist mechanic (select max value from every mechanic spell effect)
+ int32 resist_mech = 0;
+ // Get effects mechanic and chance
+ for(int eff = 0; eff < 3; ++eff)
+ {
+ int32 effect_mech = GetEffectMechanic(spell, eff);
+ if (effect_mech)
+ {
+ int32 temp = pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, effect_mech);
+ if (resist_mech < temp)
+ resist_mech = temp;
+ }
+ }
+ // Apply mod
+ modHitChance-=resist_mech;
+
+ // Chance resist debuff
+ modHitChance-=pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(spell->Dispel));
+
+ int32 HitChance = modHitChance * 100;
+ // Increase hit chance from attacker SPELL_AURA_MOD_SPELL_HIT_CHANCE and attacker ratings
+ HitChance += int32(m_modSpellHitChance*100.0f);
+
+ // Decrease hit chance from victim rating bonus
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ HitChance -= int32(((Player*)pVictim)->GetRatingBonusValue(CR_HIT_TAKEN_SPELL)*100.0f);
+
+ if (HitChance < 100) HitChance = 100;
+ if (HitChance > 9900) HitChance = 9900;
+
+ uint32 rand = urand(0,10000);
+ if (rand > HitChance)
+ return SPELL_MISS_RESIST;
+ return SPELL_MISS_NONE;
+}
+
+SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool CanReflect)
+{
+ // Return evade for units in evade mode
+ if (pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
+ return SPELL_MISS_EVADE;
+
+ // Check for immune (use charges)
+ if (pVictim->IsImmunedToSpell(spell,true))
+ return SPELL_MISS_IMMUNE;
+
+ // All positive spells can`t miss
+ // TODO: client not show miss log for this spells - so need find info for this in dbc and use it!
+ if (IsPositiveSpell(spell->Id))
+ return SPELL_MISS_NONE;
+
+ // Check for immune (use charges)
+ if (pVictim->IsImmunedToDamage(GetSpellSchoolMask(spell),true))
+ return SPELL_MISS_IMMUNE;
+
+ // Try victim reflect spell
+ if (CanReflect)
+ {
+ // specialized first
+ Unit::AuraList const& mReflectSpellsSchool = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL);
+ for(Unit::AuraList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i)
+ {
+ if((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spell))
+ {
+ int32 reflectchance = (*i)->GetModifier()->m_amount;
+ if (reflectchance > 0 && roll_chance_i(reflectchance))
+ {
+ if((*i)->m_procCharges > 0)
+ {
+ --(*i)->m_procCharges;
+ if((*i)->m_procCharges==0)
+ pVictim->RemoveAurasDueToSpell((*i)->GetId());
+ }
+ return SPELL_MISS_REFLECT;
+ }
+ }
+ }
+
+ // generic reflection
+ Unit::AuraList const& mReflectSpells = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS);
+ for(Unit::AuraList::const_iterator i = mReflectSpells.begin(); i != mReflectSpells.end(); ++i)
+ {
+ int32 reflectchance = (*i)->GetModifier()->m_amount;
+ if (reflectchance > 0 && roll_chance_i(reflectchance))
+ {
+ if((*i)->m_procCharges > 0)
+ {
+ --(*i)->m_procCharges;
+ if((*i)->m_procCharges==0)
+ pVictim->RemoveAurasDueToSpell((*i)->GetId());
+ }
+ return SPELL_MISS_REFLECT;
+ }
+ }
+ }
+
+ // Temporary solution for melee based spells and spells vs SPELL_SCHOOL_NORMAL (hit result calculated after)
+ for (int i=0;i<3;i++)
+ {
+ if (spell->Effect[i] == SPELL_EFFECT_WEAPON_DAMAGE ||
+ spell->Effect[i] == SPELL_EFFECT_WEAPON_PERCENT_DAMAGE ||
+ spell->Effect[i] == SPELL_EFFECT_NORMALIZED_WEAPON_DMG ||
+ spell->Effect[i] == SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL)
+ return SPELL_MISS_NONE;
+ }
+
+ // TODO need use this code for spell hit result calculation
+ // now code commented for compotability
+ switch (spell->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_RANGED:
+ case SPELL_DAMAGE_CLASS_MELEE:
+// return MeleeSpellHitResult(pVictim, spell);
+ return SPELL_MISS_NONE;
+ case SPELL_DAMAGE_CLASS_NONE:
+ case SPELL_DAMAGE_CLASS_MAGIC:
+ return MagicSpellHitResult(pVictim, spell);
+ }
+ return SPELL_MISS_NONE;
+}
+
+float Unit::MeleeMissChanceCalc(const Unit *pVictim, WeaponAttackType attType) const
+{
+ if(!pVictim)
+ return 0.0f;
+
+ // Base misschance 5%
+ float misschance = 5.0f;
+
+ // DualWield - Melee spells and physical dmg spells - 5% , white damage 24%
+ if (haveOffhandWeapon() && attType != RANGED_ATTACK)
+ {
+ bool isNormal = false;
+ for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
+ {
+ if( m_currentSpells[i] && (GetSpellSchoolMask(m_currentSpells[i]->m_spellInfo) & SPELL_SCHOOL_MASK_NORMAL) )
+ {
+ isNormal = true;
+ break;
+ }
+ }
+ if (isNormal || m_currentSpells[CURRENT_MELEE_SPELL])
+ {
+ misschance = 5.0f;
+ }
+ else
+ {
+ misschance = 24.0f;
+ }
+ }
+
+ // PvP : PvE melee misschances per leveldif > 2
+ int32 chance = pVictim->GetTypeId() == TYPEID_PLAYER ? 5 : 7;
+
+ int32 leveldif = int32(pVictim->getLevelForTarget(this)) - int32(getLevelForTarget(pVictim));
+ if(leveldif < 0)
+ leveldif = 0;
+
+ // Hit chance from attacker based on ratings and auras
+ float m_modHitChance;
+ if (attType == RANGED_ATTACK)
+ m_modHitChance = m_modRangedHitChance;
+ else
+ m_modHitChance = m_modMeleeHitChance;
+
+ if(leveldif < 3)
+ misschance += (leveldif - m_modHitChance);
+ else
+ misschance += ((leveldif - 2) * chance - m_modHitChance);
+
+ // Hit chance for victim based on ratings
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ {
+ if (attType == RANGED_ATTACK)
+ misschance += ((Player*)pVictim)->GetRatingBonusValue(CR_HIT_TAKEN_RANGED);
+ else
+ misschance += ((Player*)pVictim)->GetRatingBonusValue(CR_HIT_TAKEN_MELEE);
+ }
+
+ // Modify miss chance by victim auras
+ if(attType == RANGED_ATTACK)
+ misschance -= pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE);
+ else
+ misschance -= pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE);
+
+ // Modify miss chance from skill difference ( bonus from skills is 0.04% )
+ int32 skillBonus = int32(GetWeaponSkillValue(attType,pVictim)) - int32(pVictim->GetDefenseSkillValue(this));
+ misschance -= skillBonus * 0.04f;
+
+ // Limit miss chance from 0 to 60%
+ if ( misschance < 0.0f)
+ return 0.0f;
+ if ( misschance > 60.0f)
+ return 60.0f;
+
+ return misschance;
+}
+
+uint32 Unit::GetDefenseSkillValue(Unit const* target) const
+{
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ // in PvP use full skill instead current skill value
+ uint32 value = (target && target->GetTypeId() == TYPEID_PLAYER)
+ ? ((Player*)this)->GetMaxSkillValue(SKILL_DEFENSE)
+ : ((Player*)this)->GetSkillValue(SKILL_DEFENSE);
+ value += uint32(((Player*)this)->GetRatingBonusValue(CR_DEFENSE_SKILL));
+ return value;
+ }
+ else
+ return GetUnitMeleeSkill(target);
+}
+
+float Unit::GetUnitDodgeChance() const
+{
+ if(hasUnitState(UNIT_STAT_STUNNED))
+ return 0.0f;
+ if( GetTypeId() == TYPEID_PLAYER )
+ return GetFloatValue(PLAYER_DODGE_PERCENTAGE);
+ else
+ {
+ if(((Creature const*)this)->isTotem())
+ return 0.0f;
+ else
+ {
+ float dodge = 5.0f;
+ dodge += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
+ return dodge > 0.0f ? dodge : 0.0f;
+ }
+ }
+}
+
+float Unit::GetUnitParryChance() const
+{
+ if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNNED))
+ return 0.0f;
+
+ float chance = 0.0f;
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ Player const* player = (Player const*)this;
+ if(player->CanParry() )
+ {
+ Item *tmpitem = player->GetWeaponForAttack(BASE_ATTACK,true);
+ if(!tmpitem)
+ tmpitem = player->GetWeaponForAttack(OFF_ATTACK,true);
+
+ if(tmpitem)
+ chance = GetFloatValue(PLAYER_PARRY_PERCENTAGE);
+ }
+ }
+ else if(GetTypeId() == TYPEID_UNIT)
+ {
+ if(GetCreatureType() == CREATURE_TYPE_HUMANOID)
+ {
+ chance = 5.0f;
+ chance += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
+ }
+ }
+
+ return chance > 0.0f ? chance : 0.0f;
+}
+
+float Unit::GetUnitBlockChance() const
+{
+ if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNNED))
+ return 0.0f;
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ Player const* player = (Player const*)this;
+ if(player->CanBlock() )
+ {
+ Item *tmpitem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
+ if(tmpitem && !tmpitem->IsBroken() && tmpitem->GetProto()->Block)
+ return GetFloatValue(PLAYER_BLOCK_PERCENTAGE);
+ }
+ // is player but has no block ability or no not broken shield equiped
+ return 0.0f;
+ }
+ else
+ {
+ if(((Creature const*)this)->isTotem())
+ return 0.0f;
+ else
+ {
+ float block = 5.0f;
+ block += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT);
+ return block > 0.0f ? block : 0.0f;
+ }
+ }
+}
+
+float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVictim) const
+{
+ float crit;
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ switch(attackType)
+ {
+ case BASE_ATTACK:
+ crit = GetFloatValue( PLAYER_CRIT_PERCENTAGE );
+ break;
+ case OFF_ATTACK:
+ crit = GetFloatValue( PLAYER_OFFHAND_CRIT_PERCENTAGE );
+ break;
+ case RANGED_ATTACK:
+ crit = GetFloatValue( PLAYER_RANGED_CRIT_PERCENTAGE );
+ break;
+ // Just for good manner
+ default:
+ crit = 0.0f;
+ break;
+ }
+ }
+ else
+ {
+ crit = 5.0f;
+ crit += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PERCENT);
+ }
+
+ // flat aura mods
+ if(attackType == RANGED_ATTACK)
+ crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE);
+ else
+ crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE);
+
+ crit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
+
+ // reduce crit chance from Rating for players
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ {
+ if (attackType==RANGED_ATTACK)
+ crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_RANGED);
+ else
+ crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE);
+ }
+
+ if (crit < 0.0f)
+ crit = 0.0f;
+ return crit;
+}
+
+uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) const
+{
+ uint32 value = 0;
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ Item* item = ((Player*)this)->GetWeaponForAttack(attType,true);
+
+ // feral or unarmed skill only for base attack
+ if(attType != BASE_ATTACK && !item )
+ return 0;
+
+ if(((Player*)this)->IsInFeralForm())
+ return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact
+
+ // weaon skill or (unarmed for base attack)
+ uint32 skill = item ? item->GetSkill() : SKILL_UNARMED;
+
+ // in PvP use full skill instead current skill value
+ value = (target && target->GetTypeId() == TYPEID_PLAYER)
+ ? ((Player*)this)->GetMaxSkillValue(skill)
+ : ((Player*)this)->GetSkillValue(skill);
+ // Modify value from ratings
+ value += uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL));
+ switch (attType)
+ {
+ case BASE_ATTACK: value+=uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL_MAINHAND));break;
+ case OFF_ATTACK: value+=uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL_OFFHAND));break;
+ case RANGED_ATTACK: value+=uint32(((Player*)this)->GetRatingBonusValue(CR_WEAPON_SKILL_RANGED));break;
+ }
+ }
+ else
+ value = GetUnitMeleeSkill(target);
+ return value;
+}
+
+void Unit::_UpdateSpells( uint32 time )
+{
+ if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
+ _UpdateAutoRepeatSpell();
+
+ // remove finished spells from current pointers
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ {
+ if (m_currentSpells[i] && m_currentSpells[i]->getState() == SPELL_STATE_FINISHED)
+ {
+ m_currentSpells[i]->SetDeletable(true); // spell may be safely deleted now
+ m_currentSpells[i] = NULL; // remove pointer
+ }
+ }
+
+ // TODO: Find a better way to prevent crash when multiple auras are removed.
+ m_removedAuras = 0;
+ for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
+ if ((*i).second)
+ (*i).second->SetUpdated(false);
+
+ for (AuraMap::iterator i = m_Auras.begin(), next; i != m_Auras.end(); i = next)
+ {
+ next = i;
+ ++next;
+ if ((*i).second)
+ {
+ // prevent double update
+ if ((*i).second->IsUpdated())
+ continue;
+ (*i).second->SetUpdated(true);
+ (*i).second->Update( time );
+ // several auras can be deleted due to update
+ if (m_removedAuras)
+ {
+ if (m_Auras.empty()) break;
+ next = m_Auras.begin();
+ m_removedAuras = 0;
+ }
+ }
+ }
+
+ for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end();)
+ {
+ if ((*i).second)
+ {
+ if ( !(*i).second->GetAuraDuration() && !((*i).second->IsPermanent() || ((*i).second->IsPassive())) )
+ {
+ RemoveAura(i);
+ }
+ else
+ {
+ ++i;
+ }
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ if(!m_gameObj.empty())
+ {
+ std::list<GameObject*>::iterator ite1, dnext1;
+ for (ite1 = m_gameObj.begin(); ite1 != m_gameObj.end(); ite1 = dnext1)
+ {
+ dnext1 = ite1;
+ //(*i)->Update( difftime );
+ if( !(*ite1)->isSpawned() )
+ {
+ (*ite1)->SetOwnerGUID(0);
+ (*ite1)->SetRespawnTime(0);
+ (*ite1)->Delete();
+ dnext1 = m_gameObj.erase(ite1);
+ }
+ else
+ ++dnext1;
+ }
+ }
+}
+
+void Unit::_UpdateAutoRepeatSpell()
+{
+ //check "realtime" interrupts
+ if ( (GetTypeId() == TYPEID_PLAYER && ((Player*)this)->isMoving()) || IsNonMeleeSpellCasted(false,false,true) )
+ {
+ // cancel wand shoot
+ if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
+ InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+ m_AutoRepeatFirstCast = true;
+ return;
+ }
+
+ //apply delay
+ if ( m_AutoRepeatFirstCast && getAttackTimer(RANGED_ATTACK) < 500 )
+ setAttackTimer(RANGED_ATTACK,500);
+ m_AutoRepeatFirstCast = false;
+
+ //castroutine
+ if (isAttackReady(RANGED_ATTACK))
+ {
+ // Check if able to cast
+ if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CanCast(true))
+ {
+ InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+ return;
+ }
+
+ // we want to shoot
+ Spell* spell = new Spell(this, m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo, true, 0);
+ spell->prepare(&(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets));
+
+ // all went good, reset attack
+ resetAttackTimer(RANGED_ATTACK);
+ }
+}
+
+void Unit::SetCurrentCastedSpell( Spell * pSpell )
+{
+ assert(pSpell); // NULL may be never passed here, use InterruptSpell or InterruptNonMeleeSpells
+
+ uint32 CSpellType = pSpell->GetCurrentContainer();
+
+ pSpell->SetDeletable(false); // spell will not be deleted until gone from current pointers
+ if (pSpell == m_currentSpells[CSpellType]) return; // avoid breaking self
+
+ // break same type spell if it is not delayed
+ InterruptSpell(CSpellType,false);
+
+ // special breakage effects:
+ switch (CSpellType)
+ {
+ case CURRENT_GENERIC_SPELL:
+ {
+ // generic spells always break channeled not delayed spells
+ InterruptSpell(CURRENT_CHANNELED_SPELL,false);
+
+ // autorepeat breaking
+ if ( m_currentSpells[CURRENT_AUTOREPEAT_SPELL] )
+ {
+ // break autorepeat if not Auto Shot
+ if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
+ InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+ m_AutoRepeatFirstCast = true;
+ }
+ } break;
+
+ case CURRENT_CHANNELED_SPELL:
+ {
+ // channel spells always break generic non-delayed and any channeled spells
+ InterruptSpell(CURRENT_GENERIC_SPELL,false);
+ InterruptSpell(CURRENT_CHANNELED_SPELL);
+
+ // it also does break autorepeat if not Auto Shot
+ if ( m_currentSpells[CURRENT_AUTOREPEAT_SPELL] &&
+ m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351 )
+ InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+ } break;
+
+ case CURRENT_AUTOREPEAT_SPELL:
+ {
+ // only Auto Shoot does not break anything
+ if (pSpell->m_spellInfo->Category == 351)
+ {
+ // generic autorepeats break generic non-delayed and channeled non-delayed spells
+ InterruptSpell(CURRENT_GENERIC_SPELL,false);
+ InterruptSpell(CURRENT_CHANNELED_SPELL,false);
+ }
+ // special action: set first cast flag
+ m_AutoRepeatFirstCast = true;
+ } break;
+
+ default:
+ {
+ // other spell types don't break anything now
+ } break;
+ }
+
+ // current spell (if it is still here) may be safely deleted now
+ if (m_currentSpells[CSpellType])
+ m_currentSpells[CSpellType]->SetDeletable(true);
+
+ // set new current spell
+ m_currentSpells[CSpellType] = pSpell;
+}
+
+void Unit::InterruptSpell(uint32 spellType, bool withDelayed)
+{
+ assert(spellType < CURRENT_MAX_SPELL);
+
+ if(m_currentSpells[spellType] && (withDelayed || m_currentSpells[spellType]->getState() != SPELL_STATE_DELAYED) )
+ {
+ // send autorepeat cancel message for autorepeat spells
+ if (spellType == CURRENT_AUTOREPEAT_SPELL)
+ {
+ if(GetTypeId()==TYPEID_PLAYER)
+ ((Player*)this)->SendAutoRepeatCancel();
+ }
+
+ if (m_currentSpells[spellType]->getState() != SPELL_STATE_FINISHED)
+ m_currentSpells[spellType]->cancel();
+ m_currentSpells[spellType]->SetDeletable(true);
+ m_currentSpells[spellType] = NULL;
+ }
+}
+
+bool Unit::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skipAutorepeat) const
+{
+ // We don't do loop here to explicitly show that melee spell is excluded.
+ // Maybe later some special spells will be excluded too.
+
+ // generic spells are casted when they are not finished and not delayed
+ if ( m_currentSpells[CURRENT_GENERIC_SPELL] &&
+ (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) &&
+ (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED) )
+ return(true);
+
+ // channeled spells may be delayed, but they are still considered casted
+ else if ( !skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] &&
+ (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED) )
+ return(true);
+
+ // autorepeat spells may be finished or delayed, but they are still considered casted
+ else if ( !skipAutorepeat && m_currentSpells[CURRENT_AUTOREPEAT_SPELL] )
+ return(true);
+
+ return(false);
+}
+
+void Unit::InterruptNonMeleeSpells(bool withDelayed, uint32 spell_id)
+{
+ // generic spells are interrupted if they are not finished or delayed
+ if (m_currentSpells[CURRENT_GENERIC_SPELL] && (!spell_id || m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->Id==spell_id))
+ {
+ if ( (m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_FINISHED) &&
+ (withDelayed || m_currentSpells[CURRENT_GENERIC_SPELL]->getState() != SPELL_STATE_DELAYED) )
+ m_currentSpells[CURRENT_GENERIC_SPELL]->cancel();
+ m_currentSpells[CURRENT_GENERIC_SPELL]->SetDeletable(true);
+ m_currentSpells[CURRENT_GENERIC_SPELL] = NULL;
+ }
+
+ // autorepeat spells are interrupted if they are not finished or delayed
+ if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && (!spell_id || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id==spell_id))
+ {
+ // send disable autorepeat packet in any case
+ if(GetTypeId()==TYPEID_PLAYER)
+ ((Player*)this)->SendAutoRepeatCancel();
+
+ if ( (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() != SPELL_STATE_FINISHED) &&
+ (withDelayed || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() != SPELL_STATE_DELAYED) )
+ m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->cancel();
+ m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SetDeletable(true);
+ m_currentSpells[CURRENT_AUTOREPEAT_SPELL] = NULL;
+ }
+
+ // channeled spells are interrupted if they are not finished, even if they are delayed
+ if (m_currentSpells[CURRENT_CHANNELED_SPELL] && (!spell_id || m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->Id==spell_id))
+ {
+ if (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED)
+ m_currentSpells[CURRENT_CHANNELED_SPELL]->cancel();
+ m_currentSpells[CURRENT_CHANNELED_SPELL]->SetDeletable(true);
+ m_currentSpells[CURRENT_CHANNELED_SPELL] = NULL;
+ }
+}
+
+Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const
+{
+ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
+ if(m_currentSpells[i] && m_currentSpells[i]->m_spellInfo->Id==spell_id)
+ return m_currentSpells[i];
+ return NULL;
+}
+
+bool Unit::isInFront(Unit const* target, float distance, float arc) const
+{
+ return IsWithinDistInMap(target, distance) && HasInArc( arc, target );
+}
+
+void Unit::SetInFront(Unit const* target)
+{
+ SetOrientation(GetAngle(target));
+}
+
+bool Unit::isInBack(Unit const* target, float distance, float arc) const
+{
+ return IsWithinDistInMap(target, distance) && !HasInArc( 2 * M_PI - arc, target );
+}
+
+bool Unit::isInAccessablePlaceFor(Creature const* c) const
+{
+ if(IsInWater())
+ return c->canSwim();
+ else
+ return c->canWalk() || c->canFly();
+}
+
+bool Unit::IsInWater() const
+{
+ return MapManager::Instance().GetBaseMap(GetMapId())->IsInWater(GetPositionX(),GetPositionY(), GetPositionZ());
+}
+
+bool Unit::IsUnderWater() const
+{
+ return MapManager::Instance().GetBaseMap(GetMapId())->IsUnderWater(GetPositionX(),GetPositionY(),GetPositionZ());
+}
+
+void Unit::DeMorph()
+{
+ SetDisplayId(GetNativeDisplayId());
+}
+
+int32 Unit::GetTotalAuraModifier(AuraType auratype) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ modifier += (*i)->GetModifier()->m_amount;
+
+ return modifier;
+}
+
+float Unit::GetTotalAuraMultiplier(AuraType auratype) const
+{
+ float multipler = 1.0f;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ multipler *= (100.0f + (*i)->GetModifier()->m_amount)/100.0f;
+
+ return multipler;
+}
+
+int32 Unit::GetMaxPositiveAuraModifier(AuraType auratype) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ if ((*i)->GetModifier()->m_amount > modifier)
+ modifier = (*i)->GetModifier()->m_amount;
+
+ return modifier;
+}
+
+int32 Unit::GetMaxNegativeAuraModifier(AuraType auratype) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ if ((*i)->GetModifier()->m_amount < modifier)
+ modifier = (*i)->GetModifier()->m_amount;
+
+ return modifier;
+}
+
+int32 Unit::GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue & misc_mask)
+ modifier += mod->m_amount;
+ }
+ return modifier;
+}
+
+float Unit::GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask) const
+{
+ float multipler = 1.0f;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue & misc_mask)
+ multipler *= (100.0f + mod->m_amount)/100.0f;
+ }
+ return multipler;
+}
+
+int32 Unit::GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue & misc_mask && mod->m_amount > modifier)
+ modifier = mod->m_amount;
+ }
+
+ return modifier;
+}
+
+int32 Unit::GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue & misc_mask && mod->m_amount < modifier)
+ modifier = mod->m_amount;
+ }
+
+ return modifier;
+}
+
+int32 Unit::GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue == misc_value)
+ modifier += mod->m_amount;
+ }
+ return modifier;
+}
+
+float Unit::GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 misc_value) const
+{
+ float multipler = 1.0f;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue == misc_value)
+ multipler *= (100.0f + mod->m_amount)/100.0f;
+ }
+ return multipler;
+}
+
+int32 Unit::GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue == misc_value && mod->m_amount > modifier)
+ modifier = mod->m_amount;
+ }
+
+ return modifier;
+}
+
+int32 Unit::GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
+{
+ int32 modifier = 0;
+
+ AuraList const& mTotalAuraList = GetAurasByType(auratype);
+ for(AuraList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
+ {
+ Modifier* mod = (*i)->GetModifier();
+ if (mod->m_miscvalue == misc_value && mod->m_amount < modifier)
+ modifier = mod->m_amount;
+ }
+
+ return modifier;
+}
+
+bool Unit::AddAura(Aura *Aur)
+{
+ // ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load)
+ if( !isAlive() && Aur->GetId() != 20584 && Aur->GetId() != 8326 && Aur->GetId() != 2584 &&
+ (GetTypeId()!=TYPEID_PLAYER || !((Player*)this)->GetSession()->PlayerLoading()) )
+ {
+ delete Aur;
+ return false;
+ }
+
+ if(Aur->GetTarget() != this)
+ {
+ sLog.outError("Aura (spell %u eff %u) add to aura list of %s (lowguid: %u) but Aura target is %s (lowguid: %u)",
+ Aur->GetId(),Aur->GetEffIndex(),(GetTypeId()==TYPEID_PLAYER?"player":"creature"),GetGUIDLow(),
+ (Aur->GetTarget()->GetTypeId()==TYPEID_PLAYER?"player":"creature"),Aur->GetTarget()->GetGUIDLow());
+ delete Aur;
+ return false;
+ }
+
+ SpellEntry const* aurSpellInfo = Aur->GetSpellProto();
+
+ spellEffectPair spair = spellEffectPair(Aur->GetId(), Aur->GetEffIndex());
+ AuraMap::iterator i = m_Auras.find( spair );
+
+ // take out same spell
+ if (i != m_Auras.end())
+ {
+ // passive and persistent auras can stack with themselves any number of times
+ if (!Aur->IsPassive() && !Aur->IsPersistent())
+ {
+ // replace aura if next will > spell StackAmount
+ if(aurSpellInfo->StackAmount)
+ {
+ if(m_Auras.count(spair) >= aurSpellInfo->StackAmount)
+ RemoveAura(i,AURA_REMOVE_BY_STACK);
+ }
+ // if StackAmount==0 not allow auras from same caster
+ else
+ {
+ for(AuraMap::iterator i2 = m_Auras.lower_bound(spair); i2 != m_Auras.upper_bound(spair); ++i2)
+ {
+ if(i2->second->GetCasterGUID()==Aur->GetCasterGUID())
+ {
+ // can be only single (this check done at _each_ aura add
+ RemoveAura(i2,AURA_REMOVE_BY_STACK);
+ break;
+ }
+
+ bool stop = false;
+ switch(aurSpellInfo->EffectApplyAuraName[Aur->GetEffIndex()])
+ {
+ // DoT/HoT/etc
+ case SPELL_AURA_PERIODIC_DAMAGE: // allow stack
+ case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
+ case SPELL_AURA_PERIODIC_LEECH:
+ case SPELL_AURA_PERIODIC_HEAL:
+ case SPELL_AURA_OBS_MOD_HEALTH:
+ case SPELL_AURA_PERIODIC_MANA_LEECH:
+ case SPELL_AURA_PERIODIC_ENERGIZE:
+ case SPELL_AURA_OBS_MOD_MANA:
+ case SPELL_AURA_POWER_BURN_MANA:
+ break;
+ default: // not allow
+ // can be only single (this check done at _each_ aura add
+ RemoveAura(i2,AURA_REMOVE_BY_STACK);
+ stop = true;
+ break;
+ }
+
+ if(stop)
+ break;
+ }
+ }
+ }
+ }
+
+ // passive auras stack with all (except passive spell proc auras)
+ if ((!Aur->IsPassive() || !IsPassiveStackableSpell(Aur->GetId())) &&
+ !(Aur->GetId() == 20584 || Aur->GetId() == 8326))
+ {
+ if (!RemoveNoStackAurasDueToAura(Aur))
+ {
+ delete Aur;
+ return false; // couldnt remove conflicting aura with higher rank
+ }
+ }
+
+ // update single target auras list (before aura add to aura list, to prevent unexpected remove recently added aura)
+ if (IsSingleTargetSpell(aurSpellInfo) && Aur->GetTarget())
+ {
+ // caster pointer can be deleted in time aura remove, find it by guid at each iteration
+ for(;;)
+ {
+ Unit* caster = Aur->GetCaster();
+ if(!caster) // caster deleted and not required adding scAura
+ break;
+
+ bool restart = false;
+ AuraList& scAuras = caster->GetSingleCastAuras();
+ for(AuraList::iterator itr = scAuras.begin(); itr != scAuras.end(); ++itr)
+ {
+ if( (*itr)->GetTarget() != Aur->GetTarget() &&
+ IsSingleTargetSpells((*itr)->GetSpellProto(),aurSpellInfo) )
+ {
+ if ((*itr)->IsInUse())
+ {
+ sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for IsSingleTargetSpell", (*itr)->GetId(), (*itr)->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
+ continue;
+ }
+ (*itr)->GetTarget()->RemoveAura((*itr)->GetId(), (*itr)->GetEffIndex());
+ restart = true;
+ break;
+ }
+ }
+
+ if(!restart)
+ {
+ // done
+ scAuras.push_back(Aur);
+ break;
+ }
+ }
+ }
+
+ // add aura, register in lists and arrays
+ Aur->_AddAura();
+ m_Auras.insert(AuraMap::value_type(spellEffectPair(Aur->GetId(), Aur->GetEffIndex()), Aur));
+ if (Aur->GetModifier()->m_auraname < TOTAL_AURAS)
+ {
+ m_modAuras[Aur->GetModifier()->m_auraname].push_back(Aur);
+ }
+
+ Aur->ApplyModifier(true,true);
+ sLog.outDebug("Aura %u now is in use", Aur->GetModifier()->m_auraname);
+ return true;
+}
+
+void Unit::RemoveRankAurasDueToSpell(uint32 spellId)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+ if(!spellInfo)
+ return;
+ AuraMap::iterator i,next;
+ for (i = m_Auras.begin(); i != m_Auras.end(); i = next)
+ {
+ next = i;
+ ++next;
+ uint32 i_spellId = (*i).second->GetId();
+ if((*i).second && i_spellId && i_spellId != spellId)
+ {
+ if(spellmgr.IsRankSpellDueToSpell(spellInfo,i_spellId))
+ {
+ RemoveAurasDueToSpell(i_spellId);
+
+ if( m_Auras.empty() )
+ break;
+ else
+ next = m_Auras.begin();
+ }
+ }
+ }
+}
+
+bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur)
+{
+ if (!Aur)
+ return false;
+
+ SpellEntry const* spellProto = Aur->GetSpellProto();
+ if (!spellProto)
+ return false;
+
+ uint32 spellId = Aur->GetId();
+ uint32 effIndex = Aur->GetEffIndex();
+
+ SpellSpecific spellId_spec = GetSpellSpecific(spellId);
+
+ AuraMap::iterator i,next;
+ for (i = m_Auras.begin(); i != m_Auras.end(); i = next)
+ {
+ next = i;
+ ++next;
+ if (!(*i).second) continue;
+
+ SpellEntry const* i_spellProto = (*i).second->GetSpellProto();
+
+ if (!i_spellProto)
+ continue;
+
+ uint32 i_spellId = i_spellProto->Id;
+
+ if(IsPassiveSpell(i_spellId))
+ {
+ if(IsPassiveStackableSpell(i_spellId))
+ continue;
+
+ // passive non-stackable spells not stackable only with another rank of same spell
+ if (!spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId))
+ continue;
+ }
+
+ uint32 i_effIndex = (*i).second->GetEffIndex();
+
+ if(i_spellId == spellId) continue;
+
+ bool is_triggered_by_spell = false;
+ // prevent triggered aura of removing aura that triggered it
+ for(int j = 0; j < 3; ++j)
+ if (i_spellProto->EffectTriggerSpell[j] == spellProto->Id)
+ is_triggered_by_spell = true;
+ if (is_triggered_by_spell) continue;
+
+ for(int j = 0; j < 3; ++j)
+ {
+ // prevent remove dummy triggered spells at next effect aura add
+ switch(spellProto->Effect[j]) // main spell auras added added after triggred spell
+ {
+ case SPELL_EFFECT_DUMMY:
+ switch(spellId)
+ {
+ case 5420: if(i_spellId==34123) is_triggered_by_spell = true; break;
+ }
+ break;
+ }
+
+ if(is_triggered_by_spell)
+ break;
+
+ // prevent remove form main spell by triggred passive spells
+ switch(i_spellProto->EffectApplyAuraName[j]) // main aura added before triggered spell
+ {
+ case SPELL_AURA_MOD_SHAPESHIFT:
+ switch(i_spellId)
+ {
+ case 24858: if(spellId==24905) is_triggered_by_spell = true; break;
+ case 33891: if(spellId==5420 || spellId==34123) is_triggered_by_spell = true; break;
+ case 34551: if(spellId==22688) is_triggered_by_spell = true; break;
+ }
+ break;
+ }
+ }
+
+ if(!is_triggered_by_spell)
+ {
+ SpellSpecific i_spellId_spec = GetSpellSpecific(i_spellId);
+
+ bool is_sspc = IsSingleFromSpellSpecificPerCaster(spellId_spec,i_spellId_spec);
+
+ if( is_sspc && Aur->GetCasterGUID() == (*i).second->GetCasterGUID() )
+ {
+ // cannot remove higher rank
+ if (spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId))
+ if(CompareAuraRanks(spellId, effIndex, i_spellId, i_effIndex) < 0)
+ return false;
+
+ // Its a parent aura (create this aura in ApplyModifier)
+ if ((*i).second->IsInUse())
+ {
+ sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
+ continue;
+ }
+ RemoveAurasDueToSpell(i_spellId);
+
+ if( m_Auras.empty() )
+ break;
+ else
+ next = m_Auras.begin();
+ }
+ else if( !is_sspc && spellmgr.IsNoStackSpellDueToSpell(spellId, i_spellId) )
+ {
+ // Its a parent aura (create this aura in ApplyModifier)
+ if ((*i).second->IsInUse())
+ {
+ sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
+ continue;
+ }
+ RemoveAurasDueToSpell(i_spellId);
+
+ if( m_Auras.empty() )
+ break;
+ else
+ next = m_Auras.begin();
+ }
+ // Potions stack aura by aura (elixirs/flask already checked)
+ else if( spellProto->SpellFamilyName == SPELLFAMILY_POTION && i_spellProto->SpellFamilyName == SPELLFAMILY_POTION )
+ {
+ if (IsNoStackAuraDueToAura(spellId, effIndex, i_spellId, i_effIndex))
+ {
+ if(CompareAuraRanks(spellId, effIndex, i_spellId, i_effIndex) < 0)
+ return false; // cannot remove higher rank
+
+ // Its a parent aura (create this aura in ApplyModifier)
+ if ((*i).second->IsInUse())
+ {
+ sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
+ continue;
+ }
+ RemoveAura(i);
+ next = i;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void Unit::RemoveAura(uint32 spellId, uint32 effindex, Aura* except)
+{
+ spellEffectPair spair = spellEffectPair(spellId, effindex);
+ for(AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair);)
+ {
+ if(iter->second!=except)
+ {
+ RemoveAura(iter);
+ iter = m_Auras.lower_bound(spair);
+ }
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit *dispeler)
+{
+ for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
+ {
+ Aura *aur = iter->second;
+ if (aur->GetId() == spellId && aur->GetCasterGUID() == casterGUID)
+ {
+ // Custom dispel case
+ // Unstable Affliction
+ if (aur->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (aur->GetSpellProto()->SpellFamilyFlags & 0x010000000000LL))
+ {
+ int32 damage = aur->GetModifier()->m_amount*9;
+ uint64 caster_guid = aur->GetCasterGUID();
+
+ // Remove aura
+ RemoveAura(iter, AURA_REMOVE_BY_DISPEL);
+
+ // backfire damage and silence
+ dispeler->CastCustomSpell(dispeler, 31117, &damage, NULL, NULL, true, NULL, NULL,caster_guid);
+
+ iter = m_Auras.begin(); // iterator can be invalidate at cast if self-dispel
+ }
+ else
+ RemoveAura(iter, AURA_REMOVE_BY_DISPEL);
+ }
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit *stealer)
+{
+ for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
+ {
+ Aura *aur = iter->second;
+ if (aur->GetId() == spellId && aur->GetCasterGUID() == casterGUID)
+ {
+ int32 basePoints = aur->GetBasePoints();
+ // construct the new aura for the attacker
+ Aura * new_aur = CreateAura(aur->GetSpellProto(), aur->GetEffIndex(), &basePoints, stealer);
+ if(!new_aur)
+ continue;
+
+ // set its duration and maximum duration
+ // max duration 2 minutes (in msecs)
+ int32 dur = aur->GetAuraDuration();
+ const int32 max_dur = 2*MINUTE*1000;
+ new_aur->SetAuraMaxDuration( max_dur > dur ? dur : max_dur );
+ new_aur->SetAuraDuration( max_dur > dur ? dur : max_dur );
+
+ // add the new aura to stealer
+ stealer->AddAura(new_aur);
+
+ // Remove aura as dispel
+ RemoveAura(iter, AURA_REMOVE_BY_DISPEL);
+ }
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveAurasDueToSpellByCancel(uint32 spellId)
+{
+ for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
+ {
+ if (iter->second->GetId() == spellId)
+ RemoveAura(iter, AURA_REMOVE_BY_CANCEL);
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveAurasWithDispelType( DispelType type )
+{
+ // Create dispel mask by dispel type
+ uint32 dispelMask = GetDispellMask(type);
+ // Dispel all existing auras vs current dispell type
+ AuraMap& auras = GetAuras();
+ for(AuraMap::iterator itr = auras.begin(); itr != auras.end(); )
+ {
+ SpellEntry const* spell = itr->second->GetSpellProto();
+ if( (1<<spell->Dispel) & dispelMask )
+ {
+ // Dispel aura
+ RemoveAurasDueToSpell(spell->Id);
+ itr = auras.begin();
+ }
+ else
+ ++itr;
+ }
+}
+
+void Unit::RemoveSingleAuraFromStack(uint32 spellId, uint32 effindex)
+{
+ AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex));
+ if(iter != m_Auras.end())
+ RemoveAura(iter);
+}
+
+void Unit::RemoveAurasDueToSpell(uint32 spellId, Aura* except)
+{
+ for (int i = 0; i < 3; ++i)
+ RemoveAura(spellId,i,except);
+}
+
+void Unit::RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId)
+{
+ for (int k=0; k < 3; ++k)
+ {
+ spellEffectPair spair = spellEffectPair(spellId, k);
+ for (AuraMap::iterator iter = m_Auras.lower_bound(spair); iter != m_Auras.upper_bound(spair);)
+ {
+ if (iter->second->GetCastItemGUID() == castItem->GetGUID())
+ {
+ RemoveAura(iter);
+ iter = m_Auras.upper_bound(spair); // overwrite by more appropriate
+ }
+ else
+ ++iter;
+ }
+ }
+}
+
+void Unit::RemoveAurasWithInterruptFlags(uint32 flags)
+{
+ for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
+ {
+ if (iter->second->GetSpellProto()->AuraInterruptFlags & flags)
+ RemoveAura(iter);
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveNotOwnSingleTargetAuras()
+{
+ // single target auras from other casters
+ for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); )
+ {
+ if (iter->second->GetCasterGUID()!=GetGUID() && IsSingleTargetSpell(iter->second->GetSpellProto()))
+ RemoveAura(iter);
+ else
+ ++iter;
+ }
+
+ // single target auras at other targets
+ AuraList& scAuras = GetSingleCastAuras();
+ for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end(); )
+ {
+ Aura* aura = *iter;
+ if (aura->GetTarget()!=this)
+ {
+ scAuras.erase(iter); // explicitly remove, instead waiting remove in RemoveAura
+ aura->GetTarget()->RemoveAura(aura->GetId(),aura->GetEffIndex());
+ iter = scAuras.begin();
+ }
+ else
+ ++iter;
+ }
+
+}
+
+void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode)
+{
+ if (IsSingleTargetSpell((*i).second->GetSpellProto()))
+ {
+ if(Unit* caster = (*i).second->GetCaster())
+ {
+ AuraList& scAuras = caster->GetSingleCastAuras();
+ scAuras.remove((*i).second);
+ }
+ else
+ {
+ sLog.outError("Couldn't find the caster of the single target aura, may crash later!");
+ assert(false);
+ }
+ }
+
+ if ((*i).second->GetModifier()->m_auraname < TOTAL_AURAS)
+ {
+ m_modAuras[(*i).second->GetModifier()->m_auraname].remove((*i).second);
+ }
+
+ // remove from list before mods removing (prevent cyclic calls, mods added before including to aura list - use reverse order)
+ Aura* Aur = i->second;
+ // Set remove mode
+ Aur->SetRemoveMode(mode);
+ // some ShapeshiftBoosts at remove trigger removing other auras including parent Shapeshift aura
+ // remove aura from list before to prevent deleting it before
+ m_Auras.erase(i);
+ ++m_removedAuras; // internal count used by unit update
+
+ // Status unsummoned at aura remove
+ Totem* statue = NULL;
+ if(IsChanneledSpell(Aur->GetSpellProto()))
+ if(Unit* caster = Aur->GetCaster())
+ if(caster->GetTypeId()==TYPEID_UNIT && ((Creature*)caster)->isTotem() && ((Totem*)caster)->GetTotemType()==TOTEM_STATUE)
+ statue = ((Totem*)caster);
+
+ sLog.outDebug("Aura %u now is remove mode %d",Aur->GetModifier()->m_auraname, mode);
+ Aur->ApplyModifier(false,true);
+ Aur->_RemoveAura();
+ delete Aur;
+
+ if(statue)
+ statue->UnSummon();
+
+ // only way correctly remove all auras from list
+ if( m_Auras.empty() )
+ i = m_Auras.end();
+ else
+ i = m_Auras.begin();
+}
+
+void Unit::RemoveAllAuras()
+{
+ while (!m_Auras.empty())
+ {
+ AuraMap::iterator iter = m_Auras.begin();
+ RemoveAura(iter);
+ }
+}
+
+void Unit::RemoveArenaAuras(bool onleave)
+{
+ // in join, remove positive buffs, on end, remove negative
+ // used to remove positive visible auras in arenas
+ for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
+ {
+ if ( !(iter->second->GetSpellProto()->AttributesEx4 & (1<<21)) // don't remove stances, shadowform, pally/hunter auras
+ && !iter->second->IsPassive() // don't remove passive auras
+ && (!(iter->second->GetSpellProto()->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY) || !(iter->second->GetSpellProto()->Attributes & SPELL_ATTR_UNK8)) // not unaffected by invulnerability auras or not having that unknown flag (that seemed the most probable)
+ && (iter->second->IsPositive() ^ onleave)) // remove positive buffs on enter, negative buffs on leave
+ RemoveAura(iter);
+ else
+ ++iter;
+ }
+}
+
+void Unit::RemoveAllAurasOnDeath()
+{
+ // used just after dieing to remove all visible auras
+ // and disable the mods for the passive ones
+ for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
+ {
+ if (!iter->second->IsPassive() && !iter->second->IsDeathPersistent())
+ RemoveAura(iter, AURA_REMOVE_BY_DEATH);
+ else
+ ++iter;
+ }
+}
+
+void Unit::DelayAura(uint32 spellId, uint32 effindex, int32 delaytime)
+{
+ AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex));
+ if (iter != m_Auras.end())
+ {
+ if (iter->second->GetAuraDuration() < delaytime)
+ iter->second->SetAuraDuration(0);
+ else
+ iter->second->SetAuraDuration(iter->second->GetAuraDuration() - delaytime);
+ iter->second->UpdateAuraDuration();
+ sLog.outDebug("Aura %u partially interrupted on unit %u, new duration: %u ms",iter->second->GetModifier()->m_auraname, GetGUIDLow(), iter->second->GetAuraDuration());
+ }
+}
+
+void Unit::_RemoveAllAuraMods()
+{
+ for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
+ {
+ (*i).second->ApplyModifier(false);
+ }
+}
+
+void Unit::_ApplyAllAuraMods()
+{
+ for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
+ {
+ (*i).second->ApplyModifier(true);
+ }
+}
+
+Aura* Unit::GetAura(uint32 spellId, uint32 effindex)
+{
+ AuraMap::iterator iter = m_Auras.find(spellEffectPair(spellId, effindex));
+ if (iter != m_Auras.end())
+ return iter->second;
+ return NULL;
+}
+
+void Unit::AddDynObject(DynamicObject* dynObj)
+{
+ m_dynObjGUIDs.push_back(dynObj->GetGUID());
+}
+
+void Unit::RemoveDynObject(uint32 spellid)
+{
+ if(m_dynObjGUIDs.empty())
+ return;
+ for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();)
+ {
+ DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
+ if(!dynObj)
+ {
+ i = m_dynObjGUIDs.erase(i);
+ }
+ else if(spellid == 0 || dynObj->GetSpellId() == spellid)
+ {
+ dynObj->Delete();
+ i = m_dynObjGUIDs.erase(i);
+ }
+ else
+ ++i;
+ }
+}
+
+void Unit::RemoveAllDynObjects()
+{
+ while(!m_dynObjGUIDs.empty())
+ {
+ DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
+ if(dynObj)
+ dynObj->Delete();
+ m_dynObjGUIDs.erase(m_dynObjGUIDs.begin());
+ }
+}
+
+DynamicObject * Unit::GetDynObject(uint32 spellId, uint32 effIndex)
+{
+ for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();)
+ {
+ DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
+ if(!dynObj)
+ {
+ i = m_dynObjGUIDs.erase(i);
+ continue;
+ }
+
+ if (dynObj->GetSpellId() == spellId && dynObj->GetEffIndex() == effIndex)
+ return dynObj;
+ ++i;
+ }
+ return NULL;
+}
+
+DynamicObject * Unit::GetDynObject(uint32 spellId)
+{
+ for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();)
+ {
+ DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin());
+ if(!dynObj)
+ {
+ i = m_dynObjGUIDs.erase(i);
+ continue;
+ }
+
+ if (dynObj->GetSpellId() == spellId)
+ return dynObj;
+ ++i;
+ }
+ return NULL;
+}
+
+void Unit::AddGameObject(GameObject* gameObj)
+{
+ assert(gameObj && gameObj->GetOwnerGUID()==0);
+ m_gameObj.push_back(gameObj);
+ gameObj->SetOwnerGUID(GetGUID());
+}
+
+void Unit::RemoveGameObject(GameObject* gameObj, bool del)
+{
+ assert(gameObj && gameObj->GetOwnerGUID()==GetGUID());
+
+ // GO created by some spell
+ if ( GetTypeId()==TYPEID_PLAYER && gameObj->GetSpellId() )
+ {
+ SpellEntry const* createBySpell = sSpellStore.LookupEntry(gameObj->GetSpellId());
+ // Need activate spell use for owner
+ if (createBySpell && createBySpell->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
+ ((Player*)this)->SendCooldownEvent(createBySpell);
+ }
+ gameObj->SetOwnerGUID(0);
+ m_gameObj.remove(gameObj);
+ if(del)
+ {
+ gameObj->SetRespawnTime(0);
+ gameObj->Delete();
+ }
+}
+
+void Unit::RemoveGameObject(uint32 spellid, bool del)
+{
+ if(m_gameObj.empty())
+ return;
+ std::list<GameObject*>::iterator i, next;
+ for (i = m_gameObj.begin(); i != m_gameObj.end(); i = next)
+ {
+ next = i;
+ if(spellid == 0 || (*i)->GetSpellId() == spellid)
+ {
+ (*i)->SetOwnerGUID(0);
+ if(del)
+ {
+ (*i)->SetRespawnTime(0);
+ (*i)->Delete();
+ }
+
+ next = m_gameObj.erase(i);
+ }
+ else
+ ++next;
+ }
+}
+
+void Unit::RemoveAllGameObjects()
+{
+ // remove references to unit
+ for(std::list<GameObject*>::iterator i = m_gameObj.begin(); i != m_gameObj.end();)
+ {
+ (*i)->SetOwnerGUID(0);
+ (*i)->SetRespawnTime(0);
+ (*i)->Delete();
+ i = m_gameObj.erase(i);
+ }
+}
+
+void Unit::SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchoolMask damageSchoolMask,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit)
+{
+ sLog.outDebug("Sending: SMSG_SPELLNONMELEEDAMAGELOG");
+ WorldPacket data(SMSG_SPELLNONMELEEDAMAGELOG, (16+4+4+1+4+4+1+1+4+4+1)); // we guess size
+ data.append(target->GetPackGUID());
+ data.append(GetPackGUID());
+ data << uint32(SpellID);
+ data << uint32(Damage-AbsorbedDamage-Resist-Blocked);
+ data << uint8(damageSchoolMask); // spell school
+ data << uint32(AbsorbedDamage); // AbsorbedDamage
+ data << uint32(Resist); // resist
+ data << uint8(PhysicalDamage); // if 1, then client show spell name (example: %s's ranged shot hit %s for %u school or %s suffers %u school damage from %s's spell_name
+ data << uint8(0); // unk isFromAura
+ data << uint32(Blocked); // blocked
+ data << uint32(CriticalHit ? 0x27 : 0x25); // hitType, flags: 0x2 - SPELL_HIT_TYPE_CRIT, 0x10 - replace caster?
+ data << uint8(0); // isDebug?
+ SendMessageToSet( &data, true );
+}
+
+void Unit::SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo)
+{
+ WorldPacket data(SMSG_SPELLLOGMISS, (4+8+1+4+8+1));
+ data << uint32(spellID);
+ data << uint64(GetGUID());
+ data << uint8(0); // can be 0 or 1
+ data << uint32(1); // target count
+ // for(i = 0; i < target count; ++i)
+ data << uint64(target->GetGUID()); // target GUID
+ data << uint8(missInfo);
+ // end loop
+ SendMessageToSet(&data, true);
+}
+
+void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount)
+{
+ sLog.outDebug("WORLD: Sending SMSG_ATTACKERSTATEUPDATE");
+
+ WorldPacket data(SMSG_ATTACKERSTATEUPDATE, (16+45)); // we guess size
+ data << (uint32)HitInfo;
+ data.append(GetPackGUID());
+ data.append(target->GetPackGUID());
+ data << (uint32)(Damage-AbsorbDamage-Resist-BlockedAmount);
+
+ data << (uint8)SwingType; // count?
+
+ // for(i = 0; i < SwingType; ++i)
+ data << (uint32)damageSchoolMask;
+ data << (float)(Damage-AbsorbDamage-Resist-BlockedAmount);
+ // still need to double check damage
+ data << (uint32)(Damage-AbsorbDamage-Resist-BlockedAmount);
+ data << (uint32)AbsorbDamage;
+ data << (uint32)Resist;
+ // end loop
+
+ data << (uint32)TargetState;
+
+ if( AbsorbDamage == 0 ) //also 0x3E8 = 0x3E8, check when that happens
+ data << (uint32)0;
+ else
+ data << (uint32)-1;
+
+ data << (uint32)0;
+ data << (uint32)BlockedAmount;
+
+ SendMessageToSet( &data, true );
+}
+
+void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage, SpellSchoolMask damageSchoolMask, SpellEntry const *procSpell, bool isTriggeredSpell, WeaponAttackType attType)
+{
+ sLog.outDebug("ProcDamageAndSpell: attacker flags are 0x%x, victim flags 0x%x", procAttacker, procVictim);
+ if(procSpell)
+ sLog.outDebug("ProcDamageAndSpell: invoked due to spell id %u %s", procSpell->Id, (isTriggeredSpell?"(triggered)":""));
+
+ // Assign melee/ranged proc flags for magic attacks, that are actually melee/ranged abilities
+ // not assign for spell proc triggered spell to prevent infinity (or unexpacted 2-3 times) melee damage spell proc call with melee damage effect
+ // That is the question though if it's fully correct
+ if(procSpell && !isTriggeredSpell)
+ {
+ if(procSpell->DmgClass == SPELL_DAMAGE_CLASS_MELEE)
+ {
+ if(procAttacker & PROC_FLAG_HIT_SPELL) procAttacker |= PROC_FLAG_HIT_MELEE;
+ if(procAttacker & PROC_FLAG_CRIT_SPELL) procAttacker |= PROC_FLAG_CRIT_MELEE;
+ if(procVictim & PROC_FLAG_STRUCK_SPELL) procVictim |= PROC_FLAG_STRUCK_MELEE;
+ if(procVictim & PROC_FLAG_STRUCK_CRIT_SPELL) procVictim |= PROC_FLAG_STRUCK_CRIT_MELEE;
+ attType = BASE_ATTACK; // Melee abilities are assumed to be dealt with mainhand weapon
+ }
+ else if (procSpell->DmgClass == SPELL_DAMAGE_CLASS_RANGED)
+ {
+ if(procAttacker & PROC_FLAG_HIT_SPELL) procAttacker |= PROC_FLAG_HIT_RANGED;
+ if(procAttacker & PROC_FLAG_CRIT_SPELL) procAttacker |= PROC_FLAG_CRIT_RANGED;
+ if(procVictim & PROC_FLAG_STRUCK_SPELL) procVictim |= PROC_FLAG_STRUCK_RANGED;
+ if(procVictim & PROC_FLAG_STRUCK_CRIT_SPELL) procVictim |= PROC_FLAG_STRUCK_CRIT_RANGED;
+ attType = RANGED_ATTACK;
+ }
+ }
+ if(damage && (procVictim & (PROC_FLAG_STRUCK_MELEE|PROC_FLAG_STRUCK_RANGED|PROC_FLAG_STRUCK_SPELL)))
+ procVictim |= (PROC_FLAG_TAKE_DAMAGE|PROC_FLAG_TOUCH);
+
+ // Not much to do if no flags are set.
+ if (procAttacker)
+ {
+ // procces auras that not generate casts at proc event before auras that generate casts to prevent proc aura added at prev. proc aura execute in set
+ ProcDamageAndSpellFor(false,pVictim,procAttacker,attackerProcEffectAuraTypes,attType, procSpell, damage, damageSchoolMask);
+ ProcDamageAndSpellFor(false,pVictim,procAttacker,attackerProcCastAuraTypes,attType, procSpell, damage, damageSchoolMask);
+ }
+
+ // Now go on with a victim's events'n'auras
+ // Not much to do if no flags are set or there is no victim
+ if(pVictim && pVictim->isAlive() && procVictim)
+ {
+ // procces auras that not generate casts at proc event before auras that generate casts to prevent proc aura added at prev. proc aura execute in set
+ pVictim->ProcDamageAndSpellFor(true,this,procVictim,victimProcEffectAuraTypes,attType,procSpell, damage, damageSchoolMask);
+ pVictim->ProcDamageAndSpellFor(true,this,procVictim,victimProcCastAuraTypes,attType,procSpell, damage, damageSchoolMask);
+ }
+}
+
+void Unit::CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, SpellSchoolMask damageSchoolMask, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted, bool isTriggeredSpell)
+{
+ if(!pVictim)
+ return;
+
+ uint32 procAttacker = PROC_FLAG_NONE;
+ uint32 procVictim = PROC_FLAG_NONE;
+
+ switch(outcome)
+ {
+ case MELEE_HIT_EVADE:
+ return;
+ case MELEE_HIT_MISS:
+ if(attType == BASE_ATTACK || attType == OFF_ATTACK)
+ {
+ procAttacker = PROC_FLAG_MISS;
+ }
+ break;
+ case MELEE_HIT_BLOCK_CRIT:
+ case MELEE_HIT_CRIT:
+ if(spellCasted && attType == BASE_ATTACK)
+ {
+ procAttacker |= PROC_FLAG_CRIT_SPELL;
+ procVictim |= PROC_FLAG_STRUCK_CRIT_SPELL;
+ if ( outcome == MELEE_HIT_BLOCK_CRIT )
+ {
+ procVictim |= PROC_FLAG_BLOCK;
+ procAttacker |= PROC_FLAG_TARGET_BLOCK;
+ }
+ }
+ else if(attType == BASE_ATTACK || attType == OFF_ATTACK)
+ {
+ procAttacker = PROC_FLAG_HIT_MELEE | PROC_FLAG_CRIT_MELEE;
+ procVictim = PROC_FLAG_STRUCK_MELEE | PROC_FLAG_STRUCK_CRIT_MELEE;
+ }
+ else
+ {
+ procAttacker = PROC_FLAG_HIT_RANGED | PROC_FLAG_CRIT_RANGED;
+ procVictim = PROC_FLAG_STRUCK_RANGED | PROC_FLAG_STRUCK_CRIT_RANGED;
+ }
+ break;
+ case MELEE_HIT_PARRY:
+ procAttacker = PROC_FLAG_TARGET_DODGE_OR_PARRY;
+ procVictim = PROC_FLAG_PARRY;
+ break;
+ case MELEE_HIT_BLOCK:
+ procAttacker = PROC_FLAG_TARGET_BLOCK;
+ procVictim = PROC_FLAG_BLOCK;
+ break;
+ case MELEE_HIT_DODGE:
+ procAttacker = PROC_FLAG_TARGET_DODGE_OR_PARRY;
+ procVictim = PROC_FLAG_DODGE;
+ break;
+ case MELEE_HIT_CRUSHING:
+ if(attType == BASE_ATTACK || attType == OFF_ATTACK)
+ {
+ procAttacker = PROC_FLAG_HIT_MELEE | PROC_FLAG_CRIT_MELEE;
+ procVictim = PROC_FLAG_STRUCK_MELEE | PROC_FLAG_STRUCK_CRIT_MELEE;
+ }
+ else
+ {
+ procAttacker = PROC_FLAG_HIT_RANGED | PROC_FLAG_CRIT_RANGED;
+ procVictim = PROC_FLAG_STRUCK_RANGED | PROC_FLAG_STRUCK_CRIT_RANGED;
+ }
+ break;
+ default:
+ if(attType == BASE_ATTACK || attType == OFF_ATTACK)
+ {
+ procAttacker = PROC_FLAG_HIT_MELEE;
+ procVictim = PROC_FLAG_STRUCK_MELEE;
+ }
+ else
+ {
+ procAttacker = PROC_FLAG_HIT_RANGED;
+ procVictim = PROC_FLAG_STRUCK_RANGED;
+ }
+ break;
+ }
+
+ if(damage > 0)
+ procVictim |= PROC_FLAG_TAKE_DAMAGE;
+
+ if(procAttacker != PROC_FLAG_NONE || procVictim != PROC_FLAG_NONE)
+ ProcDamageAndSpell(pVictim, procAttacker, procVictim, damage, damageSchoolMask, spellCasted, isTriggeredSpell, attType);
+}
+
+bool Unit::HandleHasteAuraProc(Unit *pVictim, SpellEntry const *hasteSpell, uint32 /*effIndex*/, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 /*procFlag*/, uint32 cooldown)
+{
+ Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
+ ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
+
+ uint32 triggered_spell_id = 0;
+ Unit* target = pVictim;
+ int32 basepoints0 = 0;
+
+ switch(hasteSpell->SpellFamilyName)
+ {
+ case SPELLFAMILY_ROGUE:
+ {
+ switch(hasteSpell->Id)
+ {
+ // Blade Flurry
+ case 13877:
+ case 33735:
+ {
+ target = SelectNearbyTarget();
+ if(!target)
+ return false;
+ basepoints0 = damage;
+ triggered_spell_id = 22482;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ // processed charge only counting case
+ if(!triggered_spell_id)
+ return true;
+
+ SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
+
+ if(!triggerEntry)
+ {
+ sLog.outError("Unit::HandleHasteAuraProc: Spell %u have not existed triggered spell %u",hasteSpell->Id,triggered_spell_id);
+ return false;
+ }
+
+ // default case
+ if(!target || target!=this && !target->isAlive())
+ return false;
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
+ return false;
+
+ if(basepoints0)
+ CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+ else
+ CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
+
+ return true;
+}
+
+bool Unit::HandleDummyAuraProc(Unit *pVictim, SpellEntry const *dummySpell, uint32 effIndex, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 cooldown)
+{
+ Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
+ ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
+
+ uint32 triggered_spell_id = 0;
+ Unit* target = pVictim;
+ int32 basepoints0 = 0;
+
+ switch(dummySpell->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ switch (dummySpell->Id)
+ {
+ // Eye of Eye
+ case 9799:
+ case 25988:
+ {
+ // prevent damage back from weapon special attacks
+ if (!procSpell || procSpell->DmgClass != SPELL_DAMAGE_CLASS_MAGIC )
+ return false;
+
+ // return damage % to attacker but < 50% own total health
+ basepoints0 = triggeredByAura->GetModifier()->m_amount*int32(damage)/100;
+ if(basepoints0 > GetMaxHealth()/2)
+ basepoints0 = GetMaxHealth()/2;
+
+ triggered_spell_id = 25997;
+ break;
+ }
+ // Sweeping Strikes
+ case 12328:
+ case 18765:
+ case 35429:
+ {
+ // prevent chain of triggred spell from same triggred spell
+ if(procSpell && procSpell->Id==26654)
+ return false;
+
+ target = SelectNearbyTarget();
+ if(!target)
+ return false;
+
+ triggered_spell_id = 26654;
+ break;
+ }
+ // Unstable Power
+ case 24658:
+ {
+ if (!procSpell || procSpell->Id == 24659)
+ return false;
+ // Need remove one 24659 aura
+ RemoveSingleAuraFromStack(24659, 0);
+ RemoveSingleAuraFromStack(24659, 1);
+ return true;
+ }
+ // Restless Strength
+ case 24661:
+ {
+ // Need remove one 24662 aura
+ RemoveSingleAuraFromStack(24662, 0);
+ return true;
+ }
+ // Adaptive Warding (Frostfire Regalia set)
+ case 28764:
+ {
+ if(!procSpell)
+ return false;
+
+ // find Mage Armor
+ bool found = false;
+ AuraList const& mRegenInterupt = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
+ for(AuraList::const_iterator iter = mRegenInterupt.begin(); iter != mRegenInterupt.end(); ++iter)
+ {
+ if(SpellEntry const* iterSpellProto = (*iter)->GetSpellProto())
+ {
+ if(iterSpellProto->SpellFamilyName==SPELLFAMILY_MAGE && (iterSpellProto->SpellFamilyFlags & 0x10000000))
+ {
+ found=true;
+ break;
+ }
+ }
+ }
+ if(!found)
+ return false;
+
+ switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell)))
+ {
+ case SPELL_SCHOOL_NORMAL:
+ case SPELL_SCHOOL_HOLY:
+ return false; // ignored
+ case SPELL_SCHOOL_FIRE: triggered_spell_id = 28765; break;
+ case SPELL_SCHOOL_NATURE: triggered_spell_id = 28768; break;
+ case SPELL_SCHOOL_FROST: triggered_spell_id = 28766; break;
+ case SPELL_SCHOOL_SHADOW: triggered_spell_id = 28769; break;
+ case SPELL_SCHOOL_ARCANE: triggered_spell_id = 28770; break;
+ default:
+ return false;
+ }
+
+ target = this;
+ break;
+ }
+ // Obsidian Armor (Justice Bearer`s Pauldrons shoulder)
+ case 27539:
+ {
+ if(!procSpell)
+ return false;
+
+ // not from DoT
+ bool found = false;
+ for(int j = 0; j < 3; ++j)
+ {
+ if(procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE||procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE_PERCENT)
+ {
+ found = true;
+ break;
+ }
+ }
+ if(found)
+ return false;
+
+ switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell)))
+ {
+ case SPELL_SCHOOL_NORMAL:
+ return false; // ignore
+ case SPELL_SCHOOL_HOLY: triggered_spell_id = 27536; break;
+ case SPELL_SCHOOL_FIRE: triggered_spell_id = 27533; break;
+ case SPELL_SCHOOL_NATURE: triggered_spell_id = 27538; break;
+ case SPELL_SCHOOL_FROST: triggered_spell_id = 27534; break;
+ case SPELL_SCHOOL_SHADOW: triggered_spell_id = 27535; break;
+ case SPELL_SCHOOL_ARCANE: triggered_spell_id = 27540; break;
+ default:
+ return false;
+ }
+
+ target = this;
+ break;
+ }
+ // Mana Leech (Passive) (Priest Pet Aura)
+ case 28305:
+ {
+ // Cast on owner
+ target = GetOwner();
+ if(!target)
+ return false;
+
+ basepoints0 = int32(damage * 2.5f); // manaregen
+ triggered_spell_id = 34650;
+ break;
+ }
+ // Mark of Malice
+ case 33493:
+ {
+ // Cast finish spell at last charge
+ if (triggeredByAura->m_procCharges > 1)
+ return false;
+
+ target = this;
+ triggered_spell_id = 33494;
+ break;
+ }
+ // Twisted Reflection (boss spell)
+ case 21063:
+ triggered_spell_id = 21064;
+ break;
+ // Vampiric Aura (boss spell)
+ case 38196:
+ {
+ basepoints0 = 3 * damage; // 300%
+ if (basepoints0 < 0)
+ return false;
+
+ triggered_spell_id = 31285;
+ target = this;
+ break;
+ }
+ // Aura of Madness (Darkmoon Card: Madness trinket)
+ //=====================================================
+ // 39511 Sociopath: +35 strength (Paladin, Rogue, Druid, Warrior)
+ // 40997 Delusional: +70 attack power (Rogue, Hunter, Paladin, Warrior, Druid)
+ // 40998 Kleptomania: +35 agility (Warrior, Rogue, Paladin, Hunter, Druid)
+ // 40999 Megalomania: +41 damage/healing (Druid, Shaman, Priest, Warlock, Mage, Paladin)
+ // 41002 Paranoia: +35 spell/melee/ranged crit strike rating (All classes)
+ // 41005 Manic: +35 haste (spell, melee and ranged) (All classes)
+ // 41009 Narcissism: +35 intellect (Druid, Shaman, Priest, Warlock, Mage, Paladin, Hunter)
+ // 41011 Martyr Complex: +35 stamina (All classes)
+ // 41406 Dementia: Every 5 seconds either gives you +5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin)
+ // 41409 Dementia: Every 5 seconds either gives you -5% damage/healing. (Druid, Shaman, Priest, Warlock, Mage, Paladin)
+ case 39446:
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Select class defined buff
+ switch (getClass())
+ {
+ case CLASS_PALADIN: // 39511,40997,40998,40999,41002,41005,41009,41011,41409
+ case CLASS_DRUID: // 39511,40997,40998,40999,41002,41005,41009,41011,41409
+ {
+ uint32 RandomSpell[]={39511,40997,40998,40999,41002,41005,41009,41011,41409};
+ triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
+ break;
+ }
+ case CLASS_ROGUE: // 39511,40997,40998,41002,41005,41011
+ case CLASS_WARRIOR: // 39511,40997,40998,41002,41005,41011
+ {
+ uint32 RandomSpell[]={39511,40997,40998,41002,41005,41011};
+ triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
+ break;
+ }
+ case CLASS_PRIEST: // 40999,41002,41005,41009,41011,41406,41409
+ case CLASS_SHAMAN: // 40999,41002,41005,41009,41011,41406,41409
+ case CLASS_MAGE: // 40999,41002,41005,41009,41011,41406,41409
+ case CLASS_WARLOCK: // 40999,41002,41005,41009,41011,41406,41409
+ {
+ uint32 RandomSpell[]={40999,41002,41005,41009,41011,41406,41409};
+ triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
+ break;
+ }
+ case CLASS_HUNTER: // 40997,40999,41002,41005,41009,41011,41406,41409
+ {
+ uint32 RandomSpell[]={40997,40999,41002,41005,41009,41011,41406,41409};
+ triggered_spell_id = RandomSpell[ irand(0, sizeof(RandomSpell)/sizeof(uint32) - 1) ];
+ break;
+ }
+ default:
+ return false;
+ }
+
+ target = this;
+ if (roll_chance_i(10))
+ ((Player*)this)->Say("This is Madness!", LANG_UNIVERSAL);
+ break;
+ }
+ /*
+ // TODO: need find item for aura and triggered spells
+ // Sunwell Exalted Caster Neck (??? neck)
+ // cast ??? Light's Wrath if Exalted by Aldor
+ // cast ??? Arcane Bolt if Exalted by Scryers*/
+ case 46569:
+ return false; // disable for while
+ /*
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Get Aldor reputation rank
+ if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = ???
+ break;
+ }
+ // Get Scryers reputation rank
+ if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
+ {
+ triggered_spell_id = ???
+ break;
+ }
+ return false;
+ }/**/
+ // Sunwell Exalted Caster Neck (Shattered Sun Pendant of Acumen neck)
+ // cast 45479 Light's Wrath if Exalted by Aldor
+ // cast 45429 Arcane Bolt if Exalted by Scryers
+ case 45481:
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Get Aldor reputation rank
+ if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = 45479;
+ break;
+ }
+ // Get Scryers reputation rank
+ if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
+ {
+ triggered_spell_id = 45429;
+ break;
+ }
+ return false;
+ }
+ // Sunwell Exalted Melee Neck (Shattered Sun Pendant of Might neck)
+ // cast 45480 Light's Strength if Exalted by Aldor
+ // cast 45428 Arcane Strike if Exalted by Scryers
+ case 45482:
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Get Aldor reputation rank
+ if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = 45480;
+ break;
+ }
+ // Get Scryers reputation rank
+ if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
+ {
+ triggered_spell_id = 45428;
+ break;
+ }
+ return false;
+ }
+ // Sunwell Exalted Tank Neck (Shattered Sun Pendant of Resolve neck)
+ // cast 45431 Arcane Insight if Exalted by Aldor
+ // cast 45432 Light's Ward if Exalted by Scryers
+ case 45483:
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Get Aldor reputation rank
+ if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = 45432;
+ break;
+ }
+ // Get Scryers reputation rank
+ if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = 45431;
+ break;
+ }
+ return false;
+ }
+ // Sunwell Exalted Healer Neck (Shattered Sun Pendant of Restoration neck)
+ // cast 45478 Light's Salvation if Exalted by Aldor
+ // cast 45430 Arcane Surge if Exalted by Scryers
+ case 45484:
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // Get Aldor reputation rank
+ if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
+ {
+ target = this;
+ triggered_spell_id = 45478;
+ break;
+ }
+ // Get Scryers reputation rank
+ if (((Player *)this)->GetReputationRank(934) == REP_EXALTED)
+ {
+ triggered_spell_id = 45430;
+ break;
+ }
+ return false;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_MAGE:
+ {
+ // Magic Absorption
+ if (dummySpell->SpellIconID == 459) // only this spell have SpellIconID == 459 and dummy aura
+ {
+ if (getPowerType() != POWER_MANA)
+ return false;
+
+ // mana reward
+ basepoints0 = (triggeredByAura->GetModifier()->m_amount * GetMaxPower(POWER_MANA) / 100);
+ target = this;
+ triggered_spell_id = 29442;
+ break;
+ }
+ // Master of Elements
+ if (dummySpell->SpellIconID == 1920)
+ {
+ if(!procSpell)
+ return false;
+
+ // mana cost save
+ basepoints0 = procSpell->manaCost * triggeredByAura->GetModifier()->m_amount/100;
+ if( basepoints0 <=0 )
+ return false;
+
+ target = this;
+ triggered_spell_id = 29077;
+ break;
+ }
+ switch(dummySpell->Id)
+ {
+ // Ignite
+ case 11119:
+ case 11120:
+ case 12846:
+ case 12847:
+ case 12848:
+ {
+ switch (dummySpell->Id)
+ {
+ case 11119: basepoints0 = int32(0.04f*damage); break;
+ case 11120: basepoints0 = int32(0.08f*damage); break;
+ case 12846: basepoints0 = int32(0.12f*damage); break;
+ case 12847: basepoints0 = int32(0.16f*damage); break;
+ case 12848: basepoints0 = int32(0.20f*damage); break;
+ default:
+ sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (IG)",dummySpell->Id);
+ return false;
+ }
+
+ triggered_spell_id = 12654;
+ break;
+ }
+ // Combustion
+ case 11129:
+ {
+ //last charge and crit
+ if( triggeredByAura->m_procCharges <= 1 && (procFlag & PROC_FLAG_CRIT_SPELL) )
+ {
+ RemoveAurasDueToSpell(28682); //-> remove Combustion auras
+ return true; // charge counting (will removed)
+ }
+
+ CastSpell(this, 28682, true, castItem, triggeredByAura);
+ return(procFlag & PROC_FLAG_CRIT_SPELL);// charge update only at crit hits, no hidden cooldowns
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ // Retaliation
+ if(dummySpell->SpellFamilyFlags==0x0000000800000000LL)
+ {
+ // check attack comes not from behind
+ if (!HasInArc(M_PI, pVictim))
+ return false;
+
+ triggered_spell_id = 22858;
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_WARLOCK:
+ {
+ // Seed of Corruption
+ if (dummySpell->SpellFamilyFlags & 0x0000001000000000LL)
+ {
+ Modifier* mod = triggeredByAura->GetModifier();
+ // if damage is more than need or target die from damage deal finish spell
+ // FIX ME: not triggered currently at death
+ if( mod->m_amount <= damage || GetHealth() <= damage )
+ {
+ // remember guid before aura delete
+ uint64 casterGuid = triggeredByAura->GetCasterGUID();
+
+ // Remove aura (before cast for prevent infinite loop handlers)
+ RemoveAurasDueToSpell(triggeredByAura->GetId());
+
+ // Cast finish spell (triggeredByAura already not exist!)
+ CastSpell(this, 27285, true, castItem, NULL, casterGuid);
+ return true; // no hidden cooldown
+ }
+
+ // Damage counting
+ mod->m_amount-=damage;
+ return true;
+ }
+ // Seed of Corruption (Mobs cast) - no die req
+ if (dummySpell->SpellFamilyFlags == 0x00LL && dummySpell->SpellIconID == 1932)
+ {
+ Modifier* mod = triggeredByAura->GetModifier();
+ // if damage is more than need deal finish spell
+ if( mod->m_amount <= damage )
+ {
+ // remember guid before aura delete
+ uint64 casterGuid = triggeredByAura->GetCasterGUID();
+
+ // Remove aura (before cast for prevent infinite loop handlers)
+ RemoveAurasDueToSpell(triggeredByAura->GetId());
+
+ // Cast finish spell (triggeredByAura already not exist!)
+ CastSpell(this, 32865, true, castItem, NULL, casterGuid);
+ return true; // no hidden cooldown
+ }
+ // Damage counting
+ mod->m_amount-=damage;
+ return true;
+ }
+ switch(dummySpell->Id)
+ {
+ // Nightfall
+ case 18094:
+ case 18095:
+ {
+ target = this;
+ triggered_spell_id = 17941;
+ break;
+ }
+ //Soul Leech
+ case 30293:
+ case 30295:
+ case 30296:
+ {
+ // health
+ basepoints0 = int32(damage*triggeredByAura->GetModifier()->m_amount/100);
+ target = this;
+ triggered_spell_id = 30294;
+ break;
+ }
+ // Shadowflame (Voidheart Raiment set bonus)
+ case 37377:
+ {
+ triggered_spell_id = 37379;
+ break;
+ }
+ // Pet Healing (Corruptor Raiment or Rift Stalker Armor)
+ case 37381:
+ {
+ target = GetPet();
+ if(!target)
+ return false;
+
+ // heal amount
+ basepoints0 = damage * triggeredByAura->GetModifier()->m_amount/100;
+ triggered_spell_id = 37382;
+ break;
+ }
+ // Shadowflame Hellfire (Voidheart Raiment set bonus)
+ case 39437:
+ {
+ triggered_spell_id = 37378;
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_PRIEST:
+ {
+ // Vampiric Touch
+ if( dummySpell->SpellFamilyFlags & 0x0000040000000000LL )
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ // pVictim is caster of aura
+ if(triggeredByAura->GetCasterGUID() != pVictim->GetGUID())
+ return false;
+
+ // energize amount
+ basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100;
+ pVictim->CastCustomSpell(pVictim,34919,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+ return true; // no hidden cooldown
+ }
+ switch(dummySpell->Id)
+ {
+ // Vampiric Embrace
+ case 15286:
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ // pVictim is caster of aura
+ if(triggeredByAura->GetCasterGUID() != pVictim->GetGUID())
+ return false;
+
+ // heal amount
+ basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100;
+ pVictim->CastCustomSpell(pVictim,15290,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+ return true; // no hidden cooldown
+ }
+ // Priest Tier 6 Trinket (Ashtongue Talisman of Acumen)
+ case 40438:
+ {
+ // Shadow Word: Pain
+ if( procSpell->SpellFamilyFlags & 0x0000000000008000LL )
+ triggered_spell_id = 40441;
+ // Renew
+ else if( procSpell->SpellFamilyFlags & 0x0000000000000010LL )
+ triggered_spell_id = 40440;
+ else
+ return false;
+
+ target = this;
+ break;
+ }
+ // Oracle Healing Bonus ("Garments of the Oracle" set)
+ case 26169:
+ {
+ // heal amount
+ basepoints0 = int32(damage * 10/100);
+ target = this;
+ triggered_spell_id = 26170;
+ break;
+ }
+ // Frozen Shadoweave (Shadow's Embrace set) warning! its not only priest set
+ case 39372:
+ {
+ if(!procSpell || (GetSpellSchoolMask(procSpell) & (SPELL_SCHOOL_MASK_FROST | SPELL_SCHOOL_MASK_SHADOW))==0 )
+ return false;
+
+ // heal amount
+ basepoints0 = int32(damage * 2 / 100);
+ target = this;
+ triggered_spell_id = 39373;
+ break;
+ }
+ // Vestments of Faith (Priest Tier 3) - 4 pieces bonus
+ case 28809:
+ {
+ triggered_spell_id = 28810;
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ switch(dummySpell->Id)
+ {
+ // Healing Touch (Dreamwalker Raiment set)
+ case 28719:
+ {
+ // mana back
+ basepoints0 = int32(procSpell->manaCost * 30 / 100);
+ target = this;
+ triggered_spell_id = 28742;
+ break;
+ }
+ // Healing Touch Refund (Idol of Longevity trinket)
+ case 28847:
+ {
+ target = this;
+ triggered_spell_id = 28848;
+ break;
+ }
+ // Mana Restore (Malorne Raiment set / Malorne Regalia set)
+ case 37288:
+ case 37295:
+ {
+ target = this;
+ triggered_spell_id = 37238;
+ break;
+ }
+ // Druid Tier 6 Trinket
+ case 40442:
+ {
+ float chance;
+
+ // Starfire
+ if( procSpell->SpellFamilyFlags & 0x0000000000000004LL )
+ {
+ triggered_spell_id = 40445;
+ chance = 25.f;
+ }
+ // Rejuvenation
+ else if( procSpell->SpellFamilyFlags & 0x0000000000000010LL )
+ {
+ triggered_spell_id = 40446;
+ chance = 25.f;
+ }
+ // Mangle (cat/bear)
+ else if( procSpell->SpellFamilyFlags & 0x0000044000000000LL )
+ {
+ triggered_spell_id = 40452;
+ chance = 40.f;
+ }
+ else
+ return false;
+
+ if (!roll_chance_f(chance))
+ return false;
+
+ target = this;
+ break;
+ }
+ // Maim Interrupt
+ case 44835:
+ {
+ // Deadly Interrupt Effect
+ triggered_spell_id = 32747;
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ switch(dummySpell->Id)
+ {
+ // Deadly Throw Interrupt
+ case 32748:
+ {
+ // Prevent cast Deadly Throw Interrupt on self from last effect (apply dummy) of Deadly Throw
+ if(this == pVictim)
+ return false;
+
+ triggered_spell_id = 32747;
+ break;
+ }
+ }
+ // Quick Recovery
+ if( dummySpell->SpellIconID == 2116 )
+ {
+ if(!procSpell)
+ return false;
+
+ // only rogue's finishing moves (maybe need additional checks)
+ if( procSpell->SpellFamilyName!=SPELLFAMILY_ROGUE ||
+ (procSpell->SpellFamilyFlags & SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE) == 0)
+ return false;
+
+ // energy cost save
+ basepoints0 = procSpell->manaCost * triggeredByAura->GetModifier()->m_amount/100;
+ if(basepoints0 <= 0)
+ return false;
+
+ target = this;
+ triggered_spell_id = 31663;
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_HUNTER:
+ {
+ // Thrill of the Hunt
+ if ( dummySpell->SpellIconID == 2236 )
+ {
+ if(!procSpell)
+ return false;
+
+ // mana cost save
+ basepoints0 = procSpell->manaCost * 40/100;
+ if(basepoints0 <= 0)
+ return false;
+
+ target = this;
+ triggered_spell_id = 34720;
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ // Seal of Righteousness - melee proc dummy
+ if (dummySpell->SpellFamilyFlags&0x000000008000000LL && triggeredByAura->GetEffIndex()==0)
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ uint32 spellId;
+ switch (triggeredByAura->GetId())
+ {
+ case 21084: spellId = 25742; break; // Rank 1
+ case 20287: spellId = 25740; break; // Rank 2
+ case 20288: spellId = 25739; break; // Rank 3
+ case 20289: spellId = 25738; break; // Rank 4
+ case 20290: spellId = 25737; break; // Rank 5
+ case 20291: spellId = 25736; break; // Rank 6
+ case 20292: spellId = 25735; break; // Rank 7
+ case 20293: spellId = 25713; break; // Rank 8
+ case 27155: spellId = 27156; break; // Rank 9
+ default:
+ sLog.outError("Unit::HandleDummyAuraProc: non handled possibly SoR (Id = %u)", triggeredByAura->GetId());
+ return false;
+ }
+ Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
+ float speed = (item ? item->GetProto()->Delay : BASE_ATTACK_TIME)/1000.0f;
+
+ float damageBasePoints;
+ if(item && item->GetProto()->InventoryType == INVTYPE_2HWEAPON)
+ // two hand weapon
+ damageBasePoints=1.20f*triggeredByAura->GetModifier()->m_amount * 1.2f * 1.03f * speed/100.0f + 1;
+ else
+ // one hand weapon/no weapon
+ damageBasePoints=0.85f*ceil(triggeredByAura->GetModifier()->m_amount * 1.2f * 1.03f * speed/100.0f) - 1;
+
+ int32 damagePoint = int32(damageBasePoints + 0.03f * (GetWeaponDamageRange(BASE_ATTACK,MINDAMAGE)+GetWeaponDamageRange(BASE_ATTACK,MAXDAMAGE))/2.0f) + 1;
+
+ // apply damage bonuses manually
+ if(damagePoint >= 0)
+ damagePoint = SpellDamageBonus(pVictim, dummySpell, damagePoint, SPELL_DIRECT_DAMAGE);
+
+ CastCustomSpell(pVictim,spellId,&damagePoint,NULL,NULL,true,NULL, triggeredByAura);
+ return true; // no hidden cooldown
+ }
+ // Seal of Blood do damage trigger
+ if(dummySpell->SpellFamilyFlags & 0x0000040000000000LL)
+ {
+ switch(triggeredByAura->GetEffIndex())
+ {
+ case 0:
+ // prevent chain triggering
+ if(procSpell && procSpell->Id==31893 )
+ return false;
+
+ triggered_spell_id = 31893;
+ break;
+ case 1:
+ {
+ // damage
+ basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100;
+ target = this;
+ triggered_spell_id = 32221;
+ break;
+ }
+ }
+ }
+
+ switch(dummySpell->Id)
+ {
+ // Holy Power (Redemption Armor set)
+ case 28789:
+ {
+ if(!pVictim)
+ return false;
+
+ // Set class defined buff
+ switch (pVictim->getClass())
+ {
+ case CLASS_PALADIN:
+ case CLASS_PRIEST:
+ case CLASS_SHAMAN:
+ case CLASS_DRUID:
+ triggered_spell_id = 28795; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d.
+ break;
+ case CLASS_MAGE:
+ case CLASS_WARLOCK:
+ triggered_spell_id = 28793; // Increases the friendly target's spell damage and healing by up to $s1 for $d.
+ break;
+ case CLASS_HUNTER:
+ case CLASS_ROGUE:
+ triggered_spell_id = 28791; // Increases the friendly target's attack power by $s1 for $d.
+ break;
+ case CLASS_WARRIOR:
+ triggered_spell_id = 28790; // Increases the friendly target's armor
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ //Seal of Vengeance
+ case 31801:
+ {
+ if(effIndex != 0) // effect 1,2 used by seal unleashing code
+ return false;
+
+ triggered_spell_id = 31803;
+ break;
+ }
+ // Spiritual Att.
+ case 31785:
+ case 33776:
+ {
+ // if healed by another unit (pVictim)
+ if(this == pVictim)
+ return false;
+
+ // heal amount
+ basepoints0 = triggeredByAura->GetModifier()->m_amount*damage/100;
+ target = this;
+ triggered_spell_id = 31786;
+ break;
+ }
+ // Paladin Tier 6 Trinket (Ashtongue Talisman of Zeal)
+ case 40470:
+ {
+ if( !procSpell )
+ return false;
+
+ float chance;
+
+ // Flash of light/Holy light
+ if( procSpell->SpellFamilyFlags & 0x00000000C0000000LL)
+ {
+ triggered_spell_id = 40471;
+ chance = 15.f;
+ }
+ // Judgement
+ else if( procSpell->SpellFamilyFlags & 0x0000000000800000LL )
+ {
+ triggered_spell_id = 40472;
+ chance = 50.f;
+ }
+ else
+ return false;
+
+ if (!roll_chance_f(chance))
+ return false;
+
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_SHAMAN:
+ {
+ switch(dummySpell->Id)
+ {
+ // Totemic Power (The Earthshatterer set)
+ case 28823:
+ {
+ if( !pVictim )
+ return false;
+
+ // Set class defined buff
+ switch (pVictim->getClass())
+ {
+ case CLASS_PALADIN:
+ case CLASS_PRIEST:
+ case CLASS_SHAMAN:
+ case CLASS_DRUID:
+ triggered_spell_id = 28824; // Increases the friendly target's mana regeneration by $s1 per 5 sec. for $d.
+ break;
+ case CLASS_MAGE:
+ case CLASS_WARLOCK:
+ triggered_spell_id = 28825; // Increases the friendly target's spell damage and healing by up to $s1 for $d.
+ break;
+ case CLASS_HUNTER:
+ case CLASS_ROGUE:
+ triggered_spell_id = 28826; // Increases the friendly target's attack power by $s1 for $d.
+ break;
+ case CLASS_WARRIOR:
+ triggered_spell_id = 28827; // Increases the friendly target's armor
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ // Lesser Healing Wave (Totem of Flowing Water Relic)
+ case 28849:
+ {
+ target = this;
+ triggered_spell_id = 28850;
+ break;
+ }
+ // Windfury Weapon (Passive) 1-5 Ranks
+ case 33757:
+ {
+ if(GetTypeId()!=TYPEID_PLAYER)
+ return false;
+
+ if(!castItem || !castItem->IsEquipped())
+ return false;
+
+ // custom cooldown processing case
+ if( cooldown && ((Player*)this)->HasSpellCooldown(dummySpell->Id))
+ return false;
+
+ uint32 spellId;
+ switch (castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)))
+ {
+ case 283: spellId = 33757; break; //1 Rank
+ case 284: spellId = 33756; break; //2 Rank
+ case 525: spellId = 33755; break; //3 Rank
+ case 1669:spellId = 33754; break; //4 Rank
+ case 2636:spellId = 33727; break; //5 Rank
+ default:
+ {
+ sLog.outError("Unit::HandleDummyAuraProc: non handled item enchantment (rank?) %u for spell id: %u (Windfury)",
+ castItem->GetEnchantmentId(EnchantmentSlot(TEMP_ENCHANTMENT_SLOT)),dummySpell->Id);
+ return false;
+ }
+ }
+
+ SpellEntry const* windfurySpellEntry = sSpellStore.LookupEntry(spellId);
+ if(!windfurySpellEntry)
+ {
+ sLog.outError("Unit::HandleDummyAuraProc: non existed spell id: %u (Windfury)",spellId);
+ return false;
+ }
+
+ int32 extra_attack_power = CalculateSpellDamage(windfurySpellEntry,0,windfurySpellEntry->EffectBasePoints[0],pVictim);
+
+ // Off-Hand case
+ if ( castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND )
+ {
+ // Value gained from additional AP
+ basepoints0 = int32(extra_attack_power/14.0f * GetAttackTime(OFF_ATTACK)/1000/2);
+ triggered_spell_id = 33750;
+ }
+ // Main-Hand case
+ else
+ {
+ // Value gained from additional AP
+ basepoints0 = int32(extra_attack_power/14.0f * GetAttackTime(BASE_ATTACK)/1000);
+ triggered_spell_id = 25504;
+ }
+
+ // apply cooldown before cast to prevent processing itself
+ if( cooldown )
+ ((Player*)this)->AddSpellCooldown(dummySpell->Id,0,time(NULL) + cooldown);
+
+ // Attack Twice
+ for ( uint32 i = 0; i<2; ++i )
+ CastCustomSpell(pVictim,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+
+ return true;
+ }
+ // Shaman Tier 6 Trinket
+ case 40463:
+ {
+ if( !procSpell )
+ return false;
+
+ float chance;
+ if (procSpell->SpellFamilyFlags & 0x0000000000000001LL)
+ {
+ triggered_spell_id = 40465; // Lightning Bolt
+ chance = 15.f;
+ }
+ else if (procSpell->SpellFamilyFlags & 0x0000000000000080LL)
+ {
+ triggered_spell_id = 40465; // Lesser Healing Wave
+ chance = 10.f;
+ }
+ else if (procSpell->SpellFamilyFlags & 0x0000001000000000LL)
+ {
+ triggered_spell_id = 40466; // Stormstrike
+ chance = 50.f;
+ }
+ else
+ return false;
+
+ if (!roll_chance_f(chance))
+ return false;
+
+ target = this;
+ break;
+ }
+ }
+
+ // Earth Shield
+ if(dummySpell->SpellFamilyFlags==0x40000000000LL)
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ // heal
+ basepoints0 = triggeredByAura->GetModifier()->m_amount;
+ target = this;
+ triggered_spell_id = 379;
+ break;
+ }
+ // Lightning Overload
+ if (dummySpell->SpellIconID == 2018) // only this spell have SpellFamily Shaman SpellIconID == 2018 and dummy aura
+ {
+ if(!procSpell || GetTypeId() != TYPEID_PLAYER || !pVictim )
+ return false;
+
+ // custom cooldown processing case
+ if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(dummySpell->Id))
+ return false;
+
+ uint32 spellId = 0;
+ // Every Lightning Bolt and Chain Lightning spell have dublicate vs half damage and zero cost
+ switch (procSpell->Id)
+ {
+ // Lightning Bolt
+ case 403: spellId = 45284; break; // Rank 1
+ case 529: spellId = 45286; break; // Rank 2
+ case 548: spellId = 45287; break; // Rank 3
+ case 915: spellId = 45288; break; // Rank 4
+ case 943: spellId = 45289; break; // Rank 5
+ case 6041: spellId = 45290; break; // Rank 6
+ case 10391: spellId = 45291; break; // Rank 7
+ case 10392: spellId = 45292; break; // Rank 8
+ case 15207: spellId = 45293; break; // Rank 9
+ case 15208: spellId = 45294; break; // Rank 10
+ case 25448: spellId = 45295; break; // Rank 11
+ case 25449: spellId = 45296; break; // Rank 12
+ // Chain Lightning
+ case 421: spellId = 45297; break; // Rank 1
+ case 930: spellId = 45298; break; // Rank 2
+ case 2860: spellId = 45299; break; // Rank 3
+ case 10605: spellId = 45300; break; // Rank 4
+ case 25439: spellId = 45301; break; // Rank 5
+ case 25442: spellId = 45302; break; // Rank 6
+ default:
+ sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (LO)", procSpell->Id);
+ return false;
+ }
+ // No thread generated mod
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_THREAT;
+ mod->value = -100;
+ mod->type = SPELLMOD_PCT;
+ mod->spellId = dummySpell->Id;
+ mod->effectId = 0;
+ mod->lastAffected = NULL;
+ mod->mask = 0x0000000000000003LL;
+ mod->charges = 0;
+ ((Player*)this)->AddSpellMod(mod, true);
+
+ // Remove cooldown (Chain Lightning - have Category Recovery time)
+ if (procSpell->SpellFamilyFlags & 0x0000000000000002LL)
+ ((Player*)this)->RemoveSpellCooldown(spellId);
+
+ // Hmmm.. in most case spells alredy set half basepoints but...
+ // Lightning Bolt (2-10 rank) have full basepoint and half bonus from level
+ // As on wiki:
+ // BUG: Rank 2 to 10 (and maybe 11) of Lightning Bolt will proc another Bolt with FULL damage (not halved). This bug is known and will probably be fixed soon.
+ // So - no add changes :)
+ CastSpell(pVictim, spellId, true, castItem, triggeredByAura);
+
+ ((Player*)this)->AddSpellMod(mod, false);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(dummySpell->Id,0,time(NULL) + cooldown);
+
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // processed charge only counting case
+ if(!triggered_spell_id)
+ return true;
+
+ SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
+
+ if(!triggerEntry)
+ {
+ sLog.outError("Unit::HandleDummyAuraProc: Spell %u have not existed triggered spell %u",dummySpell->Id,triggered_spell_id);
+ return false;
+ }
+
+ // default case
+ if(!target || target!=this && !target->isAlive())
+ return false;
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
+ return false;
+
+ if(basepoints0)
+ CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+ else
+ CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
+
+ return true;
+}
+
+bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags,WeaponAttackType attackType, uint32 cooldown)
+{
+ SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto();
+
+ Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
+ ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
+
+ uint32 triggered_spell_id = auraSpellInfo->EffectTriggerSpell[triggeredByAura->GetEffIndex()];
+ Unit* target = !(procFlags & PROC_FLAG_HEAL) && IsPositiveSpell(triggered_spell_id) ? this : pVictim;
+ int32 basepoints0 = 0;
+
+ switch(auraSpellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ switch(auraSpellInfo->Id)
+ {
+ // Aegis of Preservation
+ case 23780:
+ //Aegis Heal (instead non-existed triggered spell)
+ triggered_spell_id = 23781;
+ target = this;
+ break;
+ // Elune's Touch (moonkin mana restore)
+ case 24905:
+ {
+ // Elune's Touch (instead non-existed triggered spell)
+ triggered_spell_id = 33926;
+ basepoints0 = int32(0.3f * GetTotalAttackPowerValue(BASE_ATTACK));
+ target = this;
+ break;
+ }
+ // Enlightenment
+ case 29601:
+ {
+ // only for cast with mana price
+ if(!procSpell || procSpell->powerType!=POWER_MANA || procSpell->manaCost==0 && procSpell->ManaCostPercentage==0 && procSpell->manaCostPerlevel==0)
+ return false;
+ break; // fall through to normal cast
+ }
+ // Health Restore
+ case 33510:
+ {
+ // at melee hit call std triggered spell
+ if(procFlags & PROC_FLAG_HIT_MELEE)
+ break; // fall through to normal cast
+
+ // Mark of Conquest - else (at range hit) called custom case
+ triggered_spell_id = 39557;
+ target = this;
+ break;
+ }
+ // Shaleskin
+ case 36576:
+ return true; // nothing to do
+ // Forgotten Knowledge (Blade of Wizardry)
+ case 38319:
+ // only for harmful enemy targeted spell
+ if(!pVictim || pVictim==this || !procSpell || IsPositiveSpell(procSpell->Id))
+ return false;
+ break; // fall through to normal cast
+ // Aura of Wrath (Darkmoon Card: Wrath trinket bonus)
+ case 39442:
+ {
+ // proc only at non-crit hits
+ if(procFlags & (PROC_FLAG_CRIT_MELEE|PROC_FLAG_CRIT_RANGED|PROC_FLAG_CRIT_SPELL))
+ return false;
+ break; // fall through to normal cast
+ }
+ // Augment Pain (Timbal's Focusing Crystal trinket bonus)
+ case 45054:
+ {
+ if(!procSpell)
+ return false;
+
+ //only periodic damage can trigger spell
+ bool found = false;
+ for(int j = 0; j < 3; ++j)
+ {
+ if( procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE ||
+ procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE_PERCENT ||
+ procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_LEECH )
+ {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ return false;
+
+ break; // fall through to normal cast
+ }
+ // Evasive Maneuvers (Commendation of Kael'thas)
+ case 45057:
+ {
+ // damage taken that reduces below 35% health
+ // does NOT mean you must have been >= 35% before
+ if (int32(GetHealth())-int32(damage) >= int32(GetMaxHealth()*0.35f))
+ return false;
+ break; // fall through to normal cast
+ }
+ }
+
+ switch(triggered_spell_id)
+ {
+ // Setup
+ case 15250:
+ {
+ // applied only for main target
+ if(!pVictim || pVictim != getVictim())
+ return false;
+
+ // continue normal case
+ break;
+ }
+ // Shamanistic Rage triggered spell
+ case 30824:
+ basepoints0 = int32(GetTotalAttackPowerValue(BASE_ATTACK)*triggeredByAura->GetModifier()->m_amount/100);
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_MAGE:
+ {
+ switch(auraSpellInfo->SpellIconID)
+ {
+ // Blazing Speed
+ case 2127:
+ //Blazing Speed (instead non-existed triggered spell)
+ triggered_spell_id = 31643;
+ target = this;
+ break;
+ }
+ switch(auraSpellInfo->Id)
+ {
+ // Persistent Shield (Scarab Brooch)
+ case 26467:
+ basepoints0 = int32(damage * 0.15f);
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_WARRIOR:
+ {
+ //Rampage
+ if((auraSpellInfo->SpellFamilyFlags & 0x100000) && auraSpellInfo->SpellIconID==2006)
+ {
+ //all ranks have effect[0]==AURA (Proc Trigger Spell, non-existed)
+ //and effect[1]==TriggerSpell
+ if(auraSpellInfo->Effect[1]!=SPELL_EFFECT_TRIGGER_SPELL)
+ {
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have wrong effect in RM",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+ triggered_spell_id = auraSpellInfo->EffectTriggerSpell[1];
+ break; // fall through to normal cast
+ }
+ break;
+ }
+ case SPELLFAMILY_WARLOCK:
+ {
+ // Pyroclasm
+ if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000 && auraSpellInfo->SpellIconID==1137)
+ {
+ // last case for Hellfire that damage caster also but don't must stun caster
+ if( pVictim == this )
+ return false;
+
+ // custom chnace
+ float chance = 0;
+ switch (triggeredByAura->GetId())
+ {
+ case 18096: chance = 13.0f; break;
+ case 18073: chance = 26.0f; break;
+ }
+ if (!roll_chance_f(chance))
+ return false;
+
+ // Pyroclasm (instead non-existed triggered spell)
+ triggered_spell_id = 18093;
+ target = pVictim;
+ break;
+ }
+ // Drain Soul
+ if(auraSpellInfo->SpellFamilyFlags & 0x0000000000004000)
+ {
+ bool found = false;
+ Unit::AuraList const& mAddFlatModifier = GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER);
+ for(Unit::AuraList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i)
+ {
+ //Improved Drain Soul
+ if ((*i)->GetModifier()->m_miscvalue == SPELLMOD_CHANCE_OF_SUCCESS && (*i)->GetSpellProto()->SpellIconID == 113)
+ {
+ int32 value2 = CalculateSpellDamage((*i)->GetSpellProto(),2,(*i)->GetSpellProto()->EffectBasePoints[2],this);
+ basepoints0 = value2 * GetMaxPower(POWER_MANA) / 100;
+
+ // Drain Soul
+ triggered_spell_id = 18371;
+ target = this;
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ return false;
+ break; // fall through to normal cast
+ }
+ break;
+ }
+ case SPELLFAMILY_PRIEST:
+ {
+ //Blessed Recovery
+ if(auraSpellInfo->SpellFamilyFlags == 0x00000000LL && auraSpellInfo->SpellIconID==1875)
+ {
+ switch (triggeredByAura->GetSpellProto()->Id)
+ {
+ case 27811: triggered_spell_id = 27813; break;
+ case 27815: triggered_spell_id = 27817; break;
+ case 27816: triggered_spell_id = 27818; break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in BR",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+
+ int32 heal_amount = damage * triggeredByAura->GetModifier()->m_amount / 100;
+ basepoints0 = heal_amount/3;
+ target = this;
+ break;
+ }
+ // Shadowguard
+ if((auraSpellInfo->SpellFamilyFlags & 0x80000000LL) && auraSpellInfo->SpellVisual==7958)
+ {
+ switch(triggeredByAura->GetSpellProto()->Id)
+ {
+ case 18137:
+ triggered_spell_id = 28377; break; // Rank 1
+ case 19308:
+ triggered_spell_id = 28378; break; // Rank 2
+ case 19309:
+ triggered_spell_id = 28379; break; // Rank 3
+ case 19310:
+ triggered_spell_id = 28380; break; // Rank 4
+ case 19311:
+ triggered_spell_id = 28381; break; // Rank 5
+ case 19312:
+ triggered_spell_id = 28382; break; // Rank 6
+ case 25477:
+ triggered_spell_id = 28385; break; // Rank 7
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in SG",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+ target = pVictim;
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_DRUID:
+ {
+ switch(auraSpellInfo->Id)
+ {
+ // Leader of the Pack (triggering Improved Leader of the Pack heal)
+ case 24932:
+ {
+ if (triggeredByAura->GetModifier()->m_amount == 0)
+ return false;
+ basepoints0 = triggeredByAura->GetModifier()->m_amount * GetMaxHealth() / 100;
+ triggered_spell_id = 34299;
+ break;
+ };
+ // Druid Forms Trinket (Druid Tier5 Trinket, triggers different spells per Form)
+ case 37336:
+ {
+ switch(m_form)
+ {
+ case FORM_BEAR:
+ case FORM_DIREBEAR:
+ triggered_spell_id=37340; break;// Ursine Blessing
+ case FORM_CAT:
+ triggered_spell_id=37341; break;// Feline Blessing
+ case FORM_TREE:
+ triggered_spell_id=37342; break;// Slyvan Blessing
+ case FORM_MOONKIN:
+ triggered_spell_id=37343; break;// Lunar Blessing
+ case FORM_NONE:
+ triggered_spell_id=37344; break;// Cenarion Blessing (for caster form, except FORM_MOONKIN)
+ default:
+ return false;
+ }
+
+ target = this;
+ break;
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_ROGUE:
+ {
+ if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000LL)
+ {
+ switch(auraSpellInfo->SpellIconID)
+ {
+ // Combat Potency
+ case 2260:
+ {
+ // skip non offhand attacks
+ if(attackType!=OFF_ATTACK)
+ return false;
+ break; // fall through to normal cast
+ }
+ }
+ }
+ break;
+ }
+ case SPELLFAMILY_PALADIN:
+ {
+ if(auraSpellInfo->SpellFamilyFlags == 0x00000000LL)
+ {
+ switch(auraSpellInfo->Id)
+ {
+ // Lightning Capacitor
+ case 37657:
+ {
+ // trinket ProcTriggerSpell but for safe checks for player
+ if(!castItem || !pVictim || !pVictim->isAlive() || GetTypeId()!=TYPEID_PLAYER)
+ return false;
+
+ if(((Player*)this)->HasSpellCooldown(37657))
+ return false;
+
+ // stacking
+ CastSpell(this, 37658, true, castItem, triggeredByAura);
+ // 2.5s cooldown before it can stack again, current system allow 1 sec step in cooldown
+ ((Player*)this)->AddSpellCooldown(37657,0,time(NULL)+(roll_chance_i(50) ? 2 : 3));
+
+ // counting
+ uint32 count = 0;
+ AuraList const& dummyAura = GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator itr = dummyAura.begin(); itr != dummyAura.end(); ++itr)
+ if((*itr)->GetId()==37658)
+ ++count;
+
+ // release at 3 aura in stack
+ if(count <= 2)
+ return true; // main triggered spell casted anyway
+
+ RemoveAurasDueToSpell(37658);
+ CastSpell(pVictim, 37661, true, castItem, triggeredByAura);
+ return true;
+ }
+ // Healing Discount
+ case 37705:
+ // Healing Trance (instead non-existed triggered spell)
+ triggered_spell_id = 37706;
+ target = this;
+ break;
+ // HoTs on Heals (Fel Reaver's Piston trinket)
+ case 38299:
+ {
+ // at direct heal effect
+ if(!procSpell || !IsSpellHaveEffect(procSpell,SPELL_EFFECT_HEAL))
+ return false;
+
+ // single proc at time
+ AuraList const& scAuras = GetSingleCastAuras();
+ for(AuraList::const_iterator itr = scAuras.begin(); itr != scAuras.end(); ++itr)
+ if((*itr)->GetId()==triggered_spell_id)
+ return false;
+
+ // positive cast at victim instead self
+ target = pVictim;
+ break;
+ }
+ }
+ switch(auraSpellInfo->SpellIconID)
+ {
+ case 241:
+ {
+ switch(auraSpellInfo->EffectTriggerSpell[0])
+ {
+ //Illumination
+ case 18350:
+ {
+ if(!procSpell)
+ return false;
+
+ // procspell is triggered spell but we need mana cost of original casted spell
+ uint32 originalSpellId = procSpell->Id;
+
+ // Holy Shock
+ if(procSpell->SpellFamilyName == SPELLFAMILY_PALADIN)
+ {
+ if(procSpell->SpellFamilyFlags & 0x0001000000000000LL)
+ {
+ switch(procSpell->Id)
+ {
+ case 25914: originalSpellId = 20473; break;
+ case 25913: originalSpellId = 20929; break;
+ case 25903: originalSpellId = 20930; break;
+ case 27175: originalSpellId = 27174; break;
+ case 33074: originalSpellId = 33072; break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in HShock",procSpell->Id);
+ return false;
+ }
+ }
+ }
+
+ SpellEntry const *originalSpell = sSpellStore.LookupEntry(originalSpellId);
+ if(!originalSpell)
+ {
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u unknown but selected as original in Illu",originalSpellId);
+ return false;
+ }
+
+ // percent stored in effect 1 (class scripts) base points
+ int32 percent = auraSpellInfo->EffectBasePoints[1]+1;
+
+ basepoints0 = originalSpell->manaCost*percent/100;
+ triggered_spell_id = 20272;
+ target = this;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ if(auraSpellInfo->SpellFamilyFlags & 0x00080000)
+ {
+ switch(auraSpellInfo->SpellIconID)
+ {
+ //Judgement of Wisdom (overwrite non existing triggered spell call in spell.dbc
+ case 206:
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ uint32 spell = 0;
+ switch(triggeredByAura->GetSpellProto()->Id)
+ {
+ case 20186:
+ triggered_spell_id = 20268; // Rank 1
+ break;
+ case 20354:
+ triggered_spell_id = 20352; // Rank 2
+ break;
+ case 20355:
+ triggered_spell_id = 20353; // Rank 3
+ break;
+ case 27164:
+ triggered_spell_id = 27165; // Rank 4
+ break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in JoW",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+
+ pVictim->CastSpell(pVictim,triggered_spell_id,true,castItem,triggeredByAura,GetGUID());
+ return true; // no hidden cooldown
+ }
+ //Judgement of Light
+ case 299:
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ // overwrite non existing triggered spell call in spell.dbc
+ uint32 spell = 0;
+ switch(triggeredByAura->GetSpellProto()->Id)
+ {
+ case 20185:
+ triggered_spell_id = 20267; // Rank 1
+ break;
+ case 20344:
+ triggered_spell_id = 20341; // Rank 2
+ break;
+ case 20345:
+ triggered_spell_id = 20342; // Rank 3
+ break;
+ case 20346:
+ triggered_spell_id = 20343; // Rank 4
+ break;
+ case 27162:
+ triggered_spell_id = 27163; // Rank 5
+ break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in JoL",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+ pVictim->CastSpell(pVictim,triggered_spell_id,true,castItem,triggeredByAura,GetGUID());
+ return true; // no hidden cooldown
+ }
+ }
+ }
+ // custom check for proc spell
+ switch(auraSpellInfo->Id)
+ {
+ // Bonus Healing (item spell)
+ case 40971:
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ // bonus if health < 50%
+ if(pVictim->GetHealth() >= pVictim->GetMaxHealth()*triggeredByAura->GetModifier()->m_amount/100)
+ return false;
+
+ // cast at target positive spell
+ target = pVictim;
+ break;
+ }
+ }
+ switch(triggered_spell_id)
+ {
+ // Seal of Command
+ case 20424:
+ // prevent chain of triggered spell from same triggered spell
+ if(procSpell && procSpell->Id==20424)
+ return false;
+ break;
+ }
+ break;
+ }
+ case SPELLFAMILY_SHAMAN:
+ {
+ if(auraSpellInfo->SpellFamilyFlags == 0x0000000000000000)
+ {
+ switch(auraSpellInfo->SpellIconID)
+ {
+ case 19:
+ {
+ switch(auraSpellInfo->Id)
+ {
+ case 23551: // Lightning Shield - Tier2: 8 pieces proc shield
+ {
+ // Lightning Shield (overwrite non existing triggered spell call in spell.dbc)
+ triggered_spell_id = 23552;
+ target = pVictim;
+ break;
+ }
+ case 23552: // Lightning Shield - trigger shield damage
+ {
+ // Lightning Shield (overwrite non existing triggered spell call in spell.dbc)
+ triggered_spell_id = 27635;
+ target = pVictim;
+ break;
+ }
+ }
+ break;
+ }
+ // Mana Surge (Shaman T1 bonus)
+ case 87:
+ {
+ if(!procSpell)
+ return false;
+
+ basepoints0 = procSpell->manaCost * 35/100;
+ triggered_spell_id = 23571;
+ target = this;
+ break;
+ }
+ //Nature's Guardian
+ case 2013:
+ {
+ if(GetTypeId()!=TYPEID_PLAYER)
+ return false;
+
+ // damage taken that reduces below 30% health
+ // does NOT mean you must have been >= 30% before
+ if (10*(int32(GetHealth())-int32(damage)) >= 3*GetMaxHealth())
+ return false;
+
+ triggered_spell_id = 31616;
+
+ // need check cooldown now
+ if( cooldown && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
+ return false;
+
+ basepoints0 = triggeredByAura->GetModifier()->m_amount * GetMaxHealth() / 100;
+ target = this;
+ if(pVictim && pVictim->isAlive())
+ pVictim->getThreatManager().modifyThreatPercent(this,-10);
+ break;
+ }
+ }
+ }
+
+ // Water Shield (we can't set cooldown for main spell - it's player casted spell
+ if((auraSpellInfo->SpellFamilyFlags & 0x0000002000000000LL) && auraSpellInfo->SpellVisual==7358)
+ {
+ target = this;
+ break;
+ }
+
+ // Lightning Shield
+ if((auraSpellInfo->SpellFamilyFlags & 0x00000400) && auraSpellInfo->SpellVisual==37)
+ {
+ // overwrite non existing triggered spell call in spell.dbc
+ switch(triggeredByAura->GetSpellProto()->Id)
+ {
+ case 324:
+ triggered_spell_id = 26364; break; // Rank 1
+ case 325:
+ triggered_spell_id = 26365; break; // Rank 2
+ case 905:
+ triggered_spell_id = 26366; break; // Rank 3
+ case 945:
+ triggered_spell_id = 26367; break; // Rank 4
+ case 8134:
+ triggered_spell_id = 26369; break; // Rank 5
+ case 10431:
+ triggered_spell_id = 26370; break; // Rank 6
+ case 10432:
+ triggered_spell_id = 26363; break; // Rank 7
+ case 25469:
+ triggered_spell_id = 26371; break; // Rank 8
+ case 25472:
+ triggered_spell_id = 26372; break; // Rank 9
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in LShield",triggeredByAura->GetSpellProto()->Id);
+ return false;
+ }
+
+ target = pVictim;
+ break;
+ }
+ break;
+ }
+ }
+
+ // standard non-dummy case
+ if(!triggered_spell_id)
+ {
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex());
+ return false;
+ }
+
+ SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
+
+ if(!triggerEntry)
+ {
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have not existed EffectTriggered[%d]=%u, not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex(),triggered_spell_id);
+ return false;
+ }
+
+ // not allow proc extra attack spell at extra attack
+ if( m_extraAttacks && IsSpellHaveEffect(triggerEntry,SPELL_EFFECT_ADD_EXTRA_ATTACKS) )
+ return false;
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
+ return false;
+
+ // default case
+ if(!target || target!=this && !target->isAlive())
+ return false;
+
+ if(basepoints0)
+ CastCustomSpell(target,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+ else
+ CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
+
+ return true;
+}
+
+bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, int32 scriptId, uint32 damage, Aura *triggeredByAura, SpellEntry const *procSpell, uint32 cooldown)
+{
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+
+ Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
+ ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
+
+ uint32 triggered_spell_id = 0;
+
+ switch(scriptId)
+ {
+ case 836: // Improved Blizzard (Rank 1)
+ {
+ if( !procSpell || procSpell->SpellVisual!=9487 )
+ return false;
+ triggered_spell_id = 12484;
+ break;
+ }
+ case 988: // Improved Blizzard (Rank 2)
+ {
+ if( !procSpell || procSpell->SpellVisual!=9487 )
+ return false;
+ triggered_spell_id = 12485;
+ break;
+ }
+ case 989: // Improved Blizzard (Rank 3)
+ {
+ if( !procSpell || procSpell->SpellVisual!=9487 )
+ return false;
+ triggered_spell_id = 12486;
+ break;
+ }
+ case 4086: // Improved Mend Pet (Rank 1)
+ case 4087: // Improved Mend Pet (Rank 2)
+ {
+ int32 chance = triggeredByAura->GetSpellProto()->EffectBasePoints[triggeredByAura->GetEffIndex()];
+ if(!roll_chance_i(chance))
+ return false;
+
+ triggered_spell_id = 24406;
+ break;
+ }
+ case 4533: // Dreamwalker Raiment 2 pieces bonus
+ {
+ // Chance 50%
+ if (!roll_chance_i(50))
+ return false;
+
+ switch (pVictim->getPowerType())
+ {
+ case POWER_MANA: triggered_spell_id = 28722; break;
+ case POWER_RAGE: triggered_spell_id = 28723; break;
+ case POWER_ENERGY: triggered_spell_id = 28724; break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case 4537: // Dreamwalker Raiment 6 pieces bonus
+ triggered_spell_id = 28750; // Blessing of the Claw
+ break;
+ case 5497: // Improved Mana Gems (Serpent-Coil Braid)
+ triggered_spell_id = 37445; // Mana Surge
+ break;
+ }
+
+ // not processed
+ if(!triggered_spell_id)
+ return false;
+
+ // standard non-dummy case
+ SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
+
+ if(!triggerEntry)
+ {
+ sLog.outError("Unit::HandleOverrideClassScriptAuraProc: Spell %u triggering for class script id %u",triggered_spell_id,scriptId);
+ return false;
+ }
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(triggered_spell_id))
+ return false;
+
+ CastSpell(pVictim, triggered_spell_id, true, castItem, triggeredByAura);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
+
+ return true;
+}
+
+void Unit::setPowerType(Powers new_powertype)
+{
+ SetByteValue(UNIT_FIELD_BYTES_0, 3, new_powertype);
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POWER_TYPE);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_POWER_TYPE);
+ }
+ }
+
+ switch(new_powertype)
+ {
+ default:
+ case POWER_MANA:
+ break;
+ case POWER_RAGE:
+ SetMaxPower(POWER_RAGE,GetCreatePowers(POWER_RAGE));
+ SetPower( POWER_RAGE,0);
+ break;
+ case POWER_FOCUS:
+ SetMaxPower(POWER_FOCUS,GetCreatePowers(POWER_FOCUS));
+ SetPower( POWER_FOCUS,GetCreatePowers(POWER_FOCUS));
+ break;
+ case POWER_ENERGY:
+ SetMaxPower(POWER_ENERGY,GetCreatePowers(POWER_ENERGY));
+ SetPower( POWER_ENERGY,0);
+ break;
+ case POWER_HAPPINESS:
+ SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
+ SetPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
+ break;
+ }
+}
+
+FactionTemplateEntry const* Unit::getFactionTemplateEntry() const
+{
+ FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(getFaction());
+ if(!entry)
+ {
+ static uint64 guid = 0; // prevent repeating spam same faction problem
+
+ if(GetGUID() != guid)
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ sLog.outError("Player %s have invalid faction (faction template id) #%u", ((Player*)this)->GetName(), getFaction());
+ else
+ sLog.outError("Creature (template id: %u) have invalid faction (faction template id) #%u", ((Creature*)this)->GetCreatureInfo()->Entry, getFaction());
+ guid = GetGUID();
+ }
+ }
+ return entry;
+}
+
+bool Unit::IsHostileTo(Unit const* unit) const
+{
+ // always non-hostile to self
+ if(unit==this)
+ return false;
+
+ // always non-hostile to GM in GM mode
+ if(unit->GetTypeId()==TYPEID_PLAYER && ((Player const*)unit)->isGameMaster())
+ return false;
+
+ // always hostile to enemy
+ if(getVictim()==unit || unit->getVictim()==this)
+ return true;
+
+ // test pet/charm masters instead pers/charmeds
+ Unit const* testerOwner = GetCharmerOrOwner();
+ Unit const* targetOwner = unit->GetCharmerOrOwner();
+
+ // always hostile to owner's enemy
+ if(testerOwner && (testerOwner->getVictim()==unit || unit->getVictim()==testerOwner))
+ return true;
+
+ // always hostile to enemy owner
+ if(targetOwner && (getVictim()==targetOwner || targetOwner->getVictim()==this))
+ return true;
+
+ // always hostile to owner of owner's enemy
+ if(testerOwner && targetOwner && (testerOwner->getVictim()==targetOwner || targetOwner->getVictim()==testerOwner))
+ return true;
+
+ Unit const* tester = testerOwner ? testerOwner : this;
+ Unit const* target = targetOwner ? targetOwner : unit;
+
+ // always non-hostile to target with common owner, or to owner/pet
+ if(tester==target)
+ return false;
+
+ // special cases (Duel, etc)
+ if(tester->GetTypeId()==TYPEID_PLAYER && target->GetTypeId()==TYPEID_PLAYER)
+ {
+ Player const* pTester = (Player const*)tester;
+ Player const* pTarget = (Player const*)target;
+
+ // Duel
+ if(pTester->duel && pTester->duel->opponent == pTarget && pTester->duel->startTime != 0)
+ return true;
+
+ // Group
+ if(pTester->GetGroup() && pTester->GetGroup()==pTarget->GetGroup())
+ return false;
+
+ // Sanctuary
+ if(pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY) && pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY))
+ return false;
+
+ // PvP FFA state
+ if(pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP))
+ return true;
+
+ //= PvP states
+ // Green/Blue (can't attack)
+ if(pTester->GetTeam()==pTarget->GetTeam())
+ return false;
+
+ // Red (can attack) if true, Blue/Yellow (can't attack) in another case
+ return pTester->IsPvP() && pTarget->IsPvP();
+ }
+
+ // faction base cases
+ FactionTemplateEntry const*tester_faction = tester->getFactionTemplateEntry();
+ FactionTemplateEntry const*target_faction = target->getFactionTemplateEntry();
+ if(!tester_faction || !target_faction)
+ return false;
+
+ if(target->isAttackingPlayer() && tester->IsContestedGuard())
+ return true;
+
+ // PvC forced reaction and reputation case
+ if(tester->GetTypeId()==TYPEID_PLAYER)
+ {
+ // forced reaction
+ ForcedReactions::const_iterator forceItr = ((Player*)tester)->m_forcedReactions.find(target_faction->faction);
+ if(forceItr!=((Player*)tester)->m_forcedReactions.end())
+ return forceItr->second <= REP_HOSTILE;
+
+ // if faction have reputation then hostile state for tester at 100% dependent from at_war state
+ if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction))
+ if(raw_target_faction->reputationListID >=0)
+ if(FactionState const* factionState = ((Player*)tester)->GetFactionState(raw_target_faction))
+ return (factionState->Flags & FACTION_FLAG_AT_WAR);
+ }
+ // CvP forced reaction and reputation case
+ else if(target->GetTypeId()==TYPEID_PLAYER)
+ {
+ // forced reaction
+ ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction);
+ if(forceItr!=((Player const*)target)->m_forcedReactions.end())
+ return forceItr->second <= REP_HOSTILE;
+
+ // apply reputation state
+ FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction);
+ if(raw_tester_faction && raw_tester_faction->reputationListID >=0 )
+ return ((Player const*)target)->GetReputationRank(raw_tester_faction) <= REP_HOSTILE;
+ }
+
+ // common faction based case (CvC,PvC,CvP)
+ return tester_faction->IsHostileTo(*target_faction);
+}
+
+bool Unit::IsFriendlyTo(Unit const* unit) const
+{
+ // always friendly to self
+ if(unit==this)
+ return true;
+
+ // always friendly to GM in GM mode
+ if(unit->GetTypeId()==TYPEID_PLAYER && ((Player const*)unit)->isGameMaster())
+ return true;
+
+ // always non-friendly to enemy
+ if(getVictim()==unit || unit->getVictim()==this)
+ return false;
+
+ // test pet/charm masters instead pers/charmeds
+ Unit const* testerOwner = GetCharmerOrOwner();
+ Unit const* targetOwner = unit->GetCharmerOrOwner();
+
+ // always non-friendly to owner's enemy
+ if(testerOwner && (testerOwner->getVictim()==unit || unit->getVictim()==testerOwner))
+ return false;
+
+ // always non-friendly to enemy owner
+ if(targetOwner && (getVictim()==targetOwner || targetOwner->getVictim()==this))
+ return false;
+
+ // always non-friendly to owner of owner's enemy
+ if(testerOwner && targetOwner && (testerOwner->getVictim()==targetOwner || targetOwner->getVictim()==testerOwner))
+ return false;
+
+ Unit const* tester = testerOwner ? testerOwner : this;
+ Unit const* target = targetOwner ? targetOwner : unit;
+
+ // always friendly to target with common owner, or to owner/pet
+ if(tester==target)
+ return true;
+
+ // special cases (Duel)
+ if(tester->GetTypeId()==TYPEID_PLAYER && target->GetTypeId()==TYPEID_PLAYER)
+ {
+ Player const* pTester = (Player const*)tester;
+ Player const* pTarget = (Player const*)target;
+
+ // Duel
+ if(pTester->duel && pTester->duel->opponent == target && pTester->duel->startTime != 0)
+ return false;
+
+ // Group
+ if(pTester->GetGroup() && pTester->GetGroup()==pTarget->GetGroup())
+ return true;
+
+ // Sanctuary
+ if(pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY) && pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_SANCTUARY))
+ return true;
+
+ // PvP FFA state
+ if(pTester->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP) && pTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP))
+ return false;
+
+ //= PvP states
+ // Green/Blue (non-attackable)
+ if(pTester->GetTeam()==pTarget->GetTeam())
+ return true;
+
+ // Blue (friendly/non-attackable) if not PVP, or Yellow/Red in another case (attackable)
+ return !pTarget->IsPvP();
+ }
+
+ // faction base cases
+ FactionTemplateEntry const*tester_faction = tester->getFactionTemplateEntry();
+ FactionTemplateEntry const*target_faction = target->getFactionTemplateEntry();
+ if(!tester_faction || !target_faction)
+ return false;
+
+ if(target->isAttackingPlayer() && tester->IsContestedGuard())
+ return false;
+
+ // PvC forced reaction and reputation case
+ if(tester->GetTypeId()==TYPEID_PLAYER)
+ {
+ // forced reaction
+ ForcedReactions::const_iterator forceItr = ((Player const*)tester)->m_forcedReactions.find(target_faction->faction);
+ if(forceItr!=((Player const*)tester)->m_forcedReactions.end())
+ return forceItr->second >= REP_FRIENDLY;
+
+ // if faction have reputation then friendly state for tester at 100% dependent from at_war state
+ if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction))
+ if(raw_target_faction->reputationListID >=0)
+ if(FactionState const* FactionState = ((Player*)tester)->GetFactionState(raw_target_faction))
+ return !(FactionState->Flags & FACTION_FLAG_AT_WAR);
+ }
+ // CvP forced reaction and reputation case
+ else if(target->GetTypeId()==TYPEID_PLAYER)
+ {
+ // forced reaction
+ ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction);
+ if(forceItr!=((Player const*)target)->m_forcedReactions.end())
+ return forceItr->second >= REP_FRIENDLY;
+
+ // apply reputation state
+ if(FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction))
+ if(raw_tester_faction->reputationListID >=0 )
+ return ((Player const*)target)->GetReputationRank(raw_tester_faction) >= REP_FRIENDLY;
+ }
+
+ // common faction based case (CvC,PvC,CvP)
+ return tester_faction->IsFriendlyTo(*target_faction);
+}
+
+bool Unit::IsHostileToPlayers() const
+{
+ FactionTemplateEntry const* my_faction = getFactionTemplateEntry();
+ if(!my_faction)
+ return false;
+
+ FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction);
+ if(raw_faction && raw_faction->reputationListID >=0 )
+ return false;
+
+ return my_faction->IsHostileToPlayers();
+}
+
+bool Unit::IsNeutralToAll() const
+{
+ FactionTemplateEntry const* my_faction = getFactionTemplateEntry();
+ if(!my_faction)
+ return true;
+
+ FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction);
+ if(raw_faction && raw_faction->reputationListID >=0 )
+ return false;
+
+ return my_faction->IsNeutralToAll();
+}
+
+bool Unit::Attack(Unit *victim, bool meleeAttack)
+{
+ if(!victim || victim == this)
+ return false;
+
+ // dead units can neither attack nor be attacked
+ if(!isAlive() || !victim->isAlive())
+ return false;
+
+ // player cannot attack in mount state
+ if(GetTypeId()==TYPEID_PLAYER && IsMounted())
+ return false;
+
+ // nobody can attack GM in GM-mode
+ if(victim->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(((Player*)victim)->isGameMaster())
+ return false;
+ }
+ else
+ {
+ if(((Creature*)victim)->IsInEvadeMode())
+ return false;
+ }
+
+ // remove SPELL_AURA_MOD_UNATTACKABLE at attack (in case non-interruptible spells stun aura applied also that not let attack)
+ if(HasAuraType(SPELL_AURA_MOD_UNATTACKABLE))
+ RemoveSpellsCausingAura(SPELL_AURA_MOD_UNATTACKABLE);
+
+ if (m_attacking)
+ {
+ if (m_attacking == victim)
+ {
+ // switch to melee attack from ranged/magic
+ if( meleeAttack && !hasUnitState(UNIT_STAT_MELEE_ATTACKING) )
+ {
+ addUnitState(UNIT_STAT_MELEE_ATTACKING);
+ SendAttackStart(victim);
+ return true;
+ }
+ return false;
+ }
+ AttackStop();
+ }
+
+ //Set our target
+ SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID());
+
+ if(meleeAttack)
+ addUnitState(UNIT_STAT_MELEE_ATTACKING);
+ m_attacking = victim;
+ m_attacking->_addAttacker(this);
+
+ if(m_attacking->GetTypeId()==TYPEID_UNIT && ((Creature*)m_attacking)->AI())
+ ((Creature*)m_attacking)->AI()->AttackedBy(this);
+
+ if(GetTypeId()==TYPEID_UNIT)
+ {
+ WorldPacket data(SMSG_AI_REACTION, 12);
+ data << GetGUID();
+ data << uint32(AI_REACTION_AGGRO); // Aggro sound
+ ((WorldObject*)this)->SendMessageToSet(&data, true);
+
+ ((Creature*)this)->CallAssistence();
+ ((Creature*)this)->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ());
+ }
+
+ // delay offhand weapon attack to next attack time
+ if(haveOffhandWeapon())
+ resetAttackTimer(OFF_ATTACK);
+
+ if(meleeAttack)
+ SendAttackStart(victim);
+
+ return true;
+}
+
+bool Unit::AttackStop()
+{
+ if (!m_attacking)
+ return false;
+
+ Unit* victim = m_attacking;
+
+ m_attacking->_removeAttacker(this);
+ m_attacking = NULL;
+
+ //Clear our target
+ SetUInt64Value(UNIT_FIELD_TARGET, 0);
+
+ clearUnitState(UNIT_STAT_MELEE_ATTACKING);
+
+ InterruptSpell(CURRENT_MELEE_SPELL);
+
+ if( GetTypeId()==TYPEID_UNIT )
+ {
+ // reset call assistance
+ ((Creature*)this)->SetNoCallAssistence(false);
+ }
+
+ SendAttackStop(victim);
+
+ return true;
+}
+
+void Unit::CombatStop(bool cast)
+{
+ if(cast& IsNonMeleeSpellCasted(false))
+ InterruptNonMeleeSpells(false);
+
+ AttackStop();
+ RemoveAllAttackers();
+ if( GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel
+ ClearInCombat();
+}
+
+void Unit::CombatStopWithPets(bool cast)
+{
+ CombatStop(cast);
+ if(Pet* pet = GetPet())
+ pet->CombatStop(cast);
+ if(Unit* charm = GetCharm())
+ charm->CombatStop(cast);
+ if(GetTypeId()==TYPEID_PLAYER)
+ {
+ GuardianPetList const& guardians = ((Player*)this)->GetGuardians();
+ for(GuardianPetList::const_iterator itr = guardians.begin(); itr != guardians.end(); ++itr)
+ if(Unit* guardian = Unit::GetUnit(*this,*itr))
+ guardian->CombatStop(cast);
+ }
+}
+
+bool Unit::isAttackingPlayer() const
+{
+ if(hasUnitState(UNIT_STAT_ATTACK_PLAYER))
+ return true;
+
+ Pet* pet = GetPet();
+ if(pet && pet->isAttackingPlayer())
+ return true;
+
+ Unit* charmed = GetCharm();
+ if(charmed && charmed->isAttackingPlayer())
+ return true;
+
+ for (int8 i = 0; i < MAX_TOTEM; i++)
+ {
+ if(m_TotemSlot[i])
+ {
+ Creature *totem = ObjectAccessor::GetCreature(*this, m_TotemSlot[i]);
+ if(totem && totem->isAttackingPlayer())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Unit::RemoveAllAttackers()
+{
+ while (!m_attackers.empty())
+ {
+ AttackerSet::iterator iter = m_attackers.begin();
+ if(!(*iter)->AttackStop())
+ {
+ sLog.outError("WORLD: Unit has an attacker that isnt attacking it!");
+ m_attackers.erase(iter);
+ }
+ }
+}
+
+void Unit::ModifyAuraState(AuraState flag, bool apply)
+{
+ if (apply)
+ {
+ if (!HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)))
+ {
+ SetFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1));
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ const PlayerSpellMap& sp_list = ((Player*)this)->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 || !IsPassiveSpell(itr->first)) continue;
+ if (spellInfo->CasterAuraState == flag)
+ CastSpell(this, itr->first, true, NULL);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (HasFlag(UNIT_FIELD_AURASTATE,1<<(flag-1)))
+ {
+ RemoveFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1));
+ Unit::AuraMap& tAuras = GetAuras();
+ for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();)
+ {
+ SpellEntry const* spellProto = (*itr).second->GetSpellProto();
+ if (spellProto->CasterAuraState == flag)
+ {
+ // exceptions (applied at state but not removed at state change)
+ // Rampage
+ if(spellProto->SpellIconID==2006 && spellProto->SpellFamilyName==SPELLFAMILY_WARRIOR && spellProto->SpellFamilyFlags==0x100000)
+ {
+ ++itr;
+ continue;
+ }
+
+ RemoveAura(itr);
+ }
+ else
+ ++itr;
+ }
+ }
+ }
+}
+
+Unit *Unit::GetOwner() const
+{
+ uint64 ownerid = GetOwnerGUID();
+ if(!ownerid)
+ return NULL;
+ return ObjectAccessor::GetUnit(*this, ownerid);
+}
+
+Unit *Unit::GetCharmer() const
+{
+ if(uint64 charmerid = GetCharmerGUID())
+ return ObjectAccessor::GetUnit(*this, charmerid);
+ return NULL;
+}
+
+Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself()
+{
+ uint64 guid = GetCharmerOrOwnerGUID();
+ if(IS_PLAYER_GUID(guid))
+ return ObjectAccessor::GetPlayer(*this, guid);
+
+ return GetTypeId()==TYPEID_PLAYER ? (Player*)this : NULL;
+}
+
+Pet* Unit::GetPet() const
+{
+ if(uint64 pet_guid = GetPetGUID())
+ {
+ if(Pet* pet = ObjectAccessor::GetPet(pet_guid))
+ return pet;
+
+ sLog.outError("Unit::GetPet: Pet %u not exist.",GUID_LOPART(pet_guid));
+ const_cast<Unit*>(this)->SetPet(0);
+ }
+
+ return NULL;
+}
+
+Unit* Unit::GetCharm() const
+{
+ if(uint64 charm_guid = GetCharmGUID())
+ {
+ if(Unit* pet = ObjectAccessor::GetUnit(*this, charm_guid))
+ return pet;
+
+ sLog.outError("Unit::GetCharm: Charmed creature %u not exist.",GUID_LOPART(charm_guid));
+ const_cast<Unit*>(this)->SetCharm(0);
+ }
+
+ return NULL;
+}
+
+void Unit::SetPet(Pet* pet)
+{
+ SetUInt64Value(UNIT_FIELD_SUMMON,pet ? pet->GetGUID() : 0);
+
+ // FIXME: hack, speed must be set only at follow
+ if(pet)
+ for(int i = 0; i < MAX_MOVE_TYPE; ++i)
+ pet->SetSpeed(UnitMoveType(i),m_speed_rate[i],true);
+}
+
+void Unit::SetCharm(Unit* charmed)
+{
+ SetUInt64Value(UNIT_FIELD_CHARM,charmed ? charmed->GetGUID() : 0);
+}
+
+void Unit::UnsummonAllTotems()
+{
+ for (int8 i = 0; i < MAX_TOTEM; ++i)
+ {
+ if(!m_TotemSlot[i])
+ continue;
+
+ Creature *OldTotem = ObjectAccessor::GetCreature(*this, m_TotemSlot[i]);
+ if (OldTotem && OldTotem->isTotem())
+ ((Totem*)OldTotem)->UnSummon();
+ }
+}
+
+void Unit::SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, bool critical)
+{
+ // we guess size
+ WorldPacket data(SMSG_SPELLHEALLOG, (8+8+4+4+1));
+ data.append(pVictim->GetPackGUID());
+ data.append(GetPackGUID());
+ data << uint32(SpellID);
+ data << uint32(Damage);
+ data << uint8(critical ? 1 : 0);
+ data << uint8(0); // unused in client?
+ SendMessageToSet(&data, true);
+}
+
+void Unit::SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype, bool critical)
+{
+ WorldPacket data(SMSG_SPELLENERGIZELOG, (8+8+4+4+4+1));
+ data.append(pVictim->GetPackGUID());
+ data.append(GetPackGUID());
+ data << uint32(SpellID);
+ data << uint32(powertype);
+ data << uint32(Damage);
+ //data << uint8(critical ? 1 : 0); // removed in 2.4.0
+ SendMessageToSet(&data, true);
+}
+
+uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype)
+{
+ if(!spellProto || !pVictim || damagetype==DIRECT_DAMAGE )
+ return pdamage;
+
+ int32 BonusDamage = 0;
+ if( GetTypeId()==TYPEID_UNIT )
+ {
+ // Pets just add their bonus damage to their spell damage
+ // note that their spell damage is just gain of their own auras
+ if (((Creature*)this)->isPet())
+ {
+ BonusDamage = ((Pet*)this)->GetBonusDamage();
+ }
+ // For totems get damage bonus from owner (statue isn't totem in fact)
+ else if (((Creature*)this)->isTotem() && ((Totem*)this)->GetTotemType()!=TOTEM_STATUE)
+ {
+ if(Unit* owner = GetOwner())
+ return owner->SpellDamageBonus(pVictim, spellProto, pdamage, damagetype);
+ }
+ }
+
+ // Damage Done
+ uint32 CastingTime = !IsChanneledSpell(spellProto) ? GetSpellCastTime(spellProto) : GetSpellDuration(spellProto);
+
+ // Taken/Done fixed damage bonus auras
+ int32 DoneAdvertisedBenefit = SpellBaseDamageBonus(GetSpellSchoolMask(spellProto))+BonusDamage;
+ int32 TakenAdvertisedBenefit = SpellBaseDamageBonusForVictim(GetSpellSchoolMask(spellProto), pVictim);
+
+ // Damage over Time spells bonus calculation
+ float DotFactor = 1.0f;
+ if(damagetype == DOT)
+ {
+ int32 DotDuration = GetSpellDuration(spellProto);
+ // 200% limit
+ if(DotDuration > 0)
+ {
+ if(DotDuration > 30000) DotDuration = 30000;
+ if(!IsChanneledSpell(spellProto)) DotFactor = DotDuration / 15000.0f;
+ int x = 0;
+ for(int j = 0; j < 3; j++)
+ {
+ if( spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && (
+ spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_DAMAGE ||
+ spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) )
+ {
+ x = j;
+ break;
+ }
+ }
+ int DotTicks = 6;
+ if(spellProto->EffectAmplitude[x] != 0)
+ DotTicks = DotDuration / spellProto->EffectAmplitude[x];
+ if(DotTicks)
+ {
+ DoneAdvertisedBenefit /= DotTicks;
+ TakenAdvertisedBenefit /= DotTicks;
+ }
+ }
+ }
+
+ // Taken/Done total percent damage auras
+ float DoneTotalMod = 1.0f;
+ float TakenTotalMod = 1.0f;
+
+ // ..done
+ AuraList const& mModDamagePercentDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
+ for(AuraList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i)
+ {
+ if( ((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto)) &&
+ (*i)->GetSpellProto()->EquippedItemClass == -1 &&
+ // -1 == any item class (not wand then)
+ (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0 )
+ // 0 == any inventory type (not wand then)
+ {
+ DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+ }
+ }
+
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+ AuraList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS);
+ for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+
+ // ..taken
+ AuraList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ for(AuraList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i)
+ if( (*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto) )
+ TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+
+ // .. taken pct: scripted (increases damage of * against targets *)
+ AuraList const& mOverrideClassScript = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i)
+ {
+ switch((*i)->GetModifier()->m_miscvalue)
+ {
+ //Molten Fury
+ case 4920: case 4919:
+ if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_20_PERCENT))
+ TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f; break;
+ }
+ }
+ // .. taken pct: dummy auras
+ AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
+ {
+ switch((*i)->GetSpellProto()->SpellIconID)
+ {
+ //Cheat Death
+ case 2109:
+ if( ((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spellProto)) )
+ {
+ if(pVictim->GetTypeId() != TYPEID_PLAYER)
+ continue;
+ float mod = -((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2*4;
+ if (mod < (*i)->GetModifier()->m_amount)
+ mod = (*i)->GetModifier()->m_amount;
+ TakenTotalMod *= (mod+100.0f)/100.0f;
+ }
+ break;
+ //Mangle
+ case 2312:
+ for(int j=0;j<3;j++)
+ {
+ if(GetEffectMechanic(spellProto, j)==MECHANIC_BLEED)
+ {
+ TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ // Distribute Damage over multiple effects, reduce by AoE
+ CastingTime = GetCastingTimeForBonus( spellProto, damagetype, CastingTime );
+
+ // 50% for damage and healing spells for leech spells from damage bonus and 0% from healing
+ for(int j = 0; j < 3; ++j)
+ {
+ if( spellProto->Effect[j] == SPELL_EFFECT_HEALTH_LEECH ||
+ spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH )
+ {
+ CastingTime /= 2;
+ break;
+ }
+ }
+
+ switch(spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_MAGE:
+ // Ignite - do not modify, it is (8*Rank)% damage of procing Spell
+ if(spellProto->Id==12654)
+ {
+ return pdamage;
+ }
+ // Ice Lance
+ else if((spellProto->SpellFamilyFlags & 0x20000LL) && spellProto->SpellIconID == 186)
+ {
+ CastingTime /= 3; // applied 1/3 bonuses in case generic target
+ if(pVictim->isFrozen()) // and compensate this for frozen target.
+ TakenTotalMod *= 3.0f;
+ }
+ // Pyroblast - 115% of Fire Damage, DoT - 20% of Fire Damage
+ else if((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 184 )
+ {
+ DotFactor = damagetype == DOT ? 0.2f : 1.0f;
+ CastingTime = damagetype == DOT ? 3500 : 4025;
+ }
+ // Fireball - 100% of Fire Damage, DoT - 0% of Fire Damage
+ else if((spellProto->SpellFamilyFlags & 0x1LL) && spellProto->SpellIconID == 185)
+ {
+ CastingTime = 3500;
+ DotFactor = damagetype == DOT ? 0.0f : 1.0f;
+ }
+ // Molten armor
+ else if (spellProto->SpellFamilyFlags & 0x0000000800000000LL)
+ {
+ CastingTime = 0;
+ }
+ // Arcane Missiles triggered spell
+ else if ((spellProto->SpellFamilyFlags & 0x200000LL) && spellProto->SpellIconID == 225)
+ {
+ CastingTime = 1000;
+ }
+ // Blizzard triggered spell
+ else if ((spellProto->SpellFamilyFlags & 0x80080LL) && spellProto->SpellIconID == 285)
+ {
+ CastingTime = 500;
+ }
+ break;
+ case SPELLFAMILY_WARLOCK:
+ // Life Tap
+ if((spellProto->SpellFamilyFlags & 0x40000LL) && spellProto->SpellIconID == 208)
+ {
+ CastingTime = 2800; // 80% from +shadow damage
+ DoneTotalMod = 1.0f;
+ TakenTotalMod = 1.0f;
+ }
+ // Dark Pact
+ else if((spellProto->SpellFamilyFlags & 0x80000000LL) && spellProto->SpellIconID == 154 && GetPetGUID())
+ {
+ CastingTime = 3360; // 96% from +shadow damage
+ DoneTotalMod = 1.0f;
+ TakenTotalMod = 1.0f;
+ }
+ // Soul Fire - 115% of Fire Damage
+ else if((spellProto->SpellFamilyFlags & 0x8000000000LL) && spellProto->SpellIconID == 184)
+ {
+ CastingTime = 4025;
+ }
+ // Curse of Agony - 120% of Shadow Damage
+ else if((spellProto->SpellFamilyFlags & 0x0000000400LL) && spellProto->SpellIconID == 544)
+ {
+ DotFactor = 1.2f;
+ }
+ // Drain Mana - 0% of Shadow Damage
+ else if((spellProto->SpellFamilyFlags & 0x10LL) && spellProto->SpellIconID == 548)
+ {
+ CastingTime = 0;
+ }
+ // Drain Soul 214.3%
+ else if ((spellProto->SpellFamilyFlags & 0x4000LL) && spellProto->SpellIconID == 113 )
+ {
+ CastingTime = 7500;
+ }
+ // Hellfire
+ else if ((spellProto->SpellFamilyFlags & 0x40LL) && spellProto->SpellIconID == 937)
+ {
+ CastingTime = damagetype == DOT ? 5000 : 500; // self damage seems to be so
+ }
+ // Unstable Affliction - 180%
+ else if (spellProto->Id == 31117 && spellProto->SpellIconID == 232)
+ {
+ CastingTime = 6300;
+ }
+ // Corruption 93%
+ else if ((spellProto->SpellFamilyFlags & 0x2LL) && spellProto->SpellIconID == 313)
+ {
+ DotFactor = 0.93f;
+ }
+ break;
+ case SPELLFAMILY_PALADIN:
+ // Consecration - 95% of Holy Damage
+ if((spellProto->SpellFamilyFlags & 0x20LL) && spellProto->SpellIconID == 51)
+ {
+ DotFactor = 0.95f;
+ CastingTime = 3500;
+ }
+ // Seal of Righteousness - 10.2%/9.8% ( based on weapon type ) of Holy Damage, multiplied by weapon speed
+ else if((spellProto->SpellFamilyFlags & 0x8000000LL) && spellProto->SpellIconID == 25)
+ {
+ Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
+ float wspeed = GetAttackTime(BASE_ATTACK)/1000.0f;
+
+ if( item && item->GetProto()->InventoryType == INVTYPE_2HWEAPON)
+ CastingTime = uint32(wspeed*3500*0.102f);
+ else
+ CastingTime = uint32(wspeed*3500*0.098f);
+ }
+ // Judgement of Righteousness - 73%
+ else if ((spellProto->SpellFamilyFlags & 1024) && spellProto->SpellIconID == 25)
+ {
+ CastingTime = 2555;
+ }
+ // Seal of Vengeance - 17% per Fully Stacked Tick - 5 Applications
+ else if ((spellProto->SpellFamilyFlags & 0x80000000000LL) && spellProto->SpellIconID == 2292)
+ {
+ DotFactor = 0.17f;
+ CastingTime = 3500;
+ }
+ // Holy shield - 5% of Holy Damage
+ else if ((spellProto->SpellFamilyFlags & 0x4000000000LL) && spellProto->SpellIconID == 453)
+ {
+ CastingTime = 175;
+ }
+ // Blessing of Sanctuary - 0%
+ else if ((spellProto->SpellFamilyFlags & 0x10000000LL) && spellProto->SpellIconID == 29)
+ {
+ CastingTime = 0;
+ }
+ // Seal of Righteousness trigger - already computed for parent spell
+ else if ( spellProto->SpellFamilyName==SPELLFAMILY_PALADIN && spellProto->SpellIconID==25 && spellProto->AttributesEx4 & 0x00800000LL )
+ {
+ return pdamage;
+ }
+ break;
+ case SPELLFAMILY_SHAMAN:
+ // totem attack
+ if (spellProto->SpellFamilyFlags & 0x000040000000LL)
+ {
+ if (spellProto->SpellIconID == 33) // Fire Nova totem attack must be 21.4%(untested)
+ CastingTime = 749; // ignore CastingTime and use as modifier
+ else if (spellProto->SpellIconID == 680) // Searing Totem attack 8%
+ CastingTime = 280; // ignore CastingTime and use as modifier
+ else if (spellProto->SpellIconID == 37) // Magma totem attack must be 6.67%(untested)
+ CastingTime = 234; // ignore CastingTimePenalty and use as modifier
+ }
+ // Lightning Shield (and proc shield from T2 8 pieces bonus ) 33% per charge
+ else if( (spellProto->SpellFamilyFlags & 0x00000000400LL) || spellProto->Id == 23552)
+ CastingTime = 1155; // ignore CastingTimePenalty and use as modifier
+ break;
+ case SPELLFAMILY_PRIEST:
+ // Mana Burn - 0% of Shadow Damage
+ if((spellProto->SpellFamilyFlags & 0x10LL) && spellProto->SpellIconID == 212)
+ {
+ CastingTime = 0;
+ }
+ // Mind Flay - 59% of Shadow Damage
+ else if((spellProto->SpellFamilyFlags & 0x800000LL) && spellProto->SpellIconID == 548)
+ {
+ CastingTime = 2065;
+ }
+ // Holy Fire - 86.71%, DoT - 16.5%
+ else if ((spellProto->SpellFamilyFlags & 0x100000LL) && spellProto->SpellIconID == 156)
+ {
+ DotFactor = damagetype == DOT ? 0.165f : 1.0f;
+ CastingTime = damagetype == DOT ? 3500 : 3035;
+ }
+ // Shadowguard - 28% per charge
+ else if ((spellProto->SpellFamilyFlags & 0x2000000LL) && spellProto->SpellIconID == 19)
+ {
+ CastingTime = 980;
+ }
+ // Touch of Weakeness - 10%
+ else if ((spellProto->SpellFamilyFlags & 0x80000LL) && spellProto->SpellIconID == 1591)
+ {
+ CastingTime = 350;
+ }
+ // Reflective Shield (back damage) - 0% (other spells fit to check not have damage effects/auras)
+ else if (spellProto->SpellFamilyFlags == 0 && spellProto->SpellIconID == 566)
+ {
+ CastingTime = 0;
+ }
+ // Holy Nova - 14%
+ else if ((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 1874)
+ {
+ CastingTime = 500;
+ }
+ break;
+ case SPELLFAMILY_DRUID:
+ // Hurricane triggered spell
+ if((spellProto->SpellFamilyFlags & 0x400000LL) && spellProto->SpellIconID == 220)
+ {
+ CastingTime = 500;
+ }
+ break;
+ case SPELLFAMILY_WARRIOR:
+ case SPELLFAMILY_HUNTER:
+ case SPELLFAMILY_ROGUE:
+ CastingTime = 0;
+ break;
+ default:
+ break;
+ }
+
+ float LvlPenalty = CalculateLevelPenalty(spellProto);
+
+ // Spellmod SpellDamage
+ float SpellModSpellDamage = 100.0f;
+
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_SPELL_BONUS_DAMAGE,SpellModSpellDamage);
+
+ SpellModSpellDamage /= 100.0f;
+
+ float DoneActualBenefit = DoneAdvertisedBenefit * (CastingTime / 3500.0f) * DotFactor * SpellModSpellDamage * LvlPenalty;
+ float TakenActualBenefit = TakenAdvertisedBenefit * (CastingTime / 3500.0f) * DotFactor * LvlPenalty;
+
+ float tmpDamage = (float(pdamage)+DoneActualBenefit)*DoneTotalMod;
+
+ // Add flat bonus from spell damage versus
+ tmpDamage += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS, creatureTypeMask);
+
+ // apply spellmod to Done damage
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage);
+
+ tmpDamage = (tmpDamage+TakenActualBenefit)*TakenTotalMod;
+
+ if( GetTypeId() == TYPEID_UNIT && !((Creature*)this)->isPet() )
+ tmpDamage *= ((Creature*)this)->GetSpellDamageMod(((Creature*)this)->GetCreatureInfo()->rank);
+
+ return tmpDamage > 0 ? uint32(tmpDamage) : 0;
+}
+
+int32 Unit::SpellBaseDamageBonus(SpellSchoolMask schoolMask)
+{
+ int32 DoneAdvertisedBenefit = 0;
+
+ // ..done
+ AuraList const& mDamageDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE);
+ for(AuraList::const_iterator i = mDamageDone.begin();i != mDamageDone.end(); ++i)
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0 &&
+ (*i)->GetSpellProto()->EquippedItemClass == -1 &&
+ // -1 == any item class (not wand then)
+ (*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0 )
+ // 0 == any inventory type (not wand then)
+ DoneAdvertisedBenefit += (*i)->GetModifier()->m_amount;
+
+ if (GetTypeId() == TYPEID_PLAYER)
+ {
+ // Damage bonus from stats
+ AuraList const& mDamageDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT);
+ for(AuraList::const_iterator i = mDamageDoneOfStatPercent.begin();i != mDamageDoneOfStatPercent.end(); ++i)
+ {
+ if((*i)->GetModifier()->m_miscvalue & schoolMask)
+ {
+ SpellEntry const* iSpellProto = (*i)->GetSpellProto();
+ uint8 eff = (*i)->GetEffIndex();
+
+ // stat used dependent from next effect aura SPELL_AURA_MOD_SPELL_HEALING presence and misc value (stat index)
+ Stats usedStat = STAT_INTELLECT;
+ if(eff < 2 && iSpellProto->EffectApplyAuraName[eff+1]==SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT)
+ usedStat = Stats(iSpellProto->EffectMiscValue[eff+1]);
+
+ DoneAdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetModifier()->m_amount / 100.0f);
+ }
+ }
+ // ... and attack power
+ AuraList const& mDamageDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER);
+ for(AuraList::const_iterator i =mDamageDonebyAP.begin();i != mDamageDonebyAP.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue & schoolMask)
+ DoneAdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetModifier()->m_amount / 100.0f);
+
+ }
+ return DoneAdvertisedBenefit;
+}
+
+int32 Unit::SpellBaseDamageBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim)
+{
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+
+ int32 TakenAdvertisedBenefit = 0;
+ // ..done (for creature type by mask) in taken
+ AuraList const& mDamageDoneCreature = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE);
+ for(AuraList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ TakenAdvertisedBenefit += (*i)->GetModifier()->m_amount;
+
+ // ..taken
+ AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN);
+ for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0)
+ TakenAdvertisedBenefit += (*i)->GetModifier()->m_amount;
+
+ return TakenAdvertisedBenefit;
+}
+
+bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType)
+{
+ // not criting spell
+ if((spellProto->AttributesEx2 & SPELL_ATTR_EX2_CANT_CRIT))
+ return false;
+
+ float crit_chance = 0.0f;
+ switch(spellProto->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_NONE:
+ return false;
+ case SPELL_DAMAGE_CLASS_MAGIC:
+ {
+ if (schoolMask & SPELL_SCHOOL_MASK_NORMAL)
+ crit_chance = 0.0f;
+ // For other schools
+ else if (GetTypeId() == TYPEID_PLAYER)
+ crit_chance = GetFloatValue( PLAYER_SPELL_CRIT_PERCENTAGE1 + GetFirstSchoolInMask(schoolMask));
+ else
+ {
+ crit_chance = m_baseSpellCritChance;
+ crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask);
+ }
+ // taken
+ if (pVictim && !IsPositiveSpell(spellProto->Id))
+ {
+ // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE
+ crit_chance += pVictim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE, schoolMask);
+ // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE
+ crit_chance += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
+ // Modify by player victim resilience
+ if (pVictim->GetTypeId() == TYPEID_PLAYER)
+ crit_chance -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_SPELL);
+ // scripted (increase crit chance ... against ... target by x%
+ if(pVictim->isFrozen()) // Shatter
+ {
+ AuraList const& mOverrideClassScript = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(AuraList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i)
+ {
+ switch((*i)->GetModifier()->m_miscvalue)
+ {
+ case 849: crit_chance+= 10.0f; break; //Shatter Rank 1
+ case 910: crit_chance+= 20.0f; break; //Shatter Rank 2
+ case 911: crit_chance+= 30.0f; break; //Shatter Rank 3
+ case 912: crit_chance+= 40.0f; break; //Shatter Rank 4
+ case 913: crit_chance+= 50.0f; break; //Shatter Rank 5
+ }
+ }
+ }
+ }
+ break;
+ }
+ case SPELL_DAMAGE_CLASS_MELEE:
+ case SPELL_DAMAGE_CLASS_RANGED:
+ {
+ if (pVictim)
+ {
+ crit_chance = GetUnitCriticalChance(attackType, pVictim);
+ crit_chance+= (int32(GetMaxSkillValueForLevel(pVictim)) - int32(pVictim->GetDefenseSkillValue(this))) * 0.04f;
+ crit_chance+= GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask);
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ // percent done
+ // only players use intelligence for critical chance computations
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance);
+
+ crit_chance = crit_chance > 0.0f ? crit_chance : 0.0f;
+ if (roll_chance_f(crit_chance))
+ return true;
+ return false;
+}
+
+uint32 Unit::SpellCriticalBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim)
+{
+ // Calculate critical bonus
+ int32 crit_bonus;
+ switch(spellProto->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_MELEE: // for melee based spells is 100%
+ case SPELL_DAMAGE_CLASS_RANGED:
+ // TODO: write here full calculation for melee/ranged spells
+ crit_bonus = damage;
+ break;
+ default:
+ crit_bonus = damage / 2; // for spells is 50%
+ break;
+ }
+
+ // adds additional damage to crit_bonus (from talents)
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
+
+ if(pVictim)
+ {
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+ crit_bonus = int32(crit_bonus * GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, creatureTypeMask));
+ }
+
+ if(crit_bonus > 0)
+ damage += crit_bonus;
+
+ return damage;
+}
+
+uint32 Unit::SpellHealingBonus(SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, Unit *pVictim)
+{
+ // For totems get healing bonus from owner (statue isn't totem in fact)
+ if( GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem() && ((Totem*)this)->GetTotemType()!=TOTEM_STATUE)
+ if(Unit* owner = GetOwner())
+ return owner->SpellHealingBonus(spellProto, healamount, damagetype, pVictim);
+
+ // Healing Done
+
+ // These Spells are doing fixed amount of healing (TODO found less hack-like check)
+ if(spellProto->Id == 15290 || spellProto->Id == 39373 || spellProto->Id == 33778 || spellProto->Id == 379 || spellProto->Id == 38395)
+ return healamount;
+
+
+ int32 AdvertisedBenefit = SpellBaseHealingBonus(GetSpellSchoolMask(spellProto));
+ uint32 CastingTime = GetSpellCastTime(spellProto);
+
+ // Healing Taken
+ AdvertisedBenefit += SpellBaseHealingBonusForVictim(GetSpellSchoolMask(spellProto), pVictim);
+
+ // Blessing of Light dummy effects healing taken from Holy Light and Flash of Light
+ if (spellProto->SpellFamilyName == SPELLFAMILY_PALADIN && (spellProto->SpellFamilyFlags & 0x00000000C0000000LL))
+ {
+ AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i)
+ {
+ if((*i)->GetSpellProto()->SpellVisual == 9180)
+ {
+ // Flash of Light
+ if ((spellProto->SpellFamilyFlags & 0x0000000040000000LL) && (*i)->GetEffIndex() == 1)
+ AdvertisedBenefit += (*i)->GetModifier()->m_amount;
+ // Holy Light
+ else if ((spellProto->SpellFamilyFlags & 0x0000000080000000LL) && (*i)->GetEffIndex() == 0)
+ AdvertisedBenefit += (*i)->GetModifier()->m_amount;
+ }
+ }
+ }
+
+ float ActualBenefit = 0.0f;
+
+ if (AdvertisedBenefit != 0)
+ {
+ // Healing over Time spells
+ float DotFactor = 1.0f;
+ if(damagetype == DOT)
+ {
+ int32 DotDuration = GetSpellDuration(spellProto);
+ if(DotDuration > 0)
+ {
+ // 200% limit
+ if(DotDuration > 30000) DotDuration = 30000;
+ if(!IsChanneledSpell(spellProto)) DotFactor = DotDuration / 15000.0f;
+ int x = 0;
+ for(int j = 0; j < 3; j++)
+ {
+ if( spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && (
+ spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_HEAL ||
+ spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) )
+ {
+ x = j;
+ break;
+ }
+ }
+ int DotTicks = 6;
+ if(spellProto->EffectAmplitude[x] != 0)
+ DotTicks = DotDuration / spellProto->EffectAmplitude[x];
+ if(DotTicks)
+ AdvertisedBenefit /= DotTicks;
+ }
+ }
+
+ // distribute healing to all effects, reduce AoE damage
+ CastingTime = GetCastingTimeForBonus( spellProto, damagetype, CastingTime );
+
+ // 0% bonus for damage and healing spells for leech spells from healing bonus
+ for(int j = 0; j < 3; ++j)
+ {
+ if( spellProto->Effect[j] == SPELL_EFFECT_HEALTH_LEECH ||
+ spellProto->Effect[j] == SPELL_EFFECT_APPLY_AURA && spellProto->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH )
+ {
+ CastingTime = 0;
+ break;
+ }
+ }
+
+ // Exception
+ switch (spellProto->SpellFamilyName)
+ {
+ case SPELLFAMILY_SHAMAN:
+ // Healing stream from totem (add 6% per tick from hill bonus owner)
+ if (spellProto->SpellFamilyFlags & 0x000000002000LL)
+ CastingTime = 210;
+ // Earth Shield 30% per charge
+ else if (spellProto->SpellFamilyFlags & 0x40000000000LL)
+ CastingTime = 1050;
+ break;
+ case SPELLFAMILY_DRUID:
+ // Lifebloom
+ if (spellProto->SpellFamilyFlags & 0x1000000000LL)
+ {
+ CastingTime = damagetype == DOT ? 3500 : 1200;
+ DotFactor = damagetype == DOT ? 0.519f : 1.0f;
+ }
+ // Tranquility triggered spell
+ else if (spellProto->SpellFamilyFlags & 0x80LL)
+ CastingTime = 667;
+ // Rejuvenation
+ else if (spellProto->SpellFamilyFlags & 0x10LL)
+ DotFactor = 0.845f;
+ // Regrowth
+ else if (spellProto->SpellFamilyFlags & 0x40LL)
+ {
+ DotFactor = damagetype == DOT ? 0.705f : 1.0f;
+ CastingTime = damagetype == DOT ? 3500 : 1010;
+ }
+ break;
+ case SPELLFAMILY_PRIEST:
+ // Holy Nova - 14%
+ if ((spellProto->SpellFamilyFlags & 0x8000000LL) && spellProto->SpellIconID == 1874)
+ CastingTime = 500;
+ break;
+ case SPELLFAMILY_PALADIN:
+ // Seal and Judgement of Light
+ if ( spellProto->SpellFamilyFlags & 0x100040000LL )
+ CastingTime = 0;
+ break;
+ case SPELLFAMILY_WARRIOR:
+ case SPELLFAMILY_ROGUE:
+ case SPELLFAMILY_HUNTER:
+ CastingTime = 0;
+ break;
+ }
+
+ float LvlPenalty = CalculateLevelPenalty(spellProto);
+
+ // Spellmod SpellDamage
+ float SpellModSpellDamage = 100.0f;
+
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_SPELL_BONUS_DAMAGE,SpellModSpellDamage);
+
+ SpellModSpellDamage /= 100.0f;
+
+ ActualBenefit = (float)AdvertisedBenefit * ((float)CastingTime / 3500.0f) * DotFactor * SpellModSpellDamage * LvlPenalty;
+ }
+
+ // use float as more appropriate for negative values and percent applying
+ float heal = healamount + ActualBenefit;
+
+ // TODO: check for ALL/SPELLS type
+ // Healing done percent
+ AuraList const& mHealingDonePct = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT);
+ for(AuraList::const_iterator i = mHealingDonePct.begin();i != mHealingDonePct.end(); ++i)
+ heal *= (100.0f + (*i)->GetModifier()->m_amount) / 100.0f;
+
+ // apply spellmod to Done amount
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal);
+
+ // Healing Wave cast
+ if (spellProto->SpellFamilyName == SPELLFAMILY_SHAMAN && spellProto->SpellFamilyFlags & 0x0000000000000040LL)
+ {
+ // Search for Healing Way on Victim (stack up to 3 time)
+ int32 pctMod = 0;
+ Unit::AuraList const& auraDummy = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = auraDummy.begin(); itr!=auraDummy.end(); ++itr)
+ if((*itr)->GetId() == 29203)
+ pctMod += (*itr)->GetModifier()->m_amount;
+ // Apply bonus
+ if (pctMod)
+ heal = heal * (100 + pctMod) / 100;
+ }
+
+ // Healing taken percent
+ float minval = pVictim->GetMaxNegativeAuraModifier(SPELL_AURA_MOD_HEALING_PCT);
+ if(minval)
+ heal *= (100.0f + minval) / 100.0f;
+
+ float maxval = pVictim->GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HEALING_PCT);
+ if(maxval)
+ heal *= (100.0f + maxval) / 100.0f;
+
+ if (heal < 0) heal = 0;
+
+ return uint32(heal);
+}
+
+int32 Unit::SpellBaseHealingBonus(SpellSchoolMask schoolMask)
+{
+ int32 AdvertisedBenefit = 0;
+
+ AuraList const& mHealingDone = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE);
+ for(AuraList::const_iterator i = mHealingDone.begin();i != mHealingDone.end(); ++i)
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0)
+ AdvertisedBenefit += (*i)->GetModifier()->m_amount;
+
+ // Healing bonus of spirit, intellect and strength
+ if (GetTypeId() == TYPEID_PLAYER)
+ {
+ // Healing bonus from stats
+ AuraList const& mHealingDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT);
+ for(AuraList::const_iterator i = mHealingDoneOfStatPercent.begin();i != mHealingDoneOfStatPercent.end(); ++i)
+ {
+ // stat used dependent from misc value (stat index)
+ Stats usedStat = Stats((*i)->GetSpellProto()->EffectMiscValue[(*i)->GetEffIndex()]);
+ AdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetModifier()->m_amount / 100.0f);
+ }
+
+ // ... and attack power
+ AuraList const& mHealingDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER);
+ for(AuraList::const_iterator i = mHealingDonebyAP.begin();i != mHealingDonebyAP.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue & schoolMask)
+ AdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetModifier()->m_amount / 100.0f);
+ }
+ return AdvertisedBenefit;
+}
+
+int32 Unit::SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim)
+{
+ int32 AdvertisedBenefit = 0;
+ AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_HEALING);
+ for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
+ if(((*i)->GetModifier()->m_miscvalue & schoolMask) != 0)
+ AdvertisedBenefit += (*i)->GetModifier()->m_amount;
+ return AdvertisedBenefit;
+}
+
+bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask, bool useCharges)
+{
+ // no charges dependent checks
+ SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
+ for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
+ if(itr->type & shoolMask)
+ return true;
+
+ // charges dependent checks
+ SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
+ for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
+ {
+ if(itr->type & shoolMask)
+ {
+ if(useCharges)
+ {
+ AuraList const& auraDamageImmunity = GetAurasByType(SPELL_AURA_DAMAGE_IMMUNITY);
+ for(AuraList::const_iterator auraItr = auraDamageImmunity.begin(); auraItr != auraDamageImmunity.end(); ++auraItr)
+ {
+ if((*auraItr)->GetId()==itr->spellId)
+ {
+ if((*auraItr)->m_procCharges > 0)
+ {
+ --(*auraItr)->m_procCharges;
+ if((*auraItr)->m_procCharges==0)
+ RemoveAurasDueToSpell(itr->spellId);
+ }
+ break;
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
+{
+ if (!spellInfo)
+ return false;
+
+ // no charges first
+
+ //FIX ME this hack: don't get feared if stunned
+ if (spellInfo->Mechanic == MECHANIC_FEAR )
+ {
+ if ( hasUnitState(UNIT_STAT_STUNNED) )
+ return true;
+ }
+
+ // not have spells with charges currently
+ SpellImmuneList const& dispelList = m_spellImmune[IMMUNITY_DISPEL];
+ for(SpellImmuneList::const_iterator itr = dispelList.begin(); itr != dispelList.end(); ++itr)
+ if(itr->type == spellInfo->Dispel)
+ return true;
+
+ if( !(spellInfo->AttributesEx & SPELL_ATTR_EX_UNAFFECTED_BY_SCHOOL_IMMUNE)) // unaffected by school immunity
+ {
+ // not have spells with charges currently
+ SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
+ for(SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
+ if( !(IsPositiveSpell(itr->spellId) && IsPositiveSpell(spellInfo->Id)) &&
+ (itr->type & GetSpellSchoolMask(spellInfo)) )
+ return true;
+ }
+
+ // charges dependent checks
+
+ SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
+ for(SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
+ {
+ if(itr->type == spellInfo->Mechanic)
+ {
+ if(useCharges)
+ {
+ AuraList const& auraMechImmunity = GetAurasByType(SPELL_AURA_MECHANIC_IMMUNITY);
+ for(AuraList::const_iterator auraItr = auraMechImmunity.begin(); auraItr != auraMechImmunity.end(); ++auraItr)
+ {
+ if((*auraItr)->GetId()==itr->spellId)
+ {
+ if((*auraItr)->m_procCharges > 0)
+ {
+ --(*auraItr)->m_procCharges;
+ if((*auraItr)->m_procCharges==0)
+ RemoveAurasDueToSpell(itr->spellId);
+ }
+ break;
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Unit::IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const
+{
+ //If m_immuneToEffect type contain this effect type, IMMUNE effect.
+ SpellImmuneList const& effectList = m_spellImmune[IMMUNITY_EFFECT];
+ for (SpellImmuneList::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr)
+ if(itr->type == effect)
+ return true;
+
+ SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
+ for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
+ if(itr->type == mechanic)
+ return true;
+
+ return false;
+}
+
+bool Unit::IsDamageToThreatSpell(SpellEntry const * spellInfo) const
+{
+ if(!spellInfo)
+ return false;
+
+ uint32 family = spellInfo->SpellFamilyName;
+ uint64 flags = spellInfo->SpellFamilyFlags;
+
+ if((family == 5 && flags == 256) || //Searing Pain
+ (family == 6 && flags == 8192) || //Mind Blast
+ (family == 11 && flags == 1048576)) //Earth Shock
+ return true;
+
+ return false;
+}
+
+void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attType, SpellEntry const *spellProto)
+{
+ if(!pVictim)
+ return;
+
+ if(*pdamage == 0)
+ return;
+
+ uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
+
+ // Taken/Done fixed damage bonus auras
+ int32 DoneFlatBenefit = 0;
+ int32 TakenFlatBenefit = 0;
+
+ // ..done (for creature type by mask) in taken
+ AuraList const& mDamageDoneCreature = this->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE);
+ for(AuraList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ DoneFlatBenefit += (*i)->GetModifier()->m_amount;
+
+ // ..done
+ // SPELL_AURA_MOD_DAMAGE_DONE included in weapon damage
+
+ // ..done (base at attack power for marked target and base at attack power for creature type)
+ int32 APbonus = 0;
+ if(attType == RANGED_ATTACK)
+ {
+ APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS);
+
+ // ..done (base at attack power and creature type)
+ AuraList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS);
+ for(AuraList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ APbonus += (*i)->GetModifier()->m_amount;
+ }
+ else
+ {
+ APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS);
+
+ // ..done (base at attack power and creature type)
+ AuraList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS);
+ for(AuraList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ APbonus += (*i)->GetModifier()->m_amount;
+ }
+
+ if (APbonus!=0) // Can be negative
+ {
+ bool normalized = false;
+ if(spellProto)
+ {
+ for (uint8 i = 0; i<3;i++)
+ {
+ if (spellProto->Effect[i] == SPELL_EFFECT_NORMALIZED_WEAPON_DMG)
+ {
+ normalized = true;
+ break;
+ }
+ }
+ }
+
+ DoneFlatBenefit += int32(APbonus/14.0f * GetAPMultiplier(attType,normalized));
+ }
+
+ // ..taken
+ AuraList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN);
+ for(AuraList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
+ if((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
+ TakenFlatBenefit += (*i)->GetModifier()->m_amount;
+
+ if(attType!=RANGED_ATTACK)
+ TakenFlatBenefit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN);
+ else
+ TakenFlatBenefit += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN);
+
+ // Done/Taken total percent damage auras
+ float DoneTotalMod = 1;
+ float TakenTotalMod = 1;
+
+ // ..done
+ // SPELL_AURA_MOD_DAMAGE_PERCENT_DONE included in weapon damage
+ // SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT included in weapon damage
+
+ AuraList const& mDamageDoneVersus = this->GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS);
+ for(AuraList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
+ if(creatureTypeMask & uint32((*i)->GetModifier()->m_miscvalue))
+ DoneTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+
+ // ..taken
+ AuraList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
+ for(AuraList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i)
+ if((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
+ TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+
+ // .. taken pct: dummy auras
+ AuraList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
+ {
+ switch((*i)->GetSpellProto()->SpellIconID)
+ {
+ //Cheat Death
+ case 2109:
+ if((*i)->GetModifier()->m_miscvalue & SPELL_SCHOOL_MASK_NORMAL)
+ {
+ if(pVictim->GetTypeId() != TYPEID_PLAYER)
+ continue;
+ float mod = ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE)*(-8.0f);
+ if (mod < (*i)->GetModifier()->m_amount)
+ mod = (*i)->GetModifier()->m_amount;
+ TakenTotalMod *= (mod+100.0f)/100.0f;
+ }
+ break;
+ //Mangle
+ case 2312:
+ if(spellProto==NULL)
+ break;
+ // Should increase Shred (initial Damage of Lacerate and Rake handled in Spell::EffectSchoolDMG)
+ if(spellProto->SpellFamilyName==SPELLFAMILY_DRUID && (spellProto->SpellFamilyFlags==0x00008000LL))
+ TakenTotalMod *= (100.0f+(*i)->GetModifier()->m_amount)/100.0f;
+ break;
+ }
+ }
+
+ // .. taken pct: class scripts
+ AuraList const& mclassScritAuras = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
+ for(AuraList::const_iterator i = mclassScritAuras.begin(); i != mclassScritAuras.end(); ++i)
+ {
+ switch((*i)->GetMiscValue())
+ {
+ case 6427: case 6428: // Dirty Deeds
+ if(pVictim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT))
+ {
+ Aura* eff0 = GetAura((*i)->GetId(),0);
+ if(!eff0 || (*i)->GetEffIndex()!=1)
+ {
+ sLog.outError("Spell structure of DD (%u) changed.",(*i)->GetId());
+ continue;
+ }
+
+ // effect 0 have expected value but in negative state
+ TakenTotalMod *= (-eff0->GetModifier()->m_amount+100.0f)/100.0f;
+ }
+ break;
+ }
+ }
+
+ if(attType != RANGED_ATTACK)
+ {
+ AuraList const& mModMeleeDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT);
+ for(AuraList::const_iterator i = mModMeleeDamageTakenPercent.begin(); i != mModMeleeDamageTakenPercent.end(); ++i)
+ TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+ }
+ else
+ {
+ AuraList const& mModRangedDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT);
+ for(AuraList::const_iterator i = mModRangedDamageTakenPercent.begin(); i != mModRangedDamageTakenPercent.end(); ++i)
+ TakenTotalMod *= ((*i)->GetModifier()->m_amount+100.0f)/100.0f;
+ }
+
+ float tmpDamage = float(int32(*pdamage) + DoneFlatBenefit) * DoneTotalMod;
+
+ // apply spellmod to Done damage
+ if(spellProto)
+ {
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, tmpDamage);
+ }
+
+ tmpDamage = (tmpDamage + TakenFlatBenefit)*TakenTotalMod;
+
+ // bonus result can be negative
+ *pdamage = tmpDamage > 0 ? uint32(tmpDamage) : 0;
+}
+
+void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply)
+{
+ if (apply)
+ {
+ for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(), next; itr != m_spellImmune[op].end(); itr = next)
+ {
+ next = itr; ++next;
+ if(itr->type == type)
+ {
+ m_spellImmune[op].erase(itr);
+ next = m_spellImmune[op].begin();
+ }
+ }
+ SpellImmune Immune;
+ Immune.spellId = spellId;
+ Immune.type = type;
+ m_spellImmune[op].push_back(Immune);
+ }
+ else
+ {
+ for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(); itr != m_spellImmune[op].end(); ++itr)
+ {
+ if(itr->spellId == spellId)
+ {
+ m_spellImmune[op].erase(itr);
+ break;
+ }
+ }
+ }
+
+}
+
+void Unit::ApplySpellDispelImmunity(const SpellEntry * spellProto, DispelType type, bool apply)
+{
+ ApplySpellImmune(spellProto->Id,IMMUNITY_DISPEL, type, apply);
+
+ if (apply && spellProto->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
+ RemoveAurasWithDispelType(type);
+}
+
+float Unit::GetWeaponProcChance() const
+{
+ // normalized proc chance for weapon attack speed
+ // (odd formulae...)
+ if(isAttackReady(BASE_ATTACK))
+ return (GetAttackTime(BASE_ATTACK) * 1.8f / 1000.0f);
+ else if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
+ return (GetAttackTime(OFF_ATTACK) * 1.6f / 1000.0f);
+ return 0;
+}
+
+float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM) const
+{
+ // proc per minute chance calculation
+ if (PPM <= 0) return 0.0f;
+ uint32 result = uint32((WeaponSpeed * PPM) / 600.0f); // result is chance in percents (probability = Speed_in_sec * (PPM / 60))
+ return result;
+}
+
+void Unit::Mount(uint32 mount)
+{
+ if(!mount)
+ return;
+
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOUNTING);
+
+ SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, mount);
+
+ SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT );
+
+ // unsummon pet
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ Pet* pet = GetPet();
+ if(pet)
+ {
+ if(pet->isControlled())
+ {
+ ((Player*)this)->SetTemporaryUnsummonedPetNumber(pet->GetCharmInfo()->GetPetNumber());
+ ((Player*)this)->SetOldPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL));
+ }
+
+ ((Player*)this)->RemovePet(NULL,PET_SAVE_NOT_IN_SLOT);
+ }
+ else
+ ((Player*)this)->SetTemporaryUnsummonedPetNumber(0);
+ }
+}
+
+void Unit::Unmount()
+{
+ if(!IsMounted())
+ return;
+
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_MOUNTED);
+
+ SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, 0);
+ RemoveFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT );
+
+ // only resummon old pet if the player is already added to a map
+ // this prevents adding a pet to a not created map which would otherwise cause a crash
+ // (it could probably happen when logging in after a previous crash)
+ if(GetTypeId() == TYPEID_PLAYER && IsInWorld() && ((Player*)this)->GetTemporaryUnsummonedPetNumber() && isAlive())
+ {
+ Pet* NewPet = new Pet;
+ if(!NewPet->LoadPetFromDB(this, 0, ((Player*)this)->GetTemporaryUnsummonedPetNumber(), true))
+ delete NewPet;
+
+ ((Player*)this)->SetTemporaryUnsummonedPetNumber(0);
+ }
+}
+
+void Unit::SetInCombatWith(Unit* enemy)
+{
+ Unit* eOwner = enemy->GetCharmerOrOwnerOrSelf();
+ if(eOwner->IsPvP())
+ {
+ SetInCombatState(true);
+ return;
+ }
+
+ //check for duel
+ if(eOwner->GetTypeId() == TYPEID_PLAYER && ((Player*)eOwner)->duel)
+ {
+ Unit const* myOwner = GetCharmerOrOwnerOrSelf();
+ if(((Player const*)eOwner)->duel->opponent == myOwner)
+ {
+ SetInCombatState(true);
+ return;
+ }
+ }
+ SetInCombatState(false);
+}
+
+void Unit::SetInCombatState(bool PvP)
+{
+ // only alive units can be in combat
+ if(!isAlive())
+ return;
+
+ if(PvP)
+ m_CombatTimer = 5000;
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
+
+ if(isCharmed() || (GetTypeId()!=TYPEID_PLAYER && ((Creature*)this)->isPet()))
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
+}
+
+void Unit::ClearInCombat()
+{
+ m_CombatTimer = 0;
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
+
+ if(isCharmed() || (GetTypeId()!=TYPEID_PLAYER && ((Creature*)this)->isPet()))
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
+
+ // Player's state will be cleared in Player::UpdateContestedPvP
+ if(GetTypeId()!=TYPEID_PLAYER)
+ clearUnitState(UNIT_STAT_ATTACK_PLAYER);
+}
+
+bool Unit::isTargetableForAttack() const
+{
+ if (GetTypeId()==TYPEID_PLAYER && ((Player *)this)->isGameMaster())
+ return false;
+
+ if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE))
+ return false;
+
+ return isAlive() && !hasUnitState(UNIT_STAT_DIED)&& !isInFlight() /*&& !isStealth()*/;
+}
+
+int32 Unit::ModifyHealth(int32 dVal)
+{
+ int32 gain = 0;
+
+ if(dVal==0)
+ return 0;
+
+ int32 curHealth = (int32)GetHealth();
+
+ int32 val = dVal + curHealth;
+ if(val <= 0)
+ {
+ SetHealth(0);
+ return -curHealth;
+ }
+
+ int32 maxHealth = (int32)GetMaxHealth();
+
+ if(val < maxHealth)
+ {
+ SetHealth(val);
+ gain = val - curHealth;
+ }
+ else if(curHealth != maxHealth)
+ {
+ SetHealth(maxHealth);
+ gain = maxHealth - curHealth;
+ }
+
+ return gain;
+}
+
+int32 Unit::ModifyPower(Powers power, int32 dVal)
+{
+ int32 gain = 0;
+
+ if(dVal==0)
+ return 0;
+
+ int32 curPower = (int32)GetPower(power);
+
+ int32 val = dVal + curPower;
+ if(val <= 0)
+ {
+ SetPower(power,0);
+ return -curPower;
+ }
+
+ int32 maxPower = (int32)GetMaxPower(power);
+
+ if(val < maxPower)
+ {
+ SetPower(power,val);
+ gain = val - curPower;
+ }
+ else if(curPower != maxPower)
+ {
+ SetPower(power,maxPower);
+ gain = maxPower - curPower;
+ }
+
+ return gain;
+}
+
+bool Unit::isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList) const
+{
+ if(!u)
+ return false;
+
+ // Always can see self
+ if (u==this)
+ return true;
+
+ // player visible for other player if not logout and at same transport
+ // including case when player is out of world
+ bool at_same_transport =
+ GetTypeId() == TYPEID_PLAYER && u->GetTypeId()==TYPEID_PLAYER &&
+ !((Player*)this)->GetSession()->PlayerLogout() && !((Player*)u)->GetSession()->PlayerLogout() &&
+ !((Player*)this)->GetSession()->PlayerLoading() && !((Player*)u)->GetSession()->PlayerLoading() &&
+ ((Player*)this)->GetTransport() && ((Player*)this)->GetTransport() == ((Player*)u)->GetTransport();
+
+ // not in world
+ if(!at_same_transport && (!IsInWorld() || !u->IsInWorld()))
+ return false;
+
+ // forbidden to seen (at GM respawn command)
+ if(m_Visibility==VISIBILITY_RESPAWN)
+ return false;
+
+ // always seen by owner
+ if(GetCharmerOrOwnerGUID()==u->GetGUID())
+ return true;
+
+ // Grid dead/alive checks
+ if( u->GetTypeId()==TYPEID_PLAYER)
+ {
+ // non visible at grid for any stealth state
+ if(!IsVisibleInGridForPlayer((Player *)u))
+ return false;
+
+ // if player is dead then he can't detect anyone in anycases
+ if(!u->isAlive())
+ detect = false;
+ }
+ else
+ {
+ // all dead creatures/players not visible for any creatures
+ if(!u->isAlive() || !isAlive())
+ return false;
+ }
+
+ // different visible distance checks
+ if(u->isInFlight()) // what see player in flight
+ {
+ // use object grey distance for all (only see objects any way)
+ if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceInFlight()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f)))
+ return false;
+ }
+ else if(!isAlive()) // distance for show body
+ {
+ if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f)))
+ return false;
+ }
+ else if(GetTypeId()==TYPEID_PLAYER) // distance for show player
+ {
+ if(u->GetTypeId()==TYPEID_PLAYER)
+ {
+ // Players far than max visible distance for player or not in our map are not visible too
+ if (!at_same_transport && !IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
+ return false;
+ }
+ else
+ {
+ // Units far than max visible distance for creature or not in our map are not visible too
+ if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForCreature()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
+ return false;
+ }
+ }
+ else if(GetCharmerOrOwnerGUID()) // distance for show pet/charmed
+ {
+ // Pet/charmed far than max visible distance for player or not in our map are not visible too
+ if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForPlayer()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
+ return false;
+ }
+ else // distance for show creature
+ {
+ // Units far than max visible distance for creature or not in our map are not visible too
+ if (!IsWithinDistInMap(u,World::GetMaxVisibleDistanceForCreature()+(inVisibleList ? World::GetVisibleUnitGreyDistance() : 0.0f)))
+ return false;
+ }
+
+ // Visible units, always are visible for all units, except for units under invisibility
+ if (m_Visibility == VISIBILITY_ON && u->m_invisibilityMask==0)
+ return true;
+
+ // GMs see any players, not higher GMs and all units
+ if (u->GetTypeId() == TYPEID_PLAYER && ((Player *)u)->isGameMaster())
+ {
+ if(GetTypeId() == TYPEID_PLAYER)
+ return ((Player *)this)->GetSession()->GetSecurity() <= ((Player *)u)->GetSession()->GetSecurity();
+ else
+ return true;
+ }
+
+ // non faction visibility non-breakable for non-GMs
+ if (m_Visibility == VISIBILITY_OFF)
+ return false;
+
+ // raw invisibility
+ bool invisible = (m_invisibilityMask != 0 || u->m_invisibilityMask !=0);
+
+ // detectable invisibility case
+ if( invisible && (
+ // Invisible units, always are visible for units under same invisibility type
+ (m_invisibilityMask & u->m_invisibilityMask)!=0 ||
+ // Invisible units, always are visible for unit that can detect this invisibility (have appropriate level for detect)
+ u->canDetectInvisibilityOf(this) ||
+ // Units that can detect invisibility always are visible for units that can be detected
+ canDetectInvisibilityOf(u) ))
+ {
+ invisible = false;
+ }
+
+ // special cases for always overwrite invisibility/stealth
+ if(invisible || m_Visibility == VISIBILITY_GROUP_STEALTH)
+ {
+ // non-hostile case
+ if (!u->IsHostileTo(this))
+ {
+ // player see other player with stealth/invisibility only if he in same group or raid or same team (raid/team case dependent from conf setting)
+ if(GetTypeId()==TYPEID_PLAYER && u->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(((Player*)this)->IsGroupVisibleFor(((Player*)u)))
+ return true;
+
+ // else apply same rules as for hostile case (detecting check for stealth)
+ }
+ }
+ // hostile case
+ else
+ {
+ // Hunter mark functionality
+ AuraList const& auras = GetAurasByType(SPELL_AURA_MOD_STALKED);
+ for(AuraList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
+ if((*iter)->GetCasterGUID()==u->GetGUID())
+ return true;
+
+ // else apply detecting check for stealth
+ }
+
+ // none other cases for detect invisibility, so invisible
+ if(invisible)
+ return false;
+
+ // else apply stealth detecting check
+ }
+
+ // unit got in stealth in this moment and must ignore old detected state
+ if (m_Visibility == VISIBILITY_GROUP_NO_DETECT)
+ return false;
+
+ // GM invisibility checks early, invisibility if any detectable, so if not stealth then visible
+ if (m_Visibility != VISIBILITY_GROUP_STEALTH)
+ return true;
+
+ // NOW ONLY STEALTH CASE
+
+ // stealth and detected and visible for some seconds
+ if (u->GetTypeId() == TYPEID_PLAYER && ((Player*)u)->m_DetectInvTimer > 300 && ((Player*)u)->HaveAtClient(this))
+ return true;
+
+ //if in non-detect mode then invisible for unit
+ if (!detect)
+ return false;
+
+ // Special cases
+
+ // If is attacked then stealth is lost, some creature can use stealth too
+ if( !getAttackers().empty() )
+ return true;
+
+ // If there is collision rogue is seen regardless of level difference
+ // TODO: check sizes in DB
+ float distance = GetDistance(u);
+ if (distance < 0.24f)
+ return true;
+
+ //If a mob or player is stunned he will not be able to detect stealth
+ if (u->hasUnitState(UNIT_STAT_STUNNED) && (u != this))
+ return false;
+
+ // Creature can detect target only in aggro radius
+ if(u->GetTypeId() != TYPEID_PLAYER)
+ {
+ //Always invisible from back and out of aggro range
+ bool isInFront = u->isInFront(this,((Creature const*)u)->GetAttackDistance(this));
+ if(!isInFront)
+ return false;
+ }
+ else
+ {
+ //Always invisible from back
+ bool isInFront = u->isInFront(this,(GetTypeId()==TYPEID_PLAYER || GetCharmerOrOwnerGUID()) ? World::GetMaxVisibleDistanceForPlayer() : World::GetMaxVisibleDistanceForCreature());
+ if(!isInFront)
+ return false;
+ }
+
+ // if doesn't have stealth detection (Shadow Sight), then check how stealthy the unit is, otherwise just check los
+ if(!u->HasAuraType(SPELL_AURA_DETECT_STEALTH))
+ {
+ //Calculation if target is in front
+
+ //Visible distance based on stealth value (stealth rank 4 300MOD, 10.5 - 3 = 7.5)
+ float visibleDistance = 10.5f - (GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH)/100.0f);
+
+ //Visible distance is modified by
+ //-Level Diff (every level diff = 1.0f in visible distance)
+ visibleDistance += int32(u->getLevelForTarget(this)) - int32(this->getLevelForTarget(u));
+
+ //This allows to check talent tree and will add addition stealth dependent on used points)
+ int32 stealthMod = GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH_LEVEL);
+ if(stealthMod < 0)
+ stealthMod = 0;
+
+ //-Stealth Mod(positive like Master of Deception) and Stealth Detection(negative like paranoia)
+ //based on wowwiki every 5 mod we have 1 more level diff in calculation
+ visibleDistance += (int32(u->GetTotalAuraModifier(SPELL_AURA_MOD_DETECT)) - stealthMod)/5.0f;
+
+ if(distance > visibleDistance)
+ return false;
+ }
+
+ // Now check is target visible with LoS
+ float ox,oy,oz;
+ u->GetPosition(ox,oy,oz);
+ return IsWithinLOS(ox,oy,oz);
+}
+
+void Unit::SetVisibility(UnitVisibility x)
+{
+ m_Visibility = x;
+
+ if(IsInWorld())
+ {
+ Map *m = MapManager::Instance().GetMap(GetMapId(), this);
+
+ if(GetTypeId()==TYPEID_PLAYER)
+ m->PlayerRelocation((Player*)this,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
+ else
+ m->CreatureRelocation((Creature*)this,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
+ }
+}
+
+bool Unit::canDetectInvisibilityOf(Unit const* u) const
+{
+ if(uint32 mask = (m_detectInvisibilityMask & u->m_invisibilityMask))
+ {
+ for(uint32 i = 0; i < 10; ++i)
+ {
+ if(((1 << i) & mask)==0)
+ continue;
+
+ // find invisibility level
+ uint32 invLevel = 0;
+ Unit::AuraList const& iAuras = u->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY);
+ for(Unit::AuraList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr)
+ if(((*itr)->GetModifier()->m_miscvalue)==i && invLevel < (*itr)->GetModifier()->m_amount)
+ invLevel = (*itr)->GetModifier()->m_amount;
+
+ // find invisibility detect level
+ uint32 detectLevel = 0;
+ Unit::AuraList const& dAuras = GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION);
+ for(Unit::AuraList::const_iterator itr = dAuras.begin(); itr != dAuras.end(); ++itr)
+ if(((*itr)->GetModifier()->m_miscvalue)==i && detectLevel < (*itr)->GetModifier()->m_amount)
+ detectLevel = (*itr)->GetModifier()->m_amount;
+
+ if(i==6 && GetTypeId()==TYPEID_PLAYER) // special drunk detection case
+ {
+ detectLevel = ((Player*)this)->GetDrunkValue();
+ }
+
+ if(invLevel <= detectLevel)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
+{
+ int32 main_speed_mod = 0;
+ float stack_bonus = 1.0f;
+ float non_stack_bonus = 1.0f;
+
+ switch(mtype)
+ {
+ case MOVE_WALK:
+ return;
+ case MOVE_RUN:
+ {
+ if (IsMounted()) // Use on mount auras
+ {
+ main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED);
+ stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS);
+ non_stack_bonus = (100.0f + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK))/100.0f;
+ }
+ else
+ {
+ main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_SPEED);
+ stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_SPEED_ALWAYS);
+ non_stack_bonus = (100.0f + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_SPEED_NOT_STACK))/100.0f;
+ }
+ break;
+ }
+ case MOVE_WALKBACK:
+ return;
+ case MOVE_SWIM:
+ {
+ main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_SWIM_SPEED);
+ break;
+ }
+ case MOVE_SWIMBACK:
+ return;
+ case MOVE_FLY:
+ {
+ if (IsMounted()) // Use on mount auras
+ main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED);
+ else // Use not mount (shapeshift for example) auras (should stack)
+ main_speed_mod = GetTotalAuraModifier(SPELL_AURA_MOD_SPEED_FLIGHT);
+ stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS);
+ non_stack_bonus = (100.0 + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK))/100.0f;
+ break;
+ }
+ case MOVE_FLYBACK:
+ return;
+ default:
+ sLog.outError("Unit::UpdateSpeed: Unsupported move type (%d)", mtype);
+ return;
+ }
+
+ float bonus = non_stack_bonus > stack_bonus ? non_stack_bonus : stack_bonus;
+ // now we ready for speed calculation
+ float speed = main_speed_mod ? bonus*(100.0f + main_speed_mod)/100.0f : bonus;
+
+ switch(mtype)
+ {
+ case MOVE_RUN:
+ case MOVE_SWIM:
+ case MOVE_FLY:
+ {
+ // Normalize speed by 191 aura SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED if need
+ // TODO: possible affect only on MOVE_RUN
+ if(int32 normalization = GetMaxPositiveAuraModifier(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED))
+ {
+ // Use speed from aura
+ float max_speed = normalization / baseMoveSpeed[mtype];
+ if (speed > max_speed)
+ speed = max_speed;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Apply strongest slow aura mod to speed
+ int32 slow = GetMaxNegativeAuraModifier(SPELL_AURA_MOD_DECREASE_SPEED);
+ if (slow)
+ speed *=(100.0f + slow)/100.0f;
+ SetSpeed(mtype, speed, forced);
+}
+
+float Unit::GetSpeed( UnitMoveType mtype ) const
+{
+ return m_speed_rate[mtype]*baseMoveSpeed[mtype];
+}
+
+void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
+{
+ if (rate < 0)
+ rate = 0.0f;
+
+ // Update speed only on change
+ if (m_speed_rate[mtype] == rate)
+ return;
+
+ m_speed_rate[mtype] = rate;
+
+ propagateSpeedChange();
+
+ // Send speed change packet only for player
+ if (GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ WorldPacket data;
+ if(!forced)
+ {
+ switch(mtype)
+ {
+ case MOVE_WALK:
+ data.Initialize(MSG_MOVE_SET_WALK_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_RUN:
+ data.Initialize(MSG_MOVE_SET_RUN_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_WALKBACK:
+ data.Initialize(MSG_MOVE_SET_RUN_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_SWIM:
+ data.Initialize(MSG_MOVE_SET_SWIM_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_SWIMBACK:
+ data.Initialize(MSG_MOVE_SET_SWIM_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_TURN:
+ data.Initialize(MSG_MOVE_SET_TURN_RATE, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_FLY:
+ data.Initialize(MSG_MOVE_SET_FLIGHT_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ case MOVE_FLYBACK:
+ data.Initialize(MSG_MOVE_SET_FLIGHT_BACK_SPEED, 8+4+1+4+4+4+4+4+4+4);
+ break;
+ default:
+ sLog.outError("Unit::SetSpeed: Unsupported move type (%d), data not sent to client.",mtype);
+ return;
+ }
+
+ data.append(GetPackGUID());
+ data << uint32(0); //movement flags
+ data << uint8(0); //unk
+ data << uint32(getMSTime());
+ data << float(GetPositionX());
+ data << float(GetPositionY());
+ data << float(GetPositionZ());
+ data << float(GetOrientation());
+ data << uint32(0); //flag unk
+ data << float(GetSpeed(mtype));
+ SendMessageToSet( &data, true );
+ }
+ else
+ {
+ // register forced speed changes for WorldSession::HandleForceSpeedChangeAck
+ // and do it only for real sent packets and use run for run/mounted as client expected
+ ++((Player*)this)->m_forced_speed_changes[mtype];
+ switch(mtype)
+ {
+ case MOVE_WALK:
+ data.Initialize(SMSG_FORCE_WALK_SPEED_CHANGE, 16);
+ break;
+ case MOVE_RUN:
+ data.Initialize(SMSG_FORCE_RUN_SPEED_CHANGE, 17);
+ break;
+ case MOVE_WALKBACK:
+ data.Initialize(SMSG_FORCE_RUN_BACK_SPEED_CHANGE, 16);
+ break;
+ case MOVE_SWIM:
+ data.Initialize(SMSG_FORCE_SWIM_SPEED_CHANGE, 16);
+ break;
+ case MOVE_SWIMBACK:
+ data.Initialize(SMSG_FORCE_SWIM_BACK_SPEED_CHANGE, 16);
+ break;
+ case MOVE_TURN:
+ data.Initialize(SMSG_FORCE_TURN_RATE_CHANGE, 16);
+ break;
+ case MOVE_FLY:
+ data.Initialize(SMSG_FORCE_FLIGHT_SPEED_CHANGE, 16);
+ break;
+ case MOVE_FLYBACK:
+ data.Initialize(SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, 16);
+ break;
+ default:
+ sLog.outError("Unit::SetSpeed: Unsupported move type (%d), data not sent to client.",mtype);
+ return;
+ }
+ data.append(GetPackGUID());
+ data << (uint32)0;
+ if (mtype == MOVE_RUN)
+ data << uint8(0); // new 2.1.0
+ data << float(GetSpeed(mtype));
+ SendMessageToSet( &data, true );
+ }
+ if(Pet* pet = GetPet())
+ pet->SetSpeed(MOVE_RUN, m_speed_rate[mtype],forced);
+}
+
+void Unit::SetHover(bool on)
+{
+ if(on)
+ CastSpell(this,11010,true);
+ else
+ RemoveAurasDueToSpell(11010);
+}
+
+void Unit::setDeathState(DeathState s)
+{
+ if (s != ALIVE && s!= JUST_ALIVED)
+ {
+ CombatStop();
+ DeleteThreatList();
+ ClearComboPointHolders(); // any combo points pointed to unit lost at it death
+
+ if(IsNonMeleeSpellCasted(false))
+ InterruptNonMeleeSpells(false);
+ }
+
+ if (s == JUST_DIED)
+ {
+ RemoveAllAurasOnDeath();
+ UnsummonAllTotems();
+
+ ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false);
+ ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false);
+ // remove aurastates allowing special moves
+ ClearAllReactives();
+ ClearDiminishings();
+ }
+ else if(s == JUST_ALIVED)
+ {
+ RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); // clear skinnable for creature and player (at battleground)
+ }
+
+ if (m_deathState != ALIVE && s == ALIVE)
+ {
+ //_ApplyAllAuraMods();
+ }
+ m_deathState = s;
+}
+
+/*########################################
+######## ########
+######## AGGRO SYSTEM ########
+######## ########
+########################################*/
+bool Unit::CanHaveThreatList() const
+{
+ // only creatures can have threat list
+ if( GetTypeId() != TYPEID_UNIT )
+ return false;
+
+ // only alive units can have threat list
+ if( !isAlive() )
+ return false;
+
+ // pets and totems can not have threat list
+ if( ((Creature*)this)->isPet() || ((Creature*)this)->isTotem() )
+ return false;
+
+ return true;
+}
+
+//======================================================================
+
+float Unit::ApplyTotalThreatModifier(float threat, SpellSchoolMask schoolMask)
+{
+ if(!HasAuraType(SPELL_AURA_MOD_THREAT))
+ return threat;
+
+ SpellSchools school = GetFirstSchoolInMask(schoolMask);
+
+ return threat * m_threatModifier[school];
+}
+
+//======================================================================
+
+void Unit::AddThreat(Unit* pVictim, float threat, SpellSchoolMask schoolMask, SpellEntry const *threatSpell)
+{
+ // Only mobs can manage threat lists
+ if(CanHaveThreatList())
+ m_ThreatManager.addThreat(pVictim, threat, schoolMask, threatSpell);
+}
+
+//======================================================================
+
+void Unit::DeleteThreatList()
+{
+ m_ThreatManager.clearReferences();
+}
+
+//======================================================================
+
+void Unit::TauntApply(Unit* taunter)
+{
+ assert(GetTypeId()== TYPEID_UNIT);
+
+ if(!taunter || (taunter->GetTypeId() == TYPEID_PLAYER && ((Player*)taunter)->isGameMaster()))
+ return;
+
+ if(!CanHaveThreatList())
+ return;
+
+ Unit *target = getVictim();
+ if(target && target == taunter)
+ return;
+
+ SetInFront(taunter);
+ if (((Creature*)this)->AI())
+ ((Creature*)this)->AI()->AttackStart(taunter);
+
+ m_ThreatManager.tauntApply(taunter);
+}
+
+//======================================================================
+
+void Unit::TauntFadeOut(Unit *taunter)
+{
+ assert(GetTypeId()== TYPEID_UNIT);
+
+ if(!taunter || (taunter->GetTypeId() == TYPEID_PLAYER && ((Player*)taunter)->isGameMaster()))
+ return;
+
+ if(!CanHaveThreatList())
+ return;
+
+ Unit *target = getVictim();
+ if(!target || target != taunter)
+ return;
+
+ if(m_ThreatManager.isThreatListEmpty())
+ {
+ if(((Creature*)this)->AI())
+ ((Creature*)this)->AI()->EnterEvadeMode();
+ return;
+ }
+
+ m_ThreatManager.tauntFadeOut(taunter);
+ target = m_ThreatManager.getHostilTarget();
+
+ if (target && target != taunter)
+ {
+ SetInFront(target);
+ if (((Creature*)this)->AI())
+ ((Creature*)this)->AI()->AttackStart(target);
+ }
+}
+
+//======================================================================
+
+bool Unit::SelectHostilTarget()
+{
+ //function provides main threat functionality
+ //next-victim-selection algorithm and evade mode are called
+ //threat list sorting etc.
+
+ assert(GetTypeId()== TYPEID_UNIT);
+ Unit* target = NULL;
+
+ //This function only useful once AI has been initilazied
+ if (!((Creature*)this)->AI())
+ return false;
+
+ if(!m_ThreatManager.isThreatListEmpty())
+ {
+ if(!HasAuraType(SPELL_AURA_MOD_TAUNT))
+ {
+ target = m_ThreatManager.getHostilTarget();
+ }
+ }
+
+ if(target)
+ {
+ if(!hasUnitState(UNIT_STAT_STUNNED))
+ SetInFront(target);
+ ((Creature*)this)->AI()->AttackStart(target);
+ return true;
+ }
+
+ // no target but something prevent go to evade mode
+ if( !isInCombat() || HasAuraType(SPELL_AURA_MOD_TAUNT) )
+ return false;
+
+ // last case when creature don't must go to evade mode:
+ // it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list
+ // for example at owner command to pet attack some far away creature
+ // Note: creature not have targeted movement generator but have attacker in this case
+ if( GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE )
+ {
+ for(AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr)
+ {
+ if( (*itr)->IsInMap(this) && (*itr)->isTargetableForAttack() && (*itr)->isInAccessablePlaceFor((Creature*)this) )
+ return false;
+ }
+ }
+
+ // enter in evade mode in other case
+ ((Creature*)this)->AI()->EnterEvadeMode();
+
+ return false;
+}
+
+//======================================================================
+//======================================================================
+//======================================================================
+
+int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_index, int32 effBasePoints, Unit const* target)
+{
+ Player* unitPlayer = (GetTypeId() == TYPEID_PLAYER) ? (Player*)this : NULL;
+
+ uint8 comboPoints = unitPlayer ? unitPlayer->GetComboPoints() : 0;
+
+ int32 level = int32(getLevel()) - int32(spellProto->spellLevel);
+ if (level > spellProto->maxLevel && spellProto->maxLevel > 0)
+ level = spellProto->maxLevel;
+
+ float basePointsPerLevel = spellProto->EffectRealPointsPerLevel[effect_index];
+ float randomPointsPerLevel = spellProto->EffectDicePerLevel[effect_index];
+ int32 basePoints = int32(effBasePoints + level * basePointsPerLevel);
+ int32 randomPoints = int32(spellProto->EffectDieSides[effect_index] + level * randomPointsPerLevel);
+ float comboDamage = spellProto->EffectPointsPerComboPoint[effect_index];
+
+ // prevent random generator from getting confused by spells casted with Unit::CastCustomSpell
+ int32 randvalue = spellProto->EffectBaseDice[effect_index] >= randomPoints ? spellProto->EffectBaseDice[effect_index]:irand(spellProto->EffectBaseDice[effect_index], randomPoints);
+ int32 value = basePoints + randvalue;
+ //random damage
+ if(comboDamage != 0 && unitPlayer && target && (target->GetGUID() == unitPlayer->GetComboTarget()))
+ value += (int32)(comboDamage * comboPoints);
+
+ if(Player* modOwner = GetSpellModOwner())
+ {
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_ALL_EFFECTS, value);
+ switch(effect_index)
+ {
+ case 0:
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_EFFECT1, value);
+ break;
+ case 1:
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_EFFECT2, value);
+ break;
+ case 2:
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_EFFECT3, value);
+ break;
+ }
+ }
+
+ if(spellProto->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION && spellProto->spellLevel &&
+ spellProto->Effect[effect_index] != SPELL_EFFECT_WEAPON_PERCENT_DAMAGE &&
+ spellProto->Effect[effect_index] != SPELL_EFFECT_KNOCK_BACK)
+ value = int32(value*0.25f*exp(getLevel()*(70-spellProto->spellLevel)/1000.0f));
+
+ return value;
+}
+
+int32 Unit::CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_index, Unit const* target)
+{
+ Player* unitPlayer = (GetTypeId() == TYPEID_PLAYER) ? (Player*)this : NULL;
+
+ uint8 comboPoints = unitPlayer ? unitPlayer->GetComboPoints() : 0;
+
+ int32 minduration = GetSpellDuration(spellProto);
+ int32 maxduration = GetSpellMaxDuration(spellProto);
+
+ int32 duration;
+
+ if( minduration != -1 && minduration != maxduration )
+ duration = minduration + int32((maxduration - minduration) * comboPoints / 5);
+ else
+ duration = minduration;
+
+ if (duration > 0)
+ {
+ int32 mechanic = GetEffectMechanic(spellProto, effect_index);
+ // Find total mod value (negative bonus)
+ int32 durationMod_always = target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD, mechanic);
+ // Find max mod (negative bonus)
+ int32 durationMod_not_stack = target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK, mechanic);
+
+ int32 durationMod = 0;
+ // Select strongest negative mod
+ if (durationMod_always > durationMod_not_stack)
+ durationMod = durationMod_not_stack;
+ else
+ durationMod = durationMod_always;
+
+ if (durationMod != 0)
+ duration = int32(int64(duration) * (100+durationMod) /100);
+
+ if (duration < 0) duration = 0;
+ }
+
+ return duration;
+}
+
+DiminishingLevels Unit::GetDiminishing(DiminishingGroup group)
+{
+ for(Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
+ {
+ if(i->DRGroup != group)
+ continue;
+
+ if(!i->hitCount)
+ return DIMINISHING_LEVEL_1;
+
+ if(!i->hitTime)
+ return DIMINISHING_LEVEL_1;
+
+ // If last spell was casted more than 15 seconds ago - reset the count.
+ if(i->stack==0 && getMSTimeDiff(i->hitTime,getMSTime()) > 15000)
+ {
+ i->hitCount = DIMINISHING_LEVEL_1;
+ return DIMINISHING_LEVEL_1;
+ }
+ // or else increase the count.
+ else
+ {
+ return DiminishingLevels(i->hitCount);
+ }
+ }
+ return DIMINISHING_LEVEL_1;
+}
+
+void Unit::IncrDiminishing(DiminishingGroup group)
+{
+ // Checking for existing in the table
+ bool IsExist = false;
+ for(Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
+ {
+ if(i->DRGroup != group)
+ continue;
+
+ IsExist = true;
+ if(i->hitCount < DIMINISHING_LEVEL_IMMUNE)
+ i->hitCount += 1;
+
+ break;
+ }
+
+ if(!IsExist)
+ m_Diminishing.push_back(DiminishingReturn(group,getMSTime(),DIMINISHING_LEVEL_2));
+}
+
+void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster,DiminishingLevels Level)
+{
+ if(duration == -1 || group == DIMINISHING_NONE || caster->IsFriendlyTo(this) )
+ return;
+
+ // Duration of crowd control abilities on pvp target is limited by 10 sec. (2.2.0)
+ if(duration > 10000 && IsDiminishingReturnsGroupDurationLimited(group))
+ {
+ // test pet/charm masters instead pets/charmeds
+ Unit const* targetOwner = GetCharmerOrOwner();
+ Unit const* casterOwner = caster->GetCharmerOrOwner();
+
+ Unit const* target = targetOwner ? targetOwner : this;
+ Unit const* source = casterOwner ? casterOwner : caster;
+
+ if(target->GetTypeId() == TYPEID_PLAYER && source->GetTypeId() == TYPEID_PLAYER)
+ duration = 10000;
+ }
+
+ float mod = 1.0f;
+
+ // Some diminishings applies to mobs too (for example, Stun)
+ if((GetDiminishingReturnsGroupType(group) == DRTYPE_PLAYER && GetTypeId() == TYPEID_PLAYER) || GetDiminishingReturnsGroupType(group) == DRTYPE_ALL)
+ {
+ DiminishingLevels diminish = Level;
+ switch(diminish)
+ {
+ case DIMINISHING_LEVEL_1: break;
+ case DIMINISHING_LEVEL_2: mod = 0.5f; break;
+ case DIMINISHING_LEVEL_3: mod = 0.25f; break;
+ case DIMINISHING_LEVEL_IMMUNE: mod = 0.0f;break;
+ default: break;
+ }
+ }
+
+ duration = int32(duration * mod);
+}
+
+void Unit::ApplyDiminishingAura( DiminishingGroup group, bool apply )
+{
+ // Checking for existing in the table
+ for(Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
+ {
+ if(i->DRGroup != group)
+ continue;
+
+ i->hitTime = getMSTime();
+
+ if(apply)
+ i->stack += 1;
+ else if(i->stack)
+ i->stack -= 1;
+
+ break;
+ }
+}
+
+Unit* Unit::GetUnit(WorldObject& object, uint64 guid)
+{
+ return ObjectAccessor::GetUnit(object,guid);
+}
+
+bool Unit::isVisibleForInState( Player const* u, bool inVisibleList ) const
+{
+ return isVisibleForOrDetect(u,false,inVisibleList);
+}
+
+uint32 Unit::GetCreatureType() const
+{
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(((Player*)this)->m_form);
+ if(ssEntry && ssEntry->creatureType > 0)
+ return ssEntry->creatureType;
+ else
+ return CREATURE_TYPE_HUMANOID;
+ }
+ else
+ return ((Creature*)this)->GetCreatureInfo()->type;
+}
+
+/*#######################################
+######## ########
+######## STAT SYSTEM ########
+######## ########
+#######################################*/
+
+bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply)
+{
+ if(unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END)
+ {
+ sLog.outError("ERROR in HandleStatModifier(): nonexisted UnitMods or wrong UnitModifierType!");
+ return false;
+ }
+
+ float val = 1.0f;
+
+ switch(modifierType)
+ {
+ case BASE_VALUE:
+ case TOTAL_VALUE:
+ m_auraModifiersGroup[unitMod][modifierType] += apply ? amount : -amount;
+ break;
+ case BASE_PCT:
+ case TOTAL_PCT:
+ if(amount <= -100.0f) //small hack-fix for -100% modifiers
+ amount = -200.0f;
+
+ val = (100.0f + amount) / 100.0f;
+ m_auraModifiersGroup[unitMod][modifierType] *= apply ? val : (1.0f/val);
+ break;
+
+ default:
+ break;
+ }
+
+ if(!CanModifyStats())
+ return false;
+
+ switch(unitMod)
+ {
+ case UNIT_MOD_STAT_STRENGTH:
+ case UNIT_MOD_STAT_AGILITY:
+ case UNIT_MOD_STAT_STAMINA:
+ case UNIT_MOD_STAT_INTELLECT:
+ case UNIT_MOD_STAT_SPIRIT: UpdateStats(GetStatByAuraGroup(unitMod)); break;
+
+ case UNIT_MOD_ARMOR: UpdateArmor(); break;
+ case UNIT_MOD_HEALTH: UpdateMaxHealth(); break;
+
+ case UNIT_MOD_MANA:
+ case UNIT_MOD_RAGE:
+ case UNIT_MOD_FOCUS:
+ case UNIT_MOD_ENERGY:
+ case UNIT_MOD_HAPPINESS: UpdateMaxPower(GetPowerTypeByAuraGroup(unitMod)); break;
+
+ case UNIT_MOD_RESISTANCE_HOLY:
+ case UNIT_MOD_RESISTANCE_FIRE:
+ case UNIT_MOD_RESISTANCE_NATURE:
+ case UNIT_MOD_RESISTANCE_FROST:
+ case UNIT_MOD_RESISTANCE_SHADOW:
+ case UNIT_MOD_RESISTANCE_ARCANE: UpdateResistances(GetSpellSchoolByAuraGroup(unitMod)); break;
+
+ case UNIT_MOD_ATTACK_POWER: UpdateAttackPowerAndDamage(); break;
+ case UNIT_MOD_ATTACK_POWER_RANGED: UpdateAttackPowerAndDamage(true); break;
+
+ case UNIT_MOD_DAMAGE_MAINHAND: UpdateDamagePhysical(BASE_ATTACK); break;
+ case UNIT_MOD_DAMAGE_OFFHAND: UpdateDamagePhysical(OFF_ATTACK); break;
+ case UNIT_MOD_DAMAGE_RANGED: UpdateDamagePhysical(RANGED_ATTACK); break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+float Unit::GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const
+{
+ if( unitMod >= UNIT_MOD_END || modifierType >= MODIFIER_TYPE_END)
+ {
+ sLog.outError("ERROR: trial to access nonexisted modifier value from UnitMods!");
+ return 0.0f;
+ }
+
+ if(modifierType == TOTAL_PCT && m_auraModifiersGroup[unitMod][modifierType] <= 0.0f)
+ return 0.0f;
+
+ return m_auraModifiersGroup[unitMod][modifierType];
+}
+
+float Unit::GetTotalStatValue(Stats stat) const
+{
+ UnitMods unitMod = UnitMods(UNIT_MOD_STAT_START + stat);
+
+ if(m_auraModifiersGroup[unitMod][TOTAL_PCT] <= 0.0f)
+ return 0.0f;
+
+ // value = ((base_value * base_pct) + total_value) * total_pct
+ float value = m_auraModifiersGroup[unitMod][BASE_VALUE] + GetCreateStat(stat);
+ value *= m_auraModifiersGroup[unitMod][BASE_PCT];
+ value += m_auraModifiersGroup[unitMod][TOTAL_VALUE];
+ value *= m_auraModifiersGroup[unitMod][TOTAL_PCT];
+
+ return value;
+}
+
+float Unit::GetTotalAuraModValue(UnitMods unitMod) const
+{
+ if(unitMod >= UNIT_MOD_END)
+ {
+ sLog.outError("ERROR: trial to access nonexisted UnitMods in GetTotalAuraModValue()!");
+ return 0.0f;
+ }
+
+ if(m_auraModifiersGroup[unitMod][TOTAL_PCT] <= 0.0f)
+ return 0.0f;
+
+ float value = m_auraModifiersGroup[unitMod][BASE_VALUE];
+ value *= m_auraModifiersGroup[unitMod][BASE_PCT];
+ value += m_auraModifiersGroup[unitMod][TOTAL_VALUE];
+ value *= m_auraModifiersGroup[unitMod][TOTAL_PCT];
+
+ return value;
+}
+
+SpellSchools Unit::GetSpellSchoolByAuraGroup(UnitMods unitMod) const
+{
+ SpellSchools school = SPELL_SCHOOL_NORMAL;
+
+ switch(unitMod)
+ {
+ case UNIT_MOD_RESISTANCE_HOLY: school = SPELL_SCHOOL_HOLY; break;
+ case UNIT_MOD_RESISTANCE_FIRE: school = SPELL_SCHOOL_FIRE; break;
+ case UNIT_MOD_RESISTANCE_NATURE: school = SPELL_SCHOOL_NATURE; break;
+ case UNIT_MOD_RESISTANCE_FROST: school = SPELL_SCHOOL_FROST; break;
+ case UNIT_MOD_RESISTANCE_SHADOW: school = SPELL_SCHOOL_SHADOW; break;
+ case UNIT_MOD_RESISTANCE_ARCANE: school = SPELL_SCHOOL_ARCANE; break;
+
+ default:
+ break;
+ }
+
+ return school;
+}
+
+Stats Unit::GetStatByAuraGroup(UnitMods unitMod) const
+{
+ Stats stat = STAT_STRENGTH;
+
+ switch(unitMod)
+ {
+ case UNIT_MOD_STAT_STRENGTH: stat = STAT_STRENGTH; break;
+ case UNIT_MOD_STAT_AGILITY: stat = STAT_AGILITY; break;
+ case UNIT_MOD_STAT_STAMINA: stat = STAT_STAMINA; break;
+ case UNIT_MOD_STAT_INTELLECT: stat = STAT_INTELLECT; break;
+ case UNIT_MOD_STAT_SPIRIT: stat = STAT_SPIRIT; break;
+
+ default:
+ break;
+ }
+
+ return stat;
+}
+
+Powers Unit::GetPowerTypeByAuraGroup(UnitMods unitMod) const
+{
+ Powers power = POWER_MANA;
+
+ switch(unitMod)
+ {
+ case UNIT_MOD_MANA: power = POWER_MANA; break;
+ case UNIT_MOD_RAGE: power = POWER_RAGE; break;
+ case UNIT_MOD_FOCUS: power = POWER_FOCUS; break;
+ case UNIT_MOD_ENERGY: power = POWER_ENERGY; break;
+ case UNIT_MOD_HAPPINESS: power = POWER_HAPPINESS; break;
+
+ default:
+ break;
+ }
+
+ return power;
+}
+
+float Unit::GetTotalAttackPowerValue(WeaponAttackType attType) const
+{
+ UnitMods unitMod = (attType == RANGED_ATTACK) ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
+
+ float val = GetTotalAuraModValue(unitMod);
+ if(val < 0.0f)
+ val = 0.0f;
+
+ return val;
+}
+
+float Unit::GetWeaponDamageRange(WeaponAttackType attType ,WeaponDamageRange type) const
+{
+ if (attType == OFF_ATTACK && !haveOffhandWeapon())
+ return 0.0f;
+
+ return m_weaponDamage[attType][type];
+}
+
+void Unit::SetLevel(uint32 lvl)
+{
+ SetUInt32Value(UNIT_FIELD_LEVEL, lvl);
+
+ // group update
+ if ((GetTypeId() == TYPEID_PLAYER) && ((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_LEVEL);
+}
+
+void Unit::SetHealth(uint32 val)
+{
+ uint32 maxHealth = GetMaxHealth();
+ if(maxHealth < val)
+ val = maxHealth;
+
+ SetUInt32Value(UNIT_FIELD_HEALTH, val);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_HP);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_CUR_HP);
+ }
+ }
+}
+
+void Unit::SetMaxHealth(uint32 val)
+{
+ uint32 health = GetHealth();
+ SetUInt32Value(UNIT_FIELD_MAXHEALTH, val);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_HP);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_HP);
+ }
+ }
+
+ if(val < health)
+ SetHealth(val);
+}
+
+void Unit::SetPower(Powers power, uint32 val)
+{
+ uint32 maxPower = GetMaxPower(power);
+ if(maxPower < val)
+ val = maxPower;
+
+ SetStatInt32Value(UNIT_FIELD_POWER1 + power, val);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_POWER);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_CUR_POWER);
+ }
+
+ // Update the pet's character sheet with happiness damage bonus
+ if(pet->getPetType() == HUNTER_PET && power == POWER_HAPPINESS)
+ {
+ pet->UpdateDamagePhysical(BASE_ATTACK);
+ }
+ }
+}
+
+void Unit::SetMaxPower(Powers power, uint32 val)
+{
+ uint32 cur_power = GetPower(power);
+ SetStatInt32Value(UNIT_FIELD_MAXPOWER1 + power, val);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_POWER);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_POWER);
+ }
+ }
+
+ if(val < cur_power)
+ SetPower(power, val);
+}
+
+void Unit::ApplyPowerMod(Powers power, uint32 val, bool apply)
+{
+ ApplyModUInt32Value(UNIT_FIELD_POWER1+power, val, apply);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_CUR_POWER);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_CUR_POWER);
+ }
+ }
+}
+
+void Unit::ApplyMaxPowerMod(Powers power, uint32 val, bool apply)
+{
+ ApplyModUInt32Value(UNIT_FIELD_MAXPOWER1+power, val, apply);
+
+ // group update
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ if(((Player*)this)->GetGroup())
+ ((Player*)this)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_MAX_POWER);
+ }
+ else if(((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_POWER);
+ }
+ }
+}
+
+void Unit::ApplyAuraProcTriggerDamage( Aura* aura, bool apply )
+{
+ AuraList& tAuraProcTriggerDamage = m_modAuras[SPELL_AURA_PROC_TRIGGER_DAMAGE];
+ if(apply)
+ tAuraProcTriggerDamage.push_back(aura);
+ else
+ tAuraProcTriggerDamage.remove(aura);
+}
+
+uint32 Unit::GetCreatePowers( Powers power ) const
+{
+ // POWER_FOCUS and POWER_HAPPINESS only have hunter pet
+ switch(power)
+ {
+ case POWER_MANA: return GetCreateMana();
+ case POWER_RAGE: return 1000;
+ case POWER_FOCUS: return (GetTypeId()==TYPEID_PLAYER || !((Creature const*)this)->isPet() || ((Pet const*)this)->getPetType()!=HUNTER_PET ? 0 : 100);
+ case POWER_ENERGY: return 100;
+ case POWER_HAPPINESS: return (GetTypeId()==TYPEID_PLAYER || !((Creature const*)this)->isPet() || ((Pet const*)this)->getPetType()!=HUNTER_PET ? 0 : 1050000);
+ }
+
+ return 0;
+}
+
+void Unit::AddToWorld()
+{
+ Object::AddToWorld();
+}
+
+void Unit::RemoveFromWorld()
+{
+ // cleanup
+ if(IsInWorld())
+ {
+ RemoveNotOwnSingleTargetAuras();
+ }
+
+ Object::RemoveFromWorld();
+}
+
+void Unit::CleanupsBeforeDelete()
+{
+ if(m_uint32Values) // only for fully created object
+ {
+ InterruptNonMeleeSpells(true);
+ m_Events.KillAllEvents();
+ CombatStop();
+ ClearComboPointHolders();
+ DeleteThreatList();
+ getHostilRefManager().setOnlineOfflineState(false);
+ RemoveAllAuras();
+ RemoveAllGameObjects();
+ RemoveAllDynObjects();
+ GetMotionMaster()->Clear(false); // remove different non-standard movement generators.
+ }
+ RemoveFromWorld();
+}
+
+CharmInfo* Unit::InitCharmInfo(Unit *charm)
+{
+ if(!m_charmInfo)
+ m_charmInfo = new CharmInfo(charm);
+ return m_charmInfo;
+}
+
+CharmInfo::CharmInfo(Unit* unit)
+: m_unit(unit), m_CommandState(COMMAND_FOLLOW), m_ReactSate(REACT_PASSIVE), m_petnumber(0)
+{
+ for(int i =0; i<4; ++i)
+ {
+ m_charmspells[i].spellId = 0;
+ m_charmspells[i].active = ACT_DISABLED;
+ }
+}
+
+void CharmInfo::InitPetActionBar()
+{
+ // the first 3 SpellOrActions are attack, follow and stay
+ for(uint32 i = 0; i < 3; i++)
+ {
+ PetActionBar[i].Type = ACT_COMMAND;
+ PetActionBar[i].SpellOrAction = COMMAND_ATTACK - i;
+
+ PetActionBar[i + 7].Type = ACT_REACTION;
+ PetActionBar[i + 7].SpellOrAction = COMMAND_ATTACK - i;
+ }
+ for(uint32 i=0; i < 4; i++)
+ {
+ PetActionBar[i + 3].Type = ACT_DISABLED;
+ PetActionBar[i + 3].SpellOrAction = 0;
+ }
+}
+
+void CharmInfo::InitEmptyActionBar()
+{
+ for(uint32 x = 1; x < 10; ++x)
+ {
+ PetActionBar[x].Type = ACT_CAST;
+ PetActionBar[x].SpellOrAction = 0;
+ }
+ PetActionBar[0].Type = ACT_COMMAND;
+ PetActionBar[0].SpellOrAction = COMMAND_ATTACK;
+}
+
+void CharmInfo::InitPossessCreateSpells()
+{
+ if(m_unit->GetTypeId() == TYPEID_PLAYER)
+ return;
+
+ InitEmptyActionBar(); //charm action bar
+
+ for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x)
+ {
+ if (IsPassiveSpell(((Creature*)m_unit)->m_spells[x]))
+ m_unit->CastSpell(m_unit, ((Creature*)m_unit)->m_spells[x], true);
+ else
+ AddSpellToAB(0, ((Creature*)m_unit)->m_spells[x], ACT_CAST);
+ }
+}
+
+void CharmInfo::InitCharmCreateSpells()
+{
+ if(m_unit->GetTypeId() == TYPEID_PLAYER) //charmed players don't have spells
+ {
+ InitEmptyActionBar();
+ return;
+ }
+
+ InitPetActionBar();
+
+ for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x)
+ {
+ uint32 spellId = ((Creature*)m_unit)->m_spells[x];
+ m_charmspells[x].spellId = spellId;
+
+ if(!spellId)
+ continue;
+
+ if (IsPassiveSpell(spellId))
+ {
+ m_unit->CastSpell(m_unit, spellId, true);
+ m_charmspells[x].active = ACT_PASSIVE;
+ }
+ else
+ {
+ ActiveStates newstate;
+ bool onlyselfcast = true;
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
+
+ if(!spellInfo) onlyselfcast = false;
+ for(uint32 i = 0;i<3 && onlyselfcast;++i) //non existent spell will not make any problems as onlyselfcast would be false -> break right away
+ {
+ if(spellInfo->EffectImplicitTargetA[i] != TARGET_SELF && spellInfo->EffectImplicitTargetA[i] != 0)
+ onlyselfcast = false;
+ }
+
+ if(onlyselfcast || !IsPositiveSpell(spellId)) //only self cast and spells versus enemies are autocastable
+ newstate = ACT_DISABLED;
+ else
+ newstate = ACT_CAST;
+
+ AddSpellToAB(0, spellId, newstate);
+ }
+ }
+}
+
+bool CharmInfo::AddSpellToAB(uint32 oldid, uint32 newid, ActiveStates newstate)
+{
+ for(uint8 i = 0; i < 10; i++)
+ {
+ if((PetActionBar[i].Type == ACT_DISABLED || PetActionBar[i].Type == ACT_ENABLED || PetActionBar[i].Type == ACT_CAST) && PetActionBar[i].SpellOrAction == oldid)
+ {
+ PetActionBar[i].SpellOrAction = newid;
+ if(!oldid)
+ {
+ if(newstate == ACT_DECIDE)
+ PetActionBar[i].Type = ACT_DISABLED;
+ else
+ PetActionBar[i].Type = newstate;
+ }
+
+ return true;
+ }
+ }
+ return false;
+}
+
+void CharmInfo::ToggleCreatureAutocast(uint32 spellid, bool apply)
+{
+ if(IsPassiveSpell(spellid))
+ return;
+
+ for(uint32 x = 0; x < CREATURE_MAX_SPELLS; ++x)
+ {
+ if(spellid == m_charmspells[x].spellId)
+ {
+ m_charmspells[x].active = apply ? ACT_ENABLED : ACT_DISABLED;
+ }
+ }
+}
+
+void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow)
+{
+ m_petnumber = petnumber;
+ if(statwindow)
+ m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, m_petnumber);
+ else
+ m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, 0);
+}
+
+bool Unit::isFrozen() const
+{
+ AuraList const& mRoot = GetAurasByType(SPELL_AURA_MOD_ROOT);
+ for(AuraList::const_iterator i = mRoot.begin(); i != mRoot.end(); ++i)
+ if( GetSpellSchoolMask((*i)->GetSpellProto()) & SPELL_SCHOOL_MASK_FROST)
+ return true;
+ return false;
+}
+
+struct ProcTriggeredData
+{
+ ProcTriggeredData(SpellEntry const * _spellInfo, uint32 _spellParam, Aura* _triggeredByAura, uint32 _cooldown)
+ : spellInfo(_spellInfo), spellParam(_spellParam), triggeredByAura(_triggeredByAura),
+ triggeredByAura_SpellPair(Unit::spellEffectPair(triggeredByAura->GetId(),triggeredByAura->GetEffIndex())),
+ cooldown(_cooldown)
+ {}
+
+ SpellEntry const * spellInfo;
+ uint32 spellParam;
+ Aura* triggeredByAura;
+ Unit::spellEffectPair triggeredByAura_SpellPair;
+ uint32 cooldown;
+};
+
+typedef std::list< ProcTriggeredData > ProcTriggeredList;
+
+void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, AuraTypeSet const& procAuraTypes, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage, SpellSchoolMask damageSchoolMask )
+{
+ for(AuraTypeSet::const_iterator aur = procAuraTypes.begin(); aur != procAuraTypes.end(); ++aur)
+ {
+ // List of spells (effects) that proceed. Spell prototype and aura-specific value (damage for TRIGGER_DAMAGE)
+ ProcTriggeredList procTriggered;
+
+ AuraList const& auras = GetAurasByType(*aur);
+ for(AuraList::const_iterator i = auras.begin(), next; i != auras.end(); i = next)
+ {
+ next = i; ++next;
+
+ SpellEntry const *spellProto = (*i)->GetSpellProto();
+ if(!spellProto)
+ continue;
+
+ SpellProcEventEntry const *spellProcEvent = spellmgr.GetSpellProcEvent(spellProto->Id);
+ if(!spellProcEvent)
+ {
+ // used to prevent spam in log about same non-handled spells
+ static std::set<uint32> nonHandledSpellProcSet;
+
+ if(spellProto->procFlags != 0 && nonHandledSpellProcSet.find(spellProto->Id)==nonHandledSpellProcSet.end())
+ {
+ sLog.outError("ProcDamageAndSpell: spell %u (%s aura source) not have record in `spell_proc_event`)",spellProto->Id,(isVictim?"a victim's":"an attacker's"));
+ nonHandledSpellProcSet.insert(spellProto->Id);
+ }
+
+ // spell.dbc use totally different flags, that only can create problems if used.
+ continue;
+ }
+
+ // Check spellProcEvent data requirements
+ if(!SpellMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, procSpell,procFlag))
+ continue;
+
+ // Check if current equipment allows aura to proc
+ if(!isVictim && GetTypeId() == TYPEID_PLAYER )
+ {
+ if(spellProto->EquippedItemClass == ITEM_CLASS_WEAPON)
+ {
+ Item *item = ((Player*)this)->GetWeaponForAttack(attType,true);
+
+ if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
+ continue;
+ }
+ else if(spellProto->EquippedItemClass == ITEM_CLASS_ARMOR)
+ {
+ // Check if player is wearing shield
+ Item *item = ((Player*)this)->GetShield(true);
+ if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
+ continue;
+ }
+ }
+
+ float chance = (float)spellProto->procChance;
+
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance);
+
+ if(!isVictim && spellProcEvent->ppmRate != 0)
+ {
+ uint32 WeaponSpeed = GetAttackTime(attType);
+ chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate);
+ }
+
+ if(roll_chance_f(chance))
+ {
+ uint32 cooldown = spellProcEvent->cooldown;
+
+ uint32 i_spell_eff = (*i)->GetEffIndex();
+
+ int32 i_spell_param;
+ switch(*aur)
+ {
+ case SPELL_AURA_PROC_TRIGGER_SPELL:
+ i_spell_param = procFlag;
+ break;
+ case SPELL_AURA_DUMMY:
+ case SPELL_AURA_PRAYER_OF_MENDING:
+ case SPELL_AURA_MOD_HASTE:
+ i_spell_param = i_spell_eff;
+ break;
+ case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
+ i_spell_param = (*i)->GetModifier()->m_miscvalue;
+ break;
+ default:
+ i_spell_param = (*i)->GetModifier()->m_amount;
+ break;
+ }
+
+ procTriggered.push_back( ProcTriggeredData(spellProto,i_spell_param,*i, cooldown) );
+ }
+ }
+
+ // Handle effects proceed this time
+ for(ProcTriggeredList::iterator i = procTriggered.begin(); i != procTriggered.end(); ++i)
+ {
+ // Some auras can be deleted in function called in this loop (except first, ofc)
+ // Until storing auras in std::multimap to hard check deleting by another way
+ if(i != procTriggered.begin())
+ {
+ bool found = false;
+ AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair);
+ AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair);
+ for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr)
+ {
+ if(itr->second==i->triggeredByAura)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ {
+ sLog.outError("Spell aura %u (id:%u effect:%u) has been deleted before call spell proc event handler",*aur,i->triggeredByAura_SpellPair.first,i->triggeredByAura_SpellPair.second);
+ sLog.outError("It can be deleted one from early proccesed auras:");
+ for(ProcTriggeredList::iterator i2 = procTriggered.begin(); i != i2; ++i2)
+ sLog.outError(" Spell aura %u (id:%u effect:%u)",*aur,i2->triggeredByAura_SpellPair.first,i2->triggeredByAura_SpellPair.second);
+ sLog.outError(" <end of list>");
+ continue;
+ }
+ }
+
+ // save charges existence before processing to prevent crash at access to deleted triggered aura after
+ bool triggeredByAuraWithCharges = i->triggeredByAura->m_procCharges > 0;
+
+ bool casted = false;
+ switch(*aur)
+ {
+ case SPELL_AURA_PROC_TRIGGER_SPELL:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+ casted = HandleProcTriggerSpell(pTarget, damage, i->triggeredByAura, procSpell,i->spellParam,attType,i->cooldown);
+ break;
+ }
+ case SPELL_AURA_PROC_TRIGGER_DAMAGE:
+ {
+ sLog.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", i->spellParam, i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+ uint32 damage = i->spellParam;
+ SpellNonMeleeDamageLog(pTarget, i->spellInfo->Id, damage, true, true);
+ casted = true;
+ break;
+ }
+ case SPELL_AURA_DUMMY:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+ casted = HandleDummyAuraProc(pTarget, i->spellInfo, i->spellParam, damage, i->triggeredByAura, procSpell, procFlag,i->cooldown);
+ break;
+ }
+ case SPELL_AURA_PRAYER_OF_MENDING:
+ {
+ sLog.outDebug("ProcDamageAndSpell(mending): casting spell id %u (triggered by %s dummy aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+
+ // aura can be deleted at casts
+ int32 heal = i->triggeredByAura->GetModifier()->m_amount;
+ uint64 caster_guid = i->triggeredByAura->GetCasterGUID();
+
+ // jumps
+ int32 jumps = i->triggeredByAura->m_procCharges-1;
+
+ // current aura expire
+ i->triggeredByAura->m_procCharges = 1; // will removed at next charges decrease
+
+ // next target selection
+ if(jumps > 0 && GetTypeId()==TYPEID_PLAYER && IS_PLAYER_GUID(caster_guid))
+ {
+ Aura* aura = i->triggeredByAura;
+
+ SpellEntry const* spellProto = aura->GetSpellProto();
+ uint32 effIdx = aura->GetEffIndex();
+
+ float radius;
+ if (spellProto->EffectRadiusIndex[effIdx])
+ radius = GetSpellRadius(sSpellRadiusStore.LookupEntry(spellProto->EffectRadiusIndex[effIdx]));
+ else
+ radius = GetSpellMaxRange(sSpellRangeStore.LookupEntry(spellProto->rangeIndex));
+
+ if(Player* caster = ((Player*)aura->GetCaster()))
+ {
+ caster->ApplySpellMod(spellProto->Id, SPELLMOD_RADIUS, radius,NULL);
+
+ if(Player* target = ((Player*)this)->GetNextRandomRaidMember(radius))
+ {
+ // aura will applied from caster, but spell casted from current aura holder
+ SpellModifier *mod = new SpellModifier;
+ mod->op = SPELLMOD_CHARGES;
+ mod->value = jumps-5; // negative
+ mod->type = SPELLMOD_FLAT;
+ mod->spellId = spellProto->Id;
+ mod->effectId = effIdx;
+ mod->lastAffected = NULL;
+ mod->mask = spellProto->SpellFamilyFlags;
+ mod->charges = 0;
+
+ caster->AddSpellMod(mod, true);
+ CastCustomSpell(target,spellProto->Id,&heal,NULL,NULL,true,NULL,aura,caster->GetGUID());
+ caster->AddSpellMod(mod, false);
+ }
+ }
+ }
+
+ // heal
+ CastCustomSpell(this,33110,&heal,NULL,NULL,true,NULL,NULL,caster_guid);
+ casted = true;
+ break;
+ }
+ case SPELL_AURA_MOD_HASTE:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s haste aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+ casted = HandleHasteAuraProc(pTarget, i->spellInfo, i->spellParam, damage, i->triggeredByAura, procSpell, procFlag,i->cooldown);
+ break;
+ }
+ case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN:
+ {
+ // nothing do, just charges counter
+ // but count only in case appropriate school damage
+ casted = i->triggeredByAura->GetModifier()->m_miscvalue & damageSchoolMask;
+ break;
+ }
+ case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", i->spellInfo->Id,(isVictim?"a victim's":"an attacker's"),i->triggeredByAura->GetId());
+ casted = HandleOverrideClassScriptAuraProc(pTarget, i->spellParam, damage, i->triggeredByAura, procSpell,i->cooldown);
+ break;
+ }
+ default:
+ {
+ // nothing do, just charges counter
+ casted = true;
+ break;
+ }
+ }
+
+ // Update charge (aura can be removed by triggers)
+ if(casted && triggeredByAuraWithCharges)
+ {
+ // need found aura (can be dropped by triggers)
+ AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair);
+ AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair);
+ for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr)
+ {
+ if(itr->second == i->triggeredByAura)
+ {
+ if(i->triggeredByAura->m_procCharges > 0)
+ i->triggeredByAura->m_procCharges -= 1;
+
+ i->triggeredByAura->UpdateAuraCharges();
+ break;
+ }
+ }
+ }
+ }
+
+ // Safely remove auras with zero charges
+ for(AuraList::const_iterator i = auras.begin(), next; i != auras.end(); i = next)
+ {
+ next = i; ++next;
+ if((*i)->m_procCharges == 0)
+ {
+ RemoveAurasDueToSpell((*i)->GetId());
+ next = auras.begin();
+ }
+ }
+ }
+}
+
+SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const
+{
+ return SPELL_SCHOOL_MASK_NORMAL;
+}
+
+Player* Unit::GetSpellModOwner()
+{
+ if(GetTypeId()==TYPEID_PLAYER)
+ return (Player*)this;
+ if(((Creature*)this)->isPet() || ((Creature*)this)->isTotem())
+ {
+ Unit* owner = GetOwner();
+ if(owner && owner->GetTypeId()==TYPEID_PLAYER)
+ return (Player*)owner;
+ }
+ return NULL;
+}
+
+///----------Pet responses methods-----------------
+void Unit::SendPetCastFail(uint32 spellid, uint8 msg)
+{
+ Unit *owner = GetCharmerOrOwner();
+ if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_PET_CAST_FAILED, (4+1));
+ data << uint32(spellid);
+ data << uint8(msg);
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+}
+
+void Unit::SendPetActionFeedback (uint8 msg)
+{
+ Unit* owner = GetOwner();
+ if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_PET_ACTION_FEEDBACK, 1);
+ data << uint8(msg);
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+}
+
+void Unit::SendPetTalk (uint32 pettalk)
+{
+ Unit* owner = GetOwner();
+ if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_PET_ACTION_SOUND, 8+4);
+ data << uint64(GetGUID());
+ data << uint32(pettalk);
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+}
+
+void Unit::SendPetSpellCooldown (uint32 spellid, time_t cooltime)
+{
+ Unit* owner = GetOwner();
+ if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+4+4);
+ data << uint64(GetGUID());
+ data << uint8(0x0);
+ data << uint32(spellid);
+ data << uint32(cooltime);
+
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+}
+
+void Unit::SendPetClearCooldown (uint32 spellid)
+{
+ Unit* owner = GetOwner();
+ if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8));
+ data << uint32(spellid);
+ data << uint64(GetGUID());
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+}
+
+void Unit::SendPetAIReaction(uint64 guid)
+{
+ Unit* owner = GetOwner();
+ if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ WorldPacket data(SMSG_AI_REACTION, 12);
+ data << uint64(guid) << uint32(00000002);
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+}
+
+///----------End of Pet responses methods----------
+
+void Unit::StopMoving()
+{
+ clearUnitState(UNIT_STAT_MOVING);
+
+ // send explicit stop packet
+ // rely on vmaps here because for exemple stormwind is in air
+ float z = MapManager::Instance().GetBaseMap(GetMapId())->GetHeight(GetPositionX(), GetPositionY(), GetPositionZ(), true);
+ //if (fabs(GetPositionZ() - z) < 2.0f)
+ // Relocate(GetPositionX(), GetPositionY(), z);
+ Relocate(GetPositionX(), GetPositionY(),GetPositionZ());
+
+ SendMonsterMove(GetPositionX(), GetPositionY(), GetPositionZ(), 0, true, 0);
+
+ // update position and orientation;
+ WorldPacket data;
+ BuildHeartBeatMsg(&data);
+ SendMessageToSet(&data,false);
+}
+
+void Unit::SetFeared(bool apply, uint64 casterGUID, uint32 spellID)
+{
+ if( apply )
+ {
+ if(HasAuraType(SPELL_AURA_PREVENTS_FLEEING))
+ return;
+
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+
+ GetMotionMaster()->MovementExpired(false);
+ CastStop(GetGUID()==casterGUID ? spellID : 0);
+
+ Unit* caster = ObjectAccessor::GetUnit(*this,casterGUID);
+
+ GetMotionMaster()->MoveFleeing(caster); // caster==NULL processed in MoveFleeing
+ }
+ else
+ {
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+
+ GetMotionMaster()->MovementExpired(false);
+
+ if( GetTypeId() != TYPEID_PLAYER && isAlive() )
+ {
+ // restore appropriate movement generator
+ if(getVictim())
+ GetMotionMaster()->MoveChase(getVictim());
+ else
+ GetMotionMaster()->Initialize();
+
+ // attack caster if can
+ Unit* caster = ObjectAccessor::GetObjectInWorld(casterGUID, (Unit*)NULL);
+ if(caster && caster != getVictim() && ((Creature*)this)->AI())
+ ((Creature*)this)->AI()->AttackStart(caster);
+ }
+ }
+
+ if (GetTypeId() == TYPEID_PLAYER)
+ ((Player*)this)->SetClientControl(this, !apply);
+}
+
+void Unit::SetConfused(bool apply, uint64 casterGUID, uint32 spellID)
+{
+ if( apply )
+ {
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+
+ CastStop(GetGUID()==casterGUID ? spellID : 0);
+
+ GetMotionMaster()->MoveConfused();
+ }
+ else
+ {
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+
+ GetMotionMaster()->MovementExpired(false);
+
+ if (GetTypeId() == TYPEID_UNIT)
+ {
+ // if in combat restore movement generator
+ if(getVictim())
+ GetMotionMaster()->MoveChase(getVictim());
+ }
+ }
+
+ if(GetTypeId() == TYPEID_PLAYER)
+ ((Player*)this)->SetClientControl(this, !apply);
+}
+
+bool Unit::IsSitState() const
+{
+ uint8 s = getStandState();
+ return s == PLAYER_STATE_SIT_CHAIR || s == PLAYER_STATE_SIT_LOW_CHAIR ||
+ s == PLAYER_STATE_SIT_MEDIUM_CHAIR || s == PLAYER_STATE_SIT_HIGH_CHAIR ||
+ s == PLAYER_STATE_SIT;
+}
+
+bool Unit::IsStandState() const
+{
+ uint8 s = getStandState();
+ return !IsSitState() && s != PLAYER_STATE_SLEEP && s != PLAYER_STATE_KNEEL;
+}
+
+void Unit::SetStandState(uint8 state)
+{
+ SetByteValue(UNIT_FIELD_BYTES_1, 0, state);
+
+ if (IsStandState())
+ RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_NOT_SEATED);
+
+ if(GetTypeId()==TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_STANDSTATE_UPDATE, 1);
+ data << (uint8)state;
+ ((Player*)this)->GetSession()->SendPacket(&data);
+ }
+}
+
+bool Unit::IsPolymorphed() const
+{
+ return GetSpellSpecific(getTransForm())==SPELL_MAGE_POLYMORPH;
+}
+
+void Unit::SetDisplayId(uint32 modelId)
+{
+ SetUInt32Value(UNIT_FIELD_DISPLAYID, modelId);
+
+ if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(!pet->isControlled())
+ return;
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MODEL_ID);
+ }
+}
+
+void Unit::ClearComboPointHolders()
+{
+ while(!m_ComboPointHolders.empty())
+ {
+ uint32 lowguid = *m_ComboPointHolders.begin();
+
+ Player* plr = objmgr.GetPlayer(MAKE_NEW_GUID(lowguid, 0, HIGHGUID_PLAYER));
+ if(plr && plr->GetComboTarget()==GetGUID()) // recheck for safe
+ plr->ClearComboPoints(); // remove also guid from m_ComboPointHolders;
+ else
+ m_ComboPointHolders.erase(lowguid); // or remove manually
+ }
+}
+
+void Unit::ClearAllReactives()
+{
+
+ for(int i=0; i < MAX_REACTIVE; ++i)
+ m_reactiveTimer[i] = 0;
+
+ if (HasAuraState( AURA_STATE_DEFENSE))
+ ModifyAuraState(AURA_STATE_DEFENSE, false);
+ if (getClass() == CLASS_HUNTER && HasAuraState( AURA_STATE_HUNTER_PARRY))
+ ModifyAuraState(AURA_STATE_HUNTER_PARRY, false);
+ if (HasAuraState( AURA_STATE_CRIT))
+ ModifyAuraState(AURA_STATE_CRIT, false);
+ if (getClass() == CLASS_HUNTER && HasAuraState( AURA_STATE_HUNTER_CRIT_STRIKE) )
+ ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, false);
+
+ if(getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER)
+ ((Player*)this)->ClearComboPoints();
+}
+
+void Unit::UpdateReactives( uint32 p_time )
+{
+ for(int i = 0; i < MAX_REACTIVE; ++i)
+ {
+ ReactiveType reactive = ReactiveType(i);
+
+ if(!m_reactiveTimer[reactive])
+ continue;
+
+ if ( m_reactiveTimer[reactive] <= p_time)
+ {
+ m_reactiveTimer[reactive] = 0;
+
+ switch ( reactive )
+ {
+ case REACTIVE_DEFENSE:
+ if (HasAuraState(AURA_STATE_DEFENSE))
+ ModifyAuraState(AURA_STATE_DEFENSE, false);
+ break;
+ case REACTIVE_HUNTER_PARRY:
+ if ( getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_PARRY))
+ ModifyAuraState(AURA_STATE_HUNTER_PARRY, false);
+ break;
+ case REACTIVE_CRIT:
+ if (HasAuraState(AURA_STATE_CRIT))
+ ModifyAuraState(AURA_STATE_CRIT, false);
+ break;
+ case REACTIVE_HUNTER_CRIT:
+ if ( getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_CRIT_STRIKE) )
+ ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, false);
+ break;
+ case REACTIVE_OVERPOWER:
+ if(getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER)
+ ((Player*)this)->ClearComboPoints();
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ m_reactiveTimer[reactive] -= p_time;
+ }
+ }
+}
+
+Unit* Unit::SelectNearbyTarget() const
+{
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate();
+
+ std::list<Unit *> targets;
+
+ {
+ MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, this, ATTACK_DISTANCE);
+ MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck> searcher(targets, u_check);
+
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
+ TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, world_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
+ cell_lock->Visit(cell_lock, grid_unit_searcher, *MapManager::Instance().GetMap(GetMapId(), this));
+ }
+
+ // remove current target
+ if(getVictim())
+ targets.remove(getVictim());
+
+ // remove not LoS targets
+ for(std::list<Unit *>::iterator tIter = targets.begin(); tIter != targets.end();)
+ {
+ if(!IsWithinLOSInMap(*tIter))
+ {
+ std::list<Unit *>::iterator tIter2 = tIter;
+ ++tIter;
+ targets.erase(tIter2);
+ }
+ else
+ ++tIter;
+ }
+
+ // no appropriate targets
+ if(targets.empty())
+ return NULL;
+
+ // select random
+ uint32 rIdx = urand(0,targets.size()-1);
+ std::list<Unit *>::const_iterator tcIter = targets.begin();
+ for(uint32 i = 0; i < rIdx; ++i)
+ ++tcIter;
+
+ return *tcIter;
+}
+
+void Unit::ApplyAttackTimePercentMod( WeaponAttackType att,float val, bool apply )
+{
+ if(val > 0)
+ {
+ ApplyPercentModFloatVar(m_modAttackSpeedPct[att], val, !apply);
+ ApplyPercentModFloatValue(UNIT_FIELD_BASEATTACKTIME+att,val,!apply);
+ }
+ else
+ {
+ ApplyPercentModFloatVar(m_modAttackSpeedPct[att], -val, apply);
+ ApplyPercentModFloatValue(UNIT_FIELD_BASEATTACKTIME+att,-val,apply);
+ }
+}
+
+void Unit::ApplyCastTimePercentMod(float val, bool apply )
+{
+ if(val > 0)
+ ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED,val,!apply);
+ else
+ ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED,-val,apply);
+}
+
+uint32 Unit::GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectType damagetype, uint32 CastingTime )
+{
+ if (CastingTime > 7000) CastingTime = 7000;
+ if (CastingTime < 1500) CastingTime = 1500;
+
+ if(damagetype == DOT && !IsChanneledSpell(spellProto))
+ CastingTime = 3500;
+
+ int32 overTime = 0;
+ uint8 effects = 0;
+ bool DirectDamage = false;
+ bool AreaEffect = false;
+
+ for ( uint32 i=0; i<3;i++)
+ {
+ switch ( spellProto->Effect[i] )
+ {
+ case SPELL_EFFECT_SCHOOL_DAMAGE:
+ case SPELL_EFFECT_POWER_DRAIN:
+ case SPELL_EFFECT_HEALTH_LEECH:
+ case SPELL_EFFECT_ENVIRONMENTAL_DAMAGE:
+ case SPELL_EFFECT_POWER_BURN:
+ case SPELL_EFFECT_HEAL:
+ DirectDamage = true;
+ break;
+ case SPELL_EFFECT_APPLY_AURA:
+ switch ( spellProto->EffectApplyAuraName[i] )
+ {
+ case SPELL_AURA_PERIODIC_DAMAGE:
+ case SPELL_AURA_PERIODIC_HEAL:
+ case SPELL_AURA_PERIODIC_LEECH:
+ if ( GetSpellDuration(spellProto) )
+ overTime = GetSpellDuration(spellProto);
+ break;
+ default:
+ // -5% per additional effect
+ ++effects;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if(IsAreaEffectTarget(Targets(spellProto->EffectImplicitTargetA[i])) || IsAreaEffectTarget(Targets(spellProto->EffectImplicitTargetB[i])))
+ AreaEffect = true;
+ }
+
+ // Combined Spells with Both Over Time and Direct Damage
+ if ( overTime > 0 && CastingTime > 0 && DirectDamage )
+ {
+ // mainly for DoTs which are 3500 here otherwise
+ uint32 OriginalCastTime = GetSpellCastTime(spellProto);
+ if (OriginalCastTime > 7000) OriginalCastTime = 7000;
+ if (OriginalCastTime < 1500) OriginalCastTime = 1500;
+ // Portion to Over Time
+ float PtOT = (overTime / 15000.f) / ((overTime / 15000.f) + (OriginalCastTime / 3500.f));
+
+ if ( damagetype == DOT )
+ CastingTime = uint32(CastingTime * PtOT);
+ else if ( PtOT < 1.0f )
+ CastingTime = uint32(CastingTime * (1 - PtOT));
+ else
+ CastingTime = 0;
+ }
+
+ // Area Effect Spells receive only half of bonus
+ if ( AreaEffect )
+ CastingTime /= 2;
+
+ // -5% of total per any additional effect
+ for ( uint8 i=0; i<effects; ++i)
+ {
+ if ( CastingTime > 175 )
+ {
+ CastingTime -= 175;
+ }
+ else
+ {
+ CastingTime = 0;
+ break;
+ }
+ }
+
+ return CastingTime;
+}
+
+void Unit::UpdateAuraForGroup(uint8 slot)
+{
+ if(GetTypeId() == TYPEID_PLAYER)
+ {
+ Player* player = (Player*)this;
+ if(player->GetGroup())
+ {
+ player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_AURAS);
+ player->SetAuraUpdateMask(slot);
+ }
+ }
+ else if(GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet())
+ {
+ Pet *pet = ((Pet*)this);
+ if(pet->isControlled())
+ {
+ Unit *owner = GetOwner();
+ if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup())
+ {
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_AURAS);
+ pet->SetAuraUpdateMask(slot);
+ }
+ }
+ }
+}
+
+float Unit::GetAPMultiplier(WeaponAttackType attType, bool normalized)
+{
+ if (!normalized || GetTypeId() != TYPEID_PLAYER)
+ return float(GetAttackTime(attType))/1000.0f;
+
+ Item *Weapon = ((Player*)this)->GetWeaponForAttack(attType);
+ if (!Weapon)
+ return 2.4; // fist attack
+
+ switch (Weapon->GetProto()->InventoryType)
+ {
+ case INVTYPE_2HWEAPON:
+ return 3.3;
+ case INVTYPE_RANGED:
+ case INVTYPE_RANGEDRIGHT:
+ case INVTYPE_THROWN:
+ return 2.8;
+ case INVTYPE_WEAPON:
+ case INVTYPE_WEAPONMAINHAND:
+ case INVTYPE_WEAPONOFFHAND:
+ default:
+ return Weapon->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_DAGGER ? 1.7 : 2.4;
+ }
+}
+
+Aura* Unit::GetDummyAura( uint32 spell_id ) const
+{
+ Unit::AuraList const& mDummy = GetAurasByType(SPELL_AURA_DUMMY);
+ for(Unit::AuraList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr)
+ if ((*itr)->GetId() == spell_id)
+ return *itr;
+
+ return NULL;
+}
+
+bool Unit::IsUnderLastManaUseEffect() const
+{
+ return getMSTimeDiff(m_lastManaUse,getMSTime()) < 5000;
+}
+
+void Unit::SetContestedPvP(Player *attackedPlayer)
+{
+ Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
+
+ if(!player || attackedPlayer && (attackedPlayer == player || player->duel && player->duel->opponent == attackedPlayer))
+ return;
+
+ player->SetContestedPvPTimer(30000);
+ if(!player->hasUnitState(UNIT_STAT_ATTACK_PLAYER))
+ {
+ player->addUnitState(UNIT_STAT_ATTACK_PLAYER);
+ player->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
+ // call MoveInLineOfSight for nearby contested guards
+ SetVisibility(GetVisibility());
+ }
+ if(!hasUnitState(UNIT_STAT_ATTACK_PLAYER))
+ {
+ addUnitState(UNIT_STAT_ATTACK_PLAYER);
+ // call MoveInLineOfSight for nearby contested guards
+ SetVisibility(GetVisibility());
+ }
+}
+
+void Unit::AddPetAura(PetAura const* petSpell)
+{
+ m_petAuras.insert(petSpell);
+ if(Pet* pet = GetPet())
+ pet->CastPetAura(petSpell);
+}
+
+void Unit::RemovePetAura(PetAura const* petSpell)
+{
+ m_petAuras.erase(petSpell);
+ if(Pet* pet = GetPet())
+ pet->RemoveAurasDueToSpell(petSpell->GetAura(pet->GetEntry()));
+}
diff --git a/src/game/Unit.h b/src/game/Unit.h
index 5641f5a64df..47b8da7f9ba 100644
--- a/src/game/Unit.h
+++ b/src/game/Unit.h
@@ -1,1337 +1,1337 @@
-/*
- * 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
- */
-
-#ifndef __UNIT_H
-#define __UNIT_H
-
-#include "Common.h"
-#include "Object.h"
-#include "Opcodes.h"
-#include "Mthread.h"
-#include "SpellAuraDefines.h"
-#include "UpdateFields.h"
-#include "SharedDefines.h"
-#include "ThreatManager.h"
-#include "HostilRefManager.h"
-#include "FollowerReference.h"
-#include "FollowerRefManager.h"
-#include "Utilities/EventProcessor.h"
-#include "MotionMaster.h"
-#include "Database/DBCStructure.h"
-#include <list>
-
-enum SpellInterruptFlags
-{
- SPELL_INTERRUPT_FLAG_MOVEMENT = 0x01,
- SPELL_INTERRUPT_FLAG_DAMAGE = 0x02,
- SPELL_INTERRUPT_FLAG_INTERRUPT = 0x04,
- SPELL_INTERRUPT_FLAG_AUTOATTACK = 0x08,
- //SPELL_INTERRUPT_FLAG_TURNING = 0x10 // not turning - maybe _complete_ interrupt on direct damage?
-};
-
-enum SpellChannelInterruptFlags
-{
- CHANNEL_FLAG_DAMAGE = 0x0002,
- CHANNEL_FLAG_MOVEMENT = 0x0008,
- CHANNEL_FLAG_TURNING = 0x0010,
- CHANNEL_FLAG_DAMAGE2 = 0x0080,
- CHANNEL_FLAG_DELAY = 0x4000
-};
-
-enum SpellAuraInterruptFlags
-{
- AURA_INTERRUPT_FLAG_UNK0 = 0x00000001, // 0 removed when getting hit by a negative spell?
- AURA_INTERRUPT_FLAG_DAMAGE = 0x00000002, // 1 removed by any damage
- AURA_INTERRUPT_FLAG_UNK2 = 0x00000004, // 2
- AURA_INTERRUPT_FLAG_MOVE = 0x00000008, // 3 removed by any movement
- AURA_INTERRUPT_FLAG_TURNING = 0x00000010, // 4 removed by any turning
- AURA_INTERRUPT_FLAG_ENTER_COMBAT = 0x00000020, // 5 removed by entering combat
- AURA_INTERRUPT_FLAG_NOT_MOUNTED = 0x00000040, // 6 removed by unmounting
- AURA_INTERRUPT_FLAG_NOT_ABOVEWATER = 0x00000080, // 7 removed by entering water
- AURA_INTERRUPT_FLAG_NOT_UNDERWATER = 0x00000100, // 8 removed by leaving water
- AURA_INTERRUPT_FLAG_NOT_SHEATHED = 0x00000200, // 9 removed by unsheathing
- AURA_INTERRUPT_FLAG_UNK10 = 0x00000400, // 10
- AURA_INTERRUPT_FLAG_UNK11 = 0x00000800, // 11
- AURA_INTERRUPT_FLAG_UNK12 = 0x00001000, // 12 removed by attack?
- AURA_INTERRUPT_FLAG_UNK13 = 0x00002000, // 13
- AURA_INTERRUPT_FLAG_UNK14 = 0x00004000, // 14
- AURA_INTERRUPT_FLAG_UNK15 = 0x00008000, // 15 removed by casting a spell?
- AURA_INTERRUPT_FLAG_UNK16 = 0x00010000, // 16
- AURA_INTERRUPT_FLAG_MOUNTING = 0x00020000, // 17 removed by mounting
- AURA_INTERRUPT_FLAG_NOT_SEATED = 0x00040000, // 18 removed by standing up
- AURA_INTERRUPT_FLAG_CHANGE_MAP = 0x00080000, // 19 leaving map/getting teleported
- AURA_INTERRUPT_FLAG_UNK20 = 0x00100000, // 20
- AURA_INTERRUPT_FLAG_UNK21 = 0x00200000, // 21
- AURA_INTERRUPT_FLAG_UNK22 = 0x00400000, // 22
- AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT = 0x00800000, // 23 removed by entering pvp combat
- AURA_INTERRUPT_FLAG_DIRECT_DAMAGE = 0x01000000 // 24 removed by any direct damage
-};
-
-enum SpellModOp
-{
- SPELLMOD_DAMAGE = 0,
- SPELLMOD_DURATION = 1,
- SPELLMOD_THREAT = 2,
- SPELLMOD_EFFECT1 = 3,
- SPELLMOD_CHARGES = 4,
- SPELLMOD_RANGE = 5,
- SPELLMOD_RADIUS = 6,
- SPELLMOD_CRITICAL_CHANCE = 7,
- SPELLMOD_ALL_EFFECTS = 8,
- SPELLMOD_NOT_LOSE_CASTING_TIME = 9,
- SPELLMOD_CASTING_TIME = 10,
- SPELLMOD_COOLDOWN = 11,
- SPELLMOD_EFFECT2 = 12,
- // spellmod 13 unused
- SPELLMOD_COST = 14,
- SPELLMOD_CRIT_DAMAGE_BONUS = 15,
- SPELLMOD_RESIST_MISS_CHANCE = 16,
- SPELLMOD_JUMP_TARGETS = 17,
- SPELLMOD_CHANCE_OF_SUCCESS = 18,
- SPELLMOD_ACTIVATION_TIME = 19,
- SPELLMOD_EFFECT_PAST_FIRST = 20,
- SPELLMOD_CASTING_TIME_OLD = 21,
- SPELLMOD_DOT = 22,
- SPELLMOD_EFFECT3 = 23,
- SPELLMOD_SPELL_BONUS_DAMAGE = 24,
- // spellmod 25, 26 unused
- SPELLMOD_MULTIPLE_VALUE = 27,
- SPELLMOD_RESIST_DISPEL_CHANCE = 28
-};
-
-#define MAX_SPELLMOD 32
-
-enum SpellFacingFlags
-{
- SPELL_FACING_FLAG_INFRONT = 0x0001
-};
-
-#define BASE_MINDAMAGE 1.0f
-#define BASE_MAXDAMAGE 2.0f
-#define BASE_ATTACK_TIME 2000
-
-// high byte (3 from 0..3) of UNIT_FIELD_BYTES_2
-enum ShapeshiftForm
-{
- FORM_NONE = 0x00,
- FORM_CAT = 0x01,
- FORM_TREE = 0x02,
- FORM_TRAVEL = 0x03,
- FORM_AQUA = 0x04,
- FORM_BEAR = 0x05,
- FORM_AMBIENT = 0x06,
- FORM_GHOUL = 0x07,
- FORM_DIREBEAR = 0x08,
- FORM_CREATUREBEAR = 0x0E,
- FORM_CREATURECAT = 0x0F,
- FORM_GHOSTWOLF = 0x10,
- FORM_BATTLESTANCE = 0x11,
- FORM_DEFENSIVESTANCE = 0x12,
- FORM_BERSERKERSTANCE = 0x13,
- FORM_TEST = 0x14,
- FORM_ZOMBIE = 0x15,
- FORM_FLIGHT_EPIC = 0x1B,
- FORM_SHADOW = 0x1C,
- FORM_FLIGHT = 0x1D,
- FORM_STEALTH = 0x1E,
- FORM_MOONKIN = 0x1F,
- FORM_SPIRITOFREDEMPTION = 0x20
-};
-
-// low byte ( 0 from 0..3 ) of UNIT_FIELD_BYTES_2
-enum SheathState
-{
- SHEATH_STATE_UNARMED = 0, // non prepared weapon
- SHEATH_STATE_MELEE = 1, // prepared melee weapon
- SHEATH_STATE_RANGED = 2 // prepared ranged weapon
-};
-
-// byte (1 from 0..3) of UNIT_FIELD_BYTES_2
-enum UnitBytes2_Flags
-{
- UNIT_BYTE2_FLAG_UNK0 = 0x01,
- UNIT_BYTE2_FLAG_UNK1 = 0x02,
- UNIT_BYTE2_FLAG_UNK2 = 0x04,
- UNIT_BYTE2_FLAG_UNK3 = 0x08,
- UNIT_BYTE2_FLAG_AURAS = 0x10, // show possitive auras as positive, and allow its dispel
- UNIT_BYTE2_FLAG_UNK5 = 0x20,
- UNIT_BYTE2_FLAG_UNK6 = 0x40,
- UNIT_BYTE2_FLAG_UNK7 = 0x80
-};
-
-// byte (2 from 0..3) of UNIT_FIELD_BYTES_2
-enum UnitRename
-{
- UNIT_RENAME_NOT_ALLOWED = 0x02,
- UNIT_RENAME_ALLOWED = 0x03
-};
-
-#define CREATURE_MAX_SPELLS 4
-
-enum Swing
-{
- NOSWING = 0,
- SINGLEHANDEDSWING = 1,
- TWOHANDEDSWING = 2
-};
-
-enum VictimState
-{
- VICTIMSTATE_UNKNOWN1 = 0,
- VICTIMSTATE_NORMAL = 1,
- VICTIMSTATE_DODGE = 2,
- VICTIMSTATE_PARRY = 3,
- VICTIMSTATE_INTERRUPT = 4,
- VICTIMSTATE_BLOCKS = 5,
- VICTIMSTATE_EVADES = 6,
- VICTIMSTATE_IS_IMMUNE = 7,
- VICTIMSTATE_DEFLECTS = 8
-};
-
-enum HitInfo
-{
- HITINFO_NORMALSWING = 0x00000000,
- HITINFO_UNK1 = 0x00000001, // req correct packet structure
- HITINFO_NORMALSWING2 = 0x00000002,
- HITINFO_LEFTSWING = 0x00000004,
- HITINFO_MISS = 0x00000010,
- HITINFO_ABSORB = 0x00000020, // plays absorb sound
- HITINFO_RESIST = 0x00000040, // resisted atleast some damage
- HITINFO_CRITICALHIT = 0x00000080,
- HITINFO_GLANCING = 0x00004000,
- HITINFO_CRUSHING = 0x00008000,
- HITINFO_NOACTION = 0x00010000,
- HITINFO_SWINGNOHITSOUND = 0x00080000
-};
-
-//i would like to remove this: (it is defined in item.h
-enum InventorySlot
-{
- NULL_BAG = 0,
- NULL_SLOT = 255
-};
-
-struct FactionTemplateEntry;
-struct Modifier;
-struct SpellEntry;
-struct SpellEntryExt;
-
-class Aura;
-class Creature;
-class Spell;
-class DynamicObject;
-class GameObject;
-class Item;
-class Pet;
-class Path;
-class PetAura;
-
-struct SpellImmune
-{
- uint32 type;
- uint32 spellId;
-};
-
-typedef std::list<SpellImmune> SpellImmuneList;
-
-enum UnitModifierType
-{
- BASE_VALUE = 0,
- BASE_PCT = 1,
- TOTAL_VALUE = 2,
- TOTAL_PCT = 3,
- MODIFIER_TYPE_END = 4
-};
-
-enum WeaponDamageRange
-{
- MINDAMAGE,
- MAXDAMAGE
-};
-
-enum DamageTypeToSchool
-{
- RESISTANCE,
- DAMAGE_DEALT,
- DAMAGE_TAKEN
-};
-
-enum AuraRemoveMode
-{
- AURA_REMOVE_BY_DEFAULT,
- AURA_REMOVE_BY_STACK, // at replace by semillar aura
- AURA_REMOVE_BY_CANCEL,
- AURA_REMOVE_BY_DISPEL,
- AURA_REMOVE_BY_DEATH
-};
-
-enum UnitMods
-{
- UNIT_MOD_STAT_STRENGTH, // UNIT_MOD_STAT_STRENGTH..UNIT_MOD_STAT_SPIRIT must be in existed order, it's accessed by index values of Stats enum.
- UNIT_MOD_STAT_AGILITY,
- UNIT_MOD_STAT_STAMINA,
- UNIT_MOD_STAT_INTELLECT,
- UNIT_MOD_STAT_SPIRIT,
- UNIT_MOD_HEALTH,
- UNIT_MOD_MANA, // UNIT_MOD_MANA..UNIT_MOD_HAPPINESS must be in existed order, it's accessed by index values of Powers enum.
- UNIT_MOD_RAGE,
- UNIT_MOD_FOCUS,
- UNIT_MOD_ENERGY,
- UNIT_MOD_HAPPINESS,
- UNIT_MOD_ARMOR, // UNIT_MOD_ARMOR..UNIT_MOD_RESISTANCE_ARCANE must be in existed order, it's accessed by index values of SpellSchools enum.
- UNIT_MOD_RESISTANCE_HOLY,
- UNIT_MOD_RESISTANCE_FIRE,
- UNIT_MOD_RESISTANCE_NATURE,
- UNIT_MOD_RESISTANCE_FROST,
- UNIT_MOD_RESISTANCE_SHADOW,
- UNIT_MOD_RESISTANCE_ARCANE,
- UNIT_MOD_ATTACK_POWER,
- UNIT_MOD_ATTACK_POWER_RANGED,
- UNIT_MOD_DAMAGE_MAINHAND,
- UNIT_MOD_DAMAGE_OFFHAND,
- UNIT_MOD_DAMAGE_RANGED,
- UNIT_MOD_END,
- // synonyms
- UNIT_MOD_STAT_START = UNIT_MOD_STAT_STRENGTH,
- UNIT_MOD_STAT_END = UNIT_MOD_STAT_SPIRIT + 1,
- UNIT_MOD_RESISTANCE_START = UNIT_MOD_ARMOR,
- UNIT_MOD_RESISTANCE_END = UNIT_MOD_RESISTANCE_ARCANE + 1,
- UNIT_MOD_POWER_START = UNIT_MOD_MANA,
- UNIT_MOD_POWER_END = UNIT_MOD_HAPPINESS + 1
-};
-
-enum BaseModGroup
-{
- CRIT_PERCENTAGE,
- RANGED_CRIT_PERCENTAGE,
- OFFHAND_CRIT_PERCENTAGE,
- SHIELD_BLOCK_VALUE,
- BASEMOD_END
-};
-
-enum BaseModType
-{
- FLAT_MOD,
- PCT_MOD
-};
-
-#define MOD_END (PCT_MOD+1)
-
-enum DeathState
-{
- ALIVE = 0,
- JUST_DIED = 1,
- CORPSE = 2,
- DEAD = 3,
- JUST_ALIVED = 4
-};
-
-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_STUNNED = 0x0008,
- UNIT_STAT_ROAMING = 0x0010,
- UNIT_STAT_CHASE = 0x0020,
- UNIT_STAT_SEARCHING = 0x0040,
- UNIT_STAT_FLEEING = 0x0080,
- UNIT_STAT_MOVING = (UNIT_STAT_ROAMING | UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING),
- UNIT_STAT_IN_FLIGHT = 0x0100, // player is in flight mode
- UNIT_STAT_FOLLOW = 0x0200,
- UNIT_STAT_ROOT = 0x0400,
- UNIT_STAT_CONFUSED = 0x0800,
- UNIT_STAT_DISTRACTED = 0x1000,
- UNIT_STAT_ISOLATED = 0x2000, // area auras do not affect other players
- UNIT_STAT_ATTACK_PLAYER = 0x4000,
- UNIT_STAT_ALL_STATE = 0xffff //(UNIT_STAT_STOPPED | UNIT_STAT_MOVING | UNIT_STAT_IN_COMBAT | UNIT_STAT_IN_FLIGHT)
-};
-
-enum UnitMoveType
-{
- MOVE_WALK = 0,
- MOVE_RUN = 1,
- MOVE_WALKBACK = 2,
- MOVE_SWIM = 3,
- MOVE_SWIMBACK = 4,
- MOVE_TURN = 5,
- MOVE_FLY = 6,
- MOVE_FLYBACK = 7
-};
-
-#define MAX_MOVE_TYPE 8
-
-extern float baseMoveSpeed[MAX_MOVE_TYPE];
-
-enum WeaponAttackType
-{
- BASE_ATTACK = 0,
- OFF_ATTACK = 1,
- RANGED_ATTACK = 2
-};
-
-#define MAX_ATTACK 3
-
-enum CombatRating
-{
- CR_WEAPON_SKILL = 0,
- CR_DEFENSE_SKILL = 1,
- CR_DODGE = 2,
- CR_PARRY = 3,
- CR_BLOCK = 4,
- CR_HIT_MELEE = 5,
- CR_HIT_RANGED = 6,
- CR_HIT_SPELL = 7,
- CR_CRIT_MELEE = 8,
- CR_CRIT_RANGED = 9,
- CR_CRIT_SPELL = 10,
- CR_HIT_TAKEN_MELEE = 11,
- CR_HIT_TAKEN_RANGED = 12,
- CR_HIT_TAKEN_SPELL = 13,
- CR_CRIT_TAKEN_MELEE = 14,
- CR_CRIT_TAKEN_RANGED = 15,
- CR_CRIT_TAKEN_SPELL = 16,
- CR_HASTE_MELEE = 17,
- CR_HASTE_RANGED = 18,
- CR_HASTE_SPELL = 19,
- CR_WEAPON_SKILL_MAINHAND = 20,
- CR_WEAPON_SKILL_OFFHAND = 21,
- CR_WEAPON_SKILL_RANGED = 22,
- CR_EXPERTISE = 23
-};
-
-#define MAX_COMBAT_RATING 24
-
-enum DamageEffectType
-{
- DIRECT_DAMAGE = 0, // used for normal weapon damage (not for class abilities or spells)
- SPELL_DIRECT_DAMAGE = 1, // spell/class abilities damage
- DOT = 2,
- HEAL = 3,
- NODAMAGE = 4, // used also in case when damage applied to health but not applied to spell channelInterruptFlags/etc
- SELF_DAMAGE = 5
-};
-
-enum UnitVisibility
-{
- VISIBILITY_OFF = 0, // absolute, not detectable, GM-like, can see all other
- VISIBILITY_ON = 1,
- VISIBILITY_GROUP_STEALTH = 2, // detect chance, seen and can see group members
- VISIBILITY_GROUP_INVISIBILITY = 3, // invisibility, can see and can be seen only another invisible unit or invisible detection unit, set only if not stealthed, and in checks not used (mask used instead)
- VISIBILITY_GROUP_NO_DETECT = 4, // state just at stealth apply for update Grid state. Don't remove, otherwise stealth spells will break
- VISIBILITY_RESPAWN = 5 // special totally not detectable visibility for force delete object at respawn command
-};
-
-// Value masks for UNIT_FIELD_FLAGS
-enum UnitFlags
-{
- UNIT_FLAG_UNKNOWN7 = 0x00000001,
- UNIT_FLAG_NON_ATTACKABLE = 0x00000002, // not attackable
- UNIT_FLAG_DISABLE_MOVE = 0x00000004,
- UNIT_FLAG_PVP_ATTACKABLE = 0x00000008, // allow apply pvp rules to attackable state in addition to faction dependent state
- UNIT_FLAG_RENAME = 0x00000010,
- UNIT_FLAG_PREPARATION = 0x00000020, // don't take reagents for spells with SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP
- UNIT_FLAG_UNKNOWN9 = 0x00000040,
- UNIT_FLAG_NOT_ATTACKABLE_1 = 0x00000080, // ?? (UNIT_FLAG_PVP_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1) is NON_PVP_ATTACKABLE
- UNIT_FLAG_UNKNOWN2 = 0x00000100, // 2.0.8
- UNIT_FLAG_UNKNOWN11 = 0x00000200,
- UNIT_FLAG_LOOTING = 0x00000400, // loot animation
- UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8
- UNIT_FLAG_PVP = 0x00001000,
- UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1
- UNIT_FLAG_UNKNOWN4 = 0x00004000, // 2.0.8
- UNIT_FLAG_UNKNOWN13 = 0x00008000,
- UNIT_FLAG_UNKNOWN14 = 0x00010000,
- UNIT_FLAG_PACIFIED = 0x00020000,
- UNIT_FLAG_DISABLE_ROTATE = 0x00040000, // stunned, 2.1.1
- UNIT_FLAG_IN_COMBAT = 0x00080000,
- UNIT_FLAG_TAXI_FLIGHT = 0x00100000, // disable casting at client side spell not allowed by taxi flight (mounted?), probably used with 0x4 flag
- UNIT_FLAG_DISARMED = 0x00200000, // disable melee spells casting..., "Required melee weapon" added to melee spells tooltip.
- UNIT_FLAG_CONFUSED = 0x00400000,
- UNIT_FLAG_FLEEING = 0x00800000,
- UNIT_FLAG_UNKNOWN5 = 0x01000000, // used in spell Eyes of the Beast for pet...
- UNIT_FLAG_NOT_SELECTABLE = 0x02000000,
- UNIT_FLAG_SKINNABLE = 0x04000000,
- UNIT_FLAG_MOUNT = 0x08000000,
- UNIT_FLAG_UNKNOWN17 = 0x10000000,
- UNIT_FLAG_UNKNOWN6 = 0x20000000, // used in Feing Death spell
- UNIT_FLAG_SHEATHE = 0x40000000
-};
-
-// Value masks for UNIT_FIELD_FLAGS_2
-enum UnitFlags2
-{
- UNIT_FLAG2_FEIGN_DEATH = 0x00000001,
- UNIT_FLAG2_COMPREHEND_LANG= 0x00000008,
- UNIT_FLAG2_FORCE_MOVE = 0x00000040
-};
-
-/// Non Player Character flags
-enum NPCFlags
-{
- UNIT_NPC_FLAG_NONE = 0x00000000,
- UNIT_NPC_FLAG_GOSSIP = 0x00000001, // 100%
- UNIT_NPC_FLAG_QUESTGIVER = 0x00000002, // guessed, probably ok
- UNIT_NPC_FLAG_UNK1 = 0x00000004,
- UNIT_NPC_FLAG_UNK2 = 0x00000008,
- UNIT_NPC_FLAG_TRAINER = 0x00000010, // 100%
- UNIT_NPC_FLAG_TRAINER_CLASS = 0x00000020, // 100%
- UNIT_NPC_FLAG_TRAINER_PROFESSION = 0x00000040, // 100%
- UNIT_NPC_FLAG_VENDOR = 0x00000080, // 100%
- UNIT_NPC_FLAG_VENDOR_AMMO = 0x00000100, // 100%, general goods vendor
- UNIT_NPC_FLAG_VENDOR_FOOD = 0x00000200, // 100%
- UNIT_NPC_FLAG_VENDOR_POISON = 0x00000400, // guessed
- UNIT_NPC_FLAG_VENDOR_REAGENT = 0x00000800, // 100%
- UNIT_NPC_FLAG_REPAIR = 0x00001000, // 100%
- UNIT_NPC_FLAG_FLIGHTMASTER = 0x00002000, // 100%
- UNIT_NPC_FLAG_SPIRITHEALER = 0x00004000, // guessed
- UNIT_NPC_FLAG_SPIRITGUIDE = 0x00008000, // guessed
- UNIT_NPC_FLAG_INNKEEPER = 0x00010000, // 100%
- UNIT_NPC_FLAG_BANKER = 0x00020000, // 100%
- UNIT_NPC_FLAG_PETITIONER = 0x00040000, // 100% 0xC0000 = guild petitions, 0x40000 = arena team petitions
- UNIT_NPC_FLAG_TABARDDESIGNER = 0x00080000, // 100%
- UNIT_NPC_FLAG_BATTLEMASTER = 0x00100000, // 100%
- UNIT_NPC_FLAG_AUCTIONEER = 0x00200000, // 100%
- UNIT_NPC_FLAG_STABLEMASTER = 0x00400000, // 100%
- UNIT_NPC_FLAG_GUILD_BANKER = 0x00800000, // cause client to send 997 opcode
- UNIT_NPC_FLAG_UNK3 = 0x01000000, // cause client to send 1015 opcode
- UNIT_NPC_FLAG_GUARD = 0x10000000, // custom flag for guards
-};
-
-enum MovementFlags
-{
- MOVEMENTFLAG_NONE = 0x00000000,
- MOVEMENTFLAG_FORWARD = 0x00000001,
- MOVEMENTFLAG_BACKWARD = 0x00000002,
- MOVEMENTFLAG_STRAFE_LEFT = 0x00000004,
- MOVEMENTFLAG_STRAFE_RIGHT = 0x00000008,
- MOVEMENTFLAG_LEFT = 0x00000010,
- MOVEMENTFLAG_RIGHT = 0x00000020,
- MOVEMENTFLAG_PITCH_UP = 0x00000040,
- MOVEMENTFLAG_PITCH_DOWN = 0x00000080,
- MOVEMENTFLAG_WALK_MODE = 0x00000100, // Walking
- MOVEMENTFLAG_ONTRANSPORT = 0x00000200, // Used for flying on some creatures
- MOVEMENTFLAG_LEVITATING = 0x00000400,
- MOVEMENTFLAG_FLY_UNK1 = 0x00000800,
- MOVEMENTFLAG_JUMPING = 0x00001000,
- MOVEMENTFLAG_UNK4 = 0x00002000,
- MOVEMENTFLAG_FALLING = 0x00004000,
- // 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000
- MOVEMENTFLAG_SWIMMING = 0x00200000, // appears with fly flag also
- MOVEMENTFLAG_FLY_UP = 0x00400000,
- MOVEMENTFLAG_CAN_FLY = 0x00800000,
- MOVEMENTFLAG_FLYING = 0x01000000,
- MOVEMENTFLAG_FLYING2 = 0x02000000, // Actual flying mode
- MOVEMENTFLAG_SPLINE = 0x04000000, // used for flight paths
- MOVEMENTFLAG_SPLINE2 = 0x08000000, // used for flight paths
- MOVEMENTFLAG_WATERWALKING = 0x10000000, // prevent unit from falling through water
- MOVEMENTFLAG_SAFE_FALL = 0x20000000, // active rogue safe fall spell (passive)
- MOVEMENTFLAG_UNK3 = 0x40000000
-};
-
-enum DiminishingLevels
-{
- DIMINISHING_LEVEL_1 = 0,
- DIMINISHING_LEVEL_2 = 1,
- DIMINISHING_LEVEL_3 = 2,
- DIMINISHING_LEVEL_IMMUNE = 3
-};
-
-struct DiminishingReturn
-{
- DiminishingReturn(DiminishingGroup group, uint32 t, uint32 count) : DRGroup(group), hitTime(t), hitCount(count), stack(0) {}
-
- DiminishingGroup DRGroup:16;
- uint16 stack:16;
- uint32 hitTime;
- uint32 hitCount;
-};
-
-enum MeleeHitOutcome
-{
- MELEE_HIT_EVADE, MELEE_HIT_MISS, MELEE_HIT_DODGE, MELEE_HIT_BLOCK, MELEE_HIT_PARRY,
- MELEE_HIT_GLANCING, MELEE_HIT_CRIT, MELEE_HIT_CRUSHING, MELEE_HIT_NORMAL, MELEE_HIT_BLOCK_CRIT
-};
-struct CleanDamage
-{
- CleanDamage(uint32 _damage, WeaponAttackType _attackType, MeleeHitOutcome _hitOutCome) :
- damage(_damage), attackType(_attackType), hitOutCome(_hitOutCome) {}
-
- uint32 damage;
- WeaponAttackType attackType;
- MeleeHitOutcome hitOutCome;
-};
-
-struct UnitActionBarEntry
-{
- uint32 Type;
- uint32 SpellOrAction;
-};
-
-#define MAX_DECLINED_NAME_CASES 5
-
-struct DeclinedName
-{
- std::string name[MAX_DECLINED_NAME_CASES];
-};
-
-enum CurrentSpellTypes
-{
- CURRENT_MELEE_SPELL = 0,
- CURRENT_FIRST_NON_MELEE_SPELL = 1, // just counter
- CURRENT_GENERIC_SPELL = 1,
- CURRENT_AUTOREPEAT_SPELL = 2,
- CURRENT_CHANNELED_SPELL = 3,
- CURRENT_MAX_SPELL = 4 // just counter
-};
-
-enum ActiveStates
-{
- ACT_ENABLED = 0xC100,
- ACT_DISABLED = 0x8100,
- ACT_COMMAND = 0x0700,
- ACT_REACTION = 0x0600,
- ACT_CAST = 0x0100,
- ACT_PASSIVE = 0x0000,
- ACT_DECIDE = 0x0001
-};
-
-enum ReactStates
-{
- REACT_PASSIVE = 0,
- REACT_DEFENSIVE = 1,
- REACT_AGGRESSIVE = 2
-};
-
-enum CommandStates
-{
- COMMAND_STAY = 0,
- COMMAND_FOLLOW = 1,
- COMMAND_ATTACK = 2,
- COMMAND_ABANDON = 3
-};
-
-struct CharmSpellEntry
-{
- uint16 spellId;
- uint16 active;
-};
-
-struct CharmInfo
-{
- public:
- explicit CharmInfo(Unit* unit);
- uint32 GetPetNumber() const { return m_petnumber; }
- void SetPetNumber(uint32 petnumber, bool statwindow);
-
- void SetCommandState(CommandStates st) { m_CommandState = st; }
- CommandStates GetCommandState() { return m_CommandState; }
- bool HasCommandState(CommandStates state) { return (m_CommandState == state); }
- void SetReactState(ReactStates st) { m_ReactSate = st; }
- ReactStates GetReactState() { return m_ReactSate; }
- bool HasReactState(ReactStates state) { return (m_ReactSate == state); }
-
- void InitPossessCreateSpells();
- void InitCharmCreateSpells();
- void InitPetActionBar();
- void InitEmptyActionBar();
- //return true if successful
- bool AddSpellToAB(uint32 oldid, uint32 newid, ActiveStates newstate = ACT_DECIDE);
- void ToggleCreatureAutocast(uint32 spellid, bool apply);
-
- UnitActionBarEntry* GetActionBarEntry(uint8 index) { return &(PetActionBar[index]); }
- CharmSpellEntry* GetCharmSpell(uint8 index) { return &(m_charmspells[index]); }
- private:
- Unit* m_unit;
- UnitActionBarEntry PetActionBar[10];
- CharmSpellEntry m_charmspells[4];
- CommandStates m_CommandState;
- ReactStates m_ReactSate;
- uint32 m_petnumber;
-};
-
-// for clearing special attacks
-#define REACTIVE_TIMER_START 4000
-
-enum ReactiveType
-{
- REACTIVE_DEFENSE = 1,
- REACTIVE_HUNTER_PARRY = 2,
- REACTIVE_CRIT = 3,
- REACTIVE_HUNTER_CRIT = 4,
- REACTIVE_OVERPOWER = 5
-};
-
-#define MAX_REACTIVE 6
-#define MAX_TOTEM 4
-
-// delay time next attack to prevent client attack animation problems
-#define ATTACK_DISPLAY_DELAY 200
-
-class MANGOS_DLL_SPEC Unit : public WorldObject
-{
- public:
- typedef std::set<Unit*> AttackerSet;
- typedef std::pair<uint32, uint8> spellEffectPair;
- typedef std::multimap< spellEffectPair, Aura*> AuraMap;
- typedef std::list<Aura *> AuraList;
- typedef std::list<DiminishingReturn> Diminishing;
- typedef std::set<AuraType> AuraTypeSet;
- typedef std::set<uint32> ComboPointHolderSet;
-
- virtual ~Unit ( );
-
- void AddToWorld();
- void RemoveFromWorld();
-
- void CleanupsBeforeDelete(); // used in ~Creature/~Player (or before mass creature delete to remove cross-references to already deleted units)
-
- DiminishingLevels GetDiminishing(DiminishingGroup group);
- void IncrDiminishing(DiminishingGroup group);
- void ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster, DiminishingLevels Level);
- void ApplyDiminishingAura(DiminishingGroup group, bool apply);
- void ClearDiminishings() { m_Diminishing.clear(); }
-
- virtual void Update( uint32 time );
-
- void setAttackTimer(WeaponAttackType type, uint32 time) { m_attackTimer[type] = time; }
- void resetAttackTimer(WeaponAttackType type = BASE_ATTACK);
- uint32 getAttackTimer(WeaponAttackType type) const { return m_attackTimer[type]; }
- bool isAttackReady(WeaponAttackType type = BASE_ATTACK) const { return m_attackTimer[type] == 0; }
- bool haveOffhandWeapon() const;
- bool canReachWithAttack(Unit *pVictim) const;
- uint32 m_extraAttacks;
-
- void _addAttacker(Unit *pAttacker) // must be called only from Unit::Attack(Unit*)
- {
- AttackerSet::iterator itr = m_attackers.find(pAttacker);
- if(itr == m_attackers.end())
- m_attackers.insert(pAttacker);
- }
- void _removeAttacker(Unit *pAttacker) // must be called only from Unit::AttackStop()
- {
- AttackerSet::iterator itr = m_attackers.find(pAttacker);
- if(itr != m_attackers.end())
- m_attackers.erase(itr);
- }
- Unit * getAttackerForHelper() // If someone wants to help, who to give them
- {
- if (getVictim() != NULL)
- return getVictim();
-
- if (!m_attackers.empty())
- return *(m_attackers.begin());
-
- return NULL;
- }
- bool Attack(Unit *victim, bool meleeAttack);
- void CastStop(uint32 except_spellid = 0);
- bool AttackStop();
- void RemoveAllAttackers();
- AttackerSet const& getAttackers() const { return m_attackers; }
- bool isAttackingPlayer() const;
- Unit* getVictim() const { return m_attacking; }
- void CombatStop(bool cast = false);
- void CombatStopWithPets(bool cast = false);
- Unit* SelectNearbyTarget() const;
-
- void addUnitState(uint32 f) { m_state |= f; }
- bool hasUnitState(const uint32 f) const { return (m_state & f); }
- void clearUnitState(uint32 f) { m_state &= ~f; }
- bool CanFreeMove() const
- {
- return !hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_FLEEING | UNIT_STAT_IN_FLIGHT |
- UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED ) && GetOwnerGUID()==0;
- }
-
- uint32 getLevel() const { return GetUInt32Value(UNIT_FIELD_LEVEL); }
- virtual uint32 getLevelForTarget(Unit const* /*target*/) const { return getLevel(); }
- void SetLevel(uint32 lvl);
- uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, 0); }
- uint32 getRaceMask() const { return 1 << (getRace()-1); }
- uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, 1); }
- uint32 getClassMask() const { return 1 << (getClass()-1); }
- uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, 2); }
-
- float GetStat(Stats stat) const { return float(GetUInt32Value(UNIT_FIELD_STAT0+stat)); }
- void SetStat(Stats stat, int32 val) { SetStatInt32Value(UNIT_FIELD_STAT0+stat, val); }
- uint32 GetArmor() const { return GetResistance(SPELL_SCHOOL_NORMAL) ; }
- void SetArmor(int32 val) { SetResistance(SPELL_SCHOOL_NORMAL, val); }
-
- uint32 GetResistance(SpellSchools school) const { return GetUInt32Value(UNIT_FIELD_RESISTANCES+school); }
- void SetResistance(SpellSchools school, int32 val) { SetStatInt32Value(UNIT_FIELD_RESISTANCES+school,val); }
-
- uint32 GetHealth() const { return GetUInt32Value(UNIT_FIELD_HEALTH); }
- uint32 GetMaxHealth() const { return GetUInt32Value(UNIT_FIELD_MAXHEALTH); }
- void SetHealth( uint32 val);
- void SetMaxHealth(uint32 val);
- int32 ModifyHealth(int32 val);
-
- Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, 3)); }
- void setPowerType(Powers power);
- uint32 GetPower( Powers power) const { return GetUInt32Value(UNIT_FIELD_POWER1 +power); }
- uint32 GetMaxPower(Powers power) const { return GetUInt32Value(UNIT_FIELD_MAXPOWER1+power); }
- void SetPower( Powers power, uint32 val);
- void SetMaxPower(Powers power, uint32 val);
- int32 ModifyPower(Powers power, int32 val);
- void ApplyPowerMod(Powers power, uint32 val, bool apply);
- void ApplyMaxPowerMod(Powers power, uint32 val, bool apply);
-
- uint32 GetAttackTime(WeaponAttackType att) const { return (uint32)(GetFloatValue(UNIT_FIELD_BASEATTACKTIME+att)/m_modAttackSpeedPct[att]); }
- void SetAttackTime(WeaponAttackType att, uint32 val) { SetFloatValue(UNIT_FIELD_BASEATTACKTIME+att,val*m_modAttackSpeedPct[att]); }
- void ApplyAttackTimePercentMod(WeaponAttackType att,float val, bool apply);
- void ApplyCastTimePercentMod(float val, bool apply);
-
- // faction template id
- uint32 getFaction() const { return GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); }
- void setFaction(uint32 faction) { SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, faction ); }
- FactionTemplateEntry const* getFactionTemplateEntry() const;
- bool IsHostileTo(Unit const* unit) const;
- bool IsHostileToPlayers() const;
- bool IsFriendlyTo(Unit const* unit) const;
- bool IsNeutralToAll() const;
- bool IsContestedGuard() const
- {
- if(FactionTemplateEntry const* entry = getFactionTemplateEntry())
- return entry->IsContestedGuardFaction();
-
- return false;
- }
- bool IsPvP() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); }
- void SetPvP(bool state) { if(state) SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); else RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); }
- uint32 GetCreatureType() const;
- uint32 GetCreatureTypeMask() const
- {
- uint32 creatureType = GetCreatureType();
- return (creatureType >= 1) ? (1 << (creatureType - 1)) : 0;
- }
-
- uint8 getStandState() const { return GetByteValue(UNIT_FIELD_BYTES_1, 0); }
- bool IsSitState() const;
- bool IsStandState() const;
- void SetStandState(uint8 state);
-
- bool IsMounted() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT ); }
- uint32 GetMountID() const { return GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID); }
- void Mount(uint32 mount);
- void Unmount();
-
- uint16 GetMaxSkillValueForLevel(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; }
- uint32 DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss);
- void DealFlatDamage(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit = false, bool isTriggeredSpell = false);
- void DoAttackDamage(Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchoolMask damageSchoolMask, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false);
-
- void CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, SpellSchoolMask damageSchoolMask, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false);
- void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage = 0, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NONE, SpellEntry const *procSpell = NULL, bool isTriggeredSpell = false, WeaponAttackType attType = BASE_ATTACK);
- void HandleEmoteCommand(uint32 anim_id);
- void AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType = BASE_ATTACK, bool extra = false );
-
- float MeleeMissChanceCalc(const Unit *pVictim, WeaponAttackType attType) const;
- SpellMissInfo MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell);
- SpellMissInfo SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool canReflect = false);
-
- float GetUnitDodgeChance() const;
- float GetUnitParryChance() const;
- float GetUnitBlockChance() const;
- float GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVictim) const;
-
- virtual uint32 GetShieldBlockValue() const =0;
- uint32 GetUnitMeleeSkill(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; }
- uint32 GetDefenseSkillValue(Unit const* target = NULL) const;
- uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = NULL) const;
- float GetWeaponProcChance() const;
- float GetPPMProcChance(uint32 WeaponSpeed, float PPM) const;
- MeleeHitOutcome RollPhysicalOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, SpellEntry const *spellInfo);
- MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType) const;
- MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance, bool SpellCasted ) const;
-
- bool isVendor() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR ); }
- bool isTrainer() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER ); }
- bool isQuestGiver() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER ); }
- bool isGossip() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP ); }
- bool isTaxi() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_FLIGHTMASTER ); }
- bool isGuildMaster() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PETITIONER ); }
- bool isBattleMaster() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_BATTLEMASTER ); }
- bool isBanker() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_BANKER ); }
- bool isInnkeeper() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_INNKEEPER ); }
- bool isSpiritHealer() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER ); }
- bool isSpiritGuide() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITGUIDE ); }
- bool isTabardDesigner()const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TABARDDESIGNER ); }
- bool isAuctioner() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_AUCTIONEER ); }
- bool isArmorer() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_REPAIR ); }
- bool isServiceProvider() const
- {
- return HasFlag( UNIT_NPC_FLAGS,
- UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_FLIGHTMASTER |
- UNIT_NPC_FLAG_PETITIONER | UNIT_NPC_FLAG_BATTLEMASTER | UNIT_NPC_FLAG_BANKER |
- UNIT_NPC_FLAG_INNKEEPER | UNIT_NPC_FLAG_GUARD | UNIT_NPC_FLAG_SPIRITHEALER |
- UNIT_NPC_FLAG_SPIRITGUIDE | UNIT_NPC_FLAG_TABARDDESIGNER | UNIT_NPC_FLAG_AUCTIONEER );
- }
- bool isSpiritService() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER | UNIT_NPC_FLAG_SPIRITGUIDE ); }
-
- //Need fix or use this
- bool isGuard() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GUARD); }
-
- bool isInFlight() const { return hasUnitState(UNIT_STAT_IN_FLIGHT); }
-
- bool isInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); }
- void SetInCombatState(bool PvP);
- void SetInCombatWith(Unit* enemy);
- void ClearInCombat();
- uint32 GetCombatTimer() const { return m_CombatTimer; }
-
- bool HasAuraType(AuraType auraType) const;
- bool HasAura(uint32 spellId, uint32 effIndex) const
- { return m_Auras.find(spellEffectPair(spellId, effIndex)) != m_Auras.end(); }
-
- bool virtual HasSpell(uint32 /*spellID*/) const { return false; }
-
- bool HasStealthAura() const { return HasAuraType(SPELL_AURA_MOD_STEALTH); }
- bool HasInvisibilityAura() const { return HasAuraType(SPELL_AURA_MOD_INVISIBILITY); }
- bool isFeared() const { return HasAuraType(SPELL_AURA_MOD_FEAR); }
- bool isInRoots() const { return HasAuraType(SPELL_AURA_MOD_ROOT); }
- bool IsPolymorphed() const;
-
- bool isFrozen() const;
-
- void RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage);
-
- bool isTargetableForAttack() const;
- virtual bool IsInWater() const;
- virtual bool IsUnderWater() const;
- bool isInAccessablePlaceFor(Creature const* c) const;
-
- void SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, bool critical = false);
- void SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage,Powers powertype, bool critical = false);
- uint32 SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool isTriggeredSpell = false, bool useSpellDamage = true);
- void CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
- void CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
- void CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
- void CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
- void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
- void CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem = NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
-
- bool IsDamageToThreatSpell(SpellEntry const * spellInfo) const;
-
- void DeMorph();
-
- void SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount);
- void SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchoolMask damageSchoolMask,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false);
- void SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo);
-
- void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL);
- void SendMonsterMoveByPath(Path const& path, uint32 start, uint32 end, uint32 MovementFlags);
- void SendMonsterMoveWithSpeed(float x, float y, float z, uint32 MovementFlags, uint32 transitTime = 0, Player* player = NULL);
- void SendMonsterMoveWithSpeedToCurrentDestination(Player* player = NULL);
-
- virtual void MoveOutOfRange(Player &) { };
-
- bool isAlive() const { return (m_deathState == ALIVE); };
- bool isDead() const { return ( m_deathState == DEAD || m_deathState == CORPSE ); };
- DeathState getDeathState() { return m_deathState; };
- virtual void setDeathState(DeathState s); // overwrited in Creature/Player/Pet
-
- uint64 const& GetOwnerGUID() const { return GetUInt64Value(UNIT_FIELD_SUMMONEDBY); }
- uint64 GetPetGUID() const { return GetUInt64Value(UNIT_FIELD_SUMMON); }
- uint64 GetCharmerGUID() const { return GetUInt64Value(UNIT_FIELD_CHARMEDBY); }
- uint64 GetCharmGUID() const { return GetUInt64Value(UNIT_FIELD_CHARM); }
- void SetCharmerGUID(uint64 owner) { SetUInt64Value(UNIT_FIELD_CHARMEDBY, owner); }
-
- uint64 GetCharmerOrOwnerGUID() const { return GetCharmerGUID() ? GetCharmerGUID() : GetOwnerGUID(); }
- uint64 GetCharmerOrOwnerOrOwnGUID() const
- {
- if(uint64 guid = GetCharmerOrOwnerGUID())
- return guid;
- return GetGUID();
- }
- bool isCharmedOwnedByPlayerOrPlayer() const { return IS_PLAYER_GUID(GetCharmerOrOwnerOrOwnGUID()); }
-
- Player* GetSpellModOwner();
-
- Unit* GetOwner() const;
- Pet* GetPet() const;
- Unit* GetCharmer() const;
- Unit* GetCharm() const;
- Unit* GetCharmerOrOwner() const { return GetCharmerGUID() ? GetCharmer() : GetOwner(); }
- Unit* GetCharmerOrOwnerOrSelf()
- {
- if(Unit* u = GetCharmerOrOwner())
- return u;
-
- return this;
- }
- Player* GetCharmerOrOwnerPlayerOrPlayerItself();
-
- void SetPet(Pet* pet);
- void SetCharm(Unit* pet);
- bool isCharmed() const { return GetCharmerGUID() != 0; }
-
- CharmInfo* GetCharmInfo() { return m_charmInfo; }
- CharmInfo* InitCharmInfo(Unit* charm);
-
- bool AddAura(Aura *aur);
-
- void RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT);
- void RemoveAura(uint32 spellId, uint32 effindex, Aura* except = NULL);
- void RemoveSingleAuraFromStack(uint32 spellId, uint32 effindex);
- void RemoveAurasDueToSpell(uint32 spellId, Aura* except = NULL);
- void RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId);
- void RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit *dispeler);
- void RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit *stealer);
- void RemoveAurasDueToSpellByCancel(uint32 spellId);
- void RemoveNotOwnSingleTargetAuras();
-
- void RemoveSpellsCausingAura(AuraType auraType);
- void RemoveRankAurasDueToSpell(uint32 spellId);
- bool RemoveNoStackAurasDueToAura(Aura *Aur);
- void RemoveAurasWithInterruptFlags(uint32 flags);
- void RemoveAurasWithDispelType( DispelType type );
-
- void RemoveAllAuras();
- void RemoveArenaAuras(bool onleave = false);
- void RemoveAllAurasOnDeath();
- void DelayAura(uint32 spellId, uint32 effindex, int32 delaytime);
-
- float GetResistanceBuffMods(SpellSchools school, bool positive) const { return GetFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school ); }
- void SetResistanceBuffMods(SpellSchools school, bool positive, float val) { SetFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school,val); }
- void ApplyResistanceBuffModsMod(SpellSchools school, bool positive, float val, bool apply) { ApplyModSignedFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school, val, apply); }
- void ApplyResistanceBuffModsPercentMod(SpellSchools school, bool positive, float val, bool apply) { ApplyPercentModFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school, val, apply); }
- void InitStatBuffMods()
- {
- for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) SetFloatValue(UNIT_FIELD_POSSTAT0+i, 0);
- for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) SetFloatValue(UNIT_FIELD_NEGSTAT0+i, 0);
- }
- void ApplyStatBuffMod(Stats stat, float val, bool apply) { ApplyModSignedFloatValue((val > 0 ? UNIT_FIELD_POSSTAT0+stat : UNIT_FIELD_NEGSTAT0+stat), val, apply); }
- void ApplyStatPercentBuffMod(Stats stat, float val, bool apply)
- {
- ApplyPercentModFloatValue(UNIT_FIELD_POSSTAT0+stat, val, apply);
- ApplyPercentModFloatValue(UNIT_FIELD_NEGSTAT0+stat, val, apply);
- }
- void SetCreateStat(Stats stat, float val) { m_createStats[stat] = val; }
- void SetCreateHealth(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_HEALTH, val); }
- uint32 GetCreateHealth() const { return GetUInt32Value(UNIT_FIELD_BASE_HEALTH); }
- void SetCreateMana(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_MANA, val); }
- uint32 GetCreateMana() const { return GetUInt32Value(UNIT_FIELD_BASE_MANA); }
- uint32 GetCreatePowers(Powers power) const;
- float GetPosStat(Stats stat) const { return GetFloatValue(UNIT_FIELD_POSSTAT0+stat); }
- float GetNegStat(Stats stat) const { return GetFloatValue(UNIT_FIELD_NEGSTAT0+stat); }
- float GetCreateStat(Stats stat) const { return m_createStats[stat]; }
-
- void SetCurrentCastedSpell(Spell * pSpell);
- virtual void ProhibitSpellScholl(SpellSchoolMask /*idSchoolMask*/, uint32 /*unTimeMs*/ ) { }
- void InterruptSpell(uint32 spellType, bool withDelayed = true);
-
- // set withDelayed to true to account delayed spells as casted
- // delayed+channeled spells are always accounted as casted
- // we can skip channeled or delayed checks using flags
- bool IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled = false, bool skipAutorepeat = false) const;
-
- // set withDelayed to true to interrupt delayed spells too
- // delayed+channeled spells are always interrupted
- void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid = 0);
-
- Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
-
- Spell* m_currentSpells[CURRENT_MAX_SPELL];
-
- uint32 m_addDmgOnce;
- uint64 m_TotemSlot[MAX_TOTEM];
- uint64 m_ObjectSlot[4];
- uint32 m_detectInvisibilityMask;
- uint32 m_invisibilityMask;
- uint32 m_ShapeShiftFormSpellId;
- ShapeshiftForm m_form;
- float m_modMeleeHitChance;
- float m_modRangedHitChance;
- float m_modSpellHitChance;
- int32 m_baseSpellCritChance;
-
- float m_threatModifier[MAX_SPELL_SCHOOL];
- float m_modAttackSpeedPct[3];
-
- // Event handler
- EventProcessor m_Events;
-
- // stat system
- bool HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply);
- void SetModifierValue(UnitMods unitMod, UnitModifierType modifierType, float value) { m_auraModifiersGroup[unitMod][modifierType] = value; }
- float GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const;
- float GetTotalStatValue(Stats stat) const;
- float GetTotalAuraModValue(UnitMods unitMod) const;
- SpellSchools GetSpellSchoolByAuraGroup(UnitMods unitMod) const;
- Stats GetStatByAuraGroup(UnitMods unitMod) const;
- Powers GetPowerTypeByAuraGroup(UnitMods unitMod) const;
- bool CanModifyStats() const { return m_canModifyStats; }
- void SetCanModifyStats(bool modifyStats) { m_canModifyStats = modifyStats; }
- virtual bool UpdateStats(Stats stat) = 0;
- virtual bool UpdateAllStats() = 0;
- virtual void UpdateResistances(uint32 school) = 0;
- virtual void UpdateArmor() = 0;
- virtual void UpdateMaxHealth() = 0;
- virtual void UpdateMaxPower(Powers power) = 0;
- virtual void UpdateAttackPowerAndDamage(bool ranged = false) = 0;
- virtual void UpdateDamagePhysical(WeaponAttackType attType) = 0;
- float GetTotalAttackPowerValue(WeaponAttackType attType) const;
- float GetWeaponDamageRange(WeaponAttackType attType ,WeaponDamageRange type) const;
- void SetBaseWeaponDamage(WeaponAttackType attType ,WeaponDamageRange damageRange, float value) { m_weaponDamage[attType][damageRange] = value; }
-
- bool isInFront(Unit const* target,float distance, float arc = M_PI) const;
- void SetInFront(Unit const* target);
- bool isInBack(Unit const* target, float distance, float arc = M_PI) const;
-
- // Visibility system
- UnitVisibility GetVisibility() const { return m_Visibility; }
- void SetVisibility(UnitVisibility x);
-
- // common function for visibility checks for player/creatures with detection code
- bool isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList = false) const;
- bool canDetectInvisibilityOf(Unit const* u) const;
-
- // virtual functions for all world objects types
- bool isVisibleForInState(Player const* u, bool inVisibleList) const;
- // function for low level grid visibility checks in player/creature cases
- virtual bool IsVisibleInGridForPlayer(Player* pl) const = 0;
-
- bool waterbreath;
- AuraList & GetSingleCastAuras() { return m_scAuras; }
- AuraList const& GetSingleCastAuras() const { return m_scAuras; }
- SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY];
-
- // Threat related methodes
- bool CanHaveThreatList() const;
- void AddThreat(Unit* pVictim, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL);
- float ApplyTotalThreatModifier(float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL);
- void DeleteThreatList();
- bool SelectHostilTarget();
- void TauntApply(Unit* pVictim);
- void TauntFadeOut(Unit *taunter);
- ThreatManager& getThreatManager() { return m_ThreatManager; }
- void addHatedBy(HostilReference* pHostilReference) { m_HostilRefManager.insertFirst(pHostilReference); };
- void removeHatedBy(HostilReference* /*pHostilReference*/ ) { /* nothing to do yet */ }
- HostilRefManager& getHostilRefManager() { return m_HostilRefManager; }
-
- Aura* GetAura(uint32 spellId, uint32 effindex);
- AuraMap & GetAuras() { return m_Auras; }
- AuraMap const& GetAuras() const { return m_Auras; }
- AuraList const& GetAurasByType(AuraType type) const { return m_modAuras[type]; }
- void ApplyAuraProcTriggerDamage(Aura* aura, bool apply);
-
- int32 GetTotalAuraModifier(AuraType auratype) const;
- float GetTotalAuraMultiplier(AuraType auratype) const;
- int32 GetMaxPositiveAuraModifier(AuraType auratype) const;
- int32 GetMaxNegativeAuraModifier(AuraType auratype) const;
-
- int32 GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const;
- float GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask) const;
- int32 GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const;
- int32 GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const;
-
- int32 GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const;
- float GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 misc_value) const;
- int32 GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const;
- int32 GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const;
-
- Aura* GetDummyAura(uint32 spell_id) const;
-
- uint32 GetDisplayId() { return GetUInt32Value(UNIT_FIELD_DISPLAYID); }
- void SetDisplayId(uint32 modelId);
- uint32 GetNativeDisplayId() { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); }
- void SetNativeDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, modelId); }
- void setTransForm(uint32 spellid) { m_transform = spellid;}
- uint32 getTransForm() const { return m_transform;}
- void AddDynObject(DynamicObject* dynObj);
- void RemoveDynObject(uint32 spellid);
- void RemoveDynObjectWithGUID(uint64 guid) { m_dynObjGUIDs.remove(guid); }
- void RemoveAllDynObjects();
- void AddGameObject(GameObject* gameObj);
- void RemoveGameObject(GameObject* gameObj, bool del);
- void RemoveGameObject(uint32 spellid, bool del);
- void RemoveAllGameObjects();
- DynamicObject *GetDynObject(uint32 spellId, uint32 effIndex);
- DynamicObject *GetDynObject(uint32 spellId);
- uint32 CalculateDamage(WeaponAttackType attType, bool normalized);
- float GetAPMultiplier(WeaponAttackType attType, bool normalized);
- void ModifyAuraState(AuraState flag, bool apply);
- bool HasAuraState(AuraState flag) const { return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); }
- void UnsummonAllTotems();
- int32 SpellBaseDamageBonus(SpellSchoolMask schoolMask);
- int32 SpellBaseHealingBonus(SpellSchoolMask schoolMask);
- int32 SpellBaseDamageBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim);
- int32 SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim);
- uint32 SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 damage, DamageEffectType damagetype);
- uint32 SpellHealingBonus(SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, Unit *pVictim);
- bool isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType);
- uint32 SpellCriticalBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim);
-
- void SetLastManaUse(uint32 spellCastTime) { m_lastManaUse = spellCastTime; }
- bool IsUnderLastManaUseEffect() const;
-
- void SetContestedPvP(Player *attackedPlayer = NULL);
-
- void MeleeDamageBonus(Unit *pVictim, uint32 *damage, WeaponAttackType attType, SpellEntry const *spellProto = NULL);
- uint32 GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectType damagetype, uint32 CastingTime );
-
- void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply);
- void ApplySpellDispelImmunity(const SpellEntry * spellProto, DispelType type, bool apply);
- virtual bool IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges = false);
- // redefined in Creature
- bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask, bool useCharges = false);
- virtual bool IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const;
- // redefined in Creature
-
- uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage);
- void CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist);
-
- void UpdateSpeed(UnitMoveType mtype, bool forced);
- float GetSpeed( UnitMoveType mtype ) const;
- float GetSpeedRate( UnitMoveType mtype ) const { return m_speed_rate[mtype]; }
- void SetSpeed(UnitMoveType mtype, float rate, bool forced = false);
-
- void SetHover(bool on);
- bool isHover() const { return HasAuraType(SPELL_AURA_HOVER); }
-
- void _RemoveAllAuraMods();
- void _ApplyAllAuraMods();
-
- int32 CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_index, int32 basePoints, Unit const* target);
- int32 CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_index, Unit const* target);
- float CalculateLevelPenalty(SpellEntry const* spellProto) const;
-
- void addFollower(FollowerReference* pRef) { m_FollowingRefManager.insertFirst(pRef); }
- void removeFollower(FollowerReference* /*pRef*/ ) { /* nothing to do yet */ }
- static Unit* GetUnit(WorldObject& object, uint64 guid);
-
- MotionMaster* GetMotionMaster() { return &i_motionMaster; }
-
- bool IsStopped() const { return !(hasUnitState(UNIT_STAT_MOVING)); }
- void StopMoving();
-
- void AddUnitMovementFlag(uint32 f) { m_unit_movement_flags |= f; }
- void RemoveUnitMovementFlag(uint32 f)
- {
- uint32 oldval = m_unit_movement_flags;
- m_unit_movement_flags = oldval & ~f;
- }
- uint32 HasUnitMovementFlag(uint32 f) const { return m_unit_movement_flags & f; }
- uint32 GetUnitMovementFlags() const { return m_unit_movement_flags; }
- void SetUnitMovementFlags(uint32 f) { m_unit_movement_flags = f; }
-
- void SetFeared(bool apply, uint64 casterGUID = 0, uint32 spellID = 0);
- void SetConfused(bool apply, uint64 casterGUID = 0, uint32 spellID = 0);
-
- void AddComboPointHolder(uint32 lowguid) { m_ComboPointHolders.insert(lowguid); }
- void RemoveComboPointHolder(uint32 lowguid) { m_ComboPointHolders.erase(lowguid); }
- void ClearComboPointHolders();
-
- ///----------Pet responses methods-----------------
- void SendPetCastFail(uint32 spellid, uint8 msg);
- void SendPetActionFeedback (uint8 msg);
- void SendPetTalk (uint32 pettalk);
- void SendPetSpellCooldown (uint32 spellid, time_t cooltime);
- void SendPetClearCooldown (uint32 spellid);
- void SendPetAIReaction(uint64 guid);
- ///----------End of Pet responses methods----------
-
- void propagateSpeedChange() { GetMotionMaster()->propagateSpeedChange(); }
-
- // reactive attacks
- void ClearAllReactives();
- void StartReactiveTimer( ReactiveType reactive ) { m_reactiveTimer[reactive] = REACTIVE_TIMER_START;}
- void UpdateReactives(uint32 p_time);
-
- // group updates
- void UpdateAuraForGroup(uint8 slot);
-
- // pet auras
- typedef std::set<PetAura const*> PetAuraSet;
- PetAuraSet m_petAuras;
- void AddPetAura(PetAura const* petSpell);
- void RemovePetAura(PetAura const* petSpell);
-
- protected:
- explicit Unit ();
-
- void _UpdateSpells(uint32 time);
-
- void _UpdateAutoRepeatSpell();
- bool m_AutoRepeatFirstCast;
-
- uint32 m_attackTimer[MAX_ATTACK];
-
- float m_createStats[MAX_STATS];
-
- AttackerSet m_attackers;
- Unit* m_attacking;
-
- DeathState m_deathState;
-
- AuraMap m_Auras;
-
- std::list<Aura *> m_scAuras; // casted singlecast auras
-
- typedef std::list<uint64> DynObjectGUIDs;
- DynObjectGUIDs m_dynObjGUIDs;
-
- std::list<GameObject*> m_gameObj;
- bool m_isSorted;
- uint32 m_transform;
- uint32 m_removedAuras;
-
- AuraList m_modAuras[TOTAL_AURAS];
- float m_auraModifiersGroup[UNIT_MOD_END][MODIFIER_TYPE_END];
- float m_weaponDamage[MAX_ATTACK][2];
- bool m_canModifyStats;
- //std::list< spellEffectPair > AuraSpells[TOTAL_AURAS]; // TODO: use this if ok for mem
-
- float m_speed_rate[MAX_MOVE_TYPE];
-
- CharmInfo *m_charmInfo;
-
- virtual SpellSchoolMask GetMeleeDamageSchoolMask() const;
-
- MotionMaster i_motionMaster;
- uint32 m_unit_movement_flags;
-
- uint32 m_reactiveTimer[MAX_REACTIVE];
-
- private:
- void SendAttackStop(Unit* victim); // only from AttackStop(Unit*)
- void SendAttackStart(Unit* pVictim); // only from Unit::AttackStart(Unit*)
-
- void ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, AuraTypeSet const& procAuraTypes, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage, SpellSchoolMask damageSchoolMask );
- bool HandleDummyAuraProc(Unit *pVictim, SpellEntry const *spellProto, uint32 effIndex, uint32 damage, Aura* triggredByAura, SpellEntry const * procSpell, uint32 procFlag,uint32 cooldown);
- bool HandleProcTriggerSpell(Unit *pVictim,uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlags,WeaponAttackType attType,uint32 cooldown);
- bool HandleHasteAuraProc(Unit *pVictim, SpellEntry const *spellProto, uint32 effIndex, uint32 damage, Aura* triggredByAura, SpellEntry const * procSpell, uint32 procFlag,uint32 cooldown);
- bool HandleOverrideClassScriptAuraProc(Unit *pVictim, int32 scriptId, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell,uint32 cooldown);
- uint32 m_state; // Even derived shouldn't modify
- uint32 m_CombatTimer;
- uint32 m_lastManaUse; // msecs
-
- UnitVisibility m_Visibility;
-
- Diminishing m_Diminishing;
- // Manage all Units threatening us
- ThreatManager m_ThreatManager;
- // Manage all Units that are threatened by us
- HostilRefManager m_HostilRefManager;
-
- FollowerRefManager m_FollowingRefManager;
-
- ComboPointHolderSet m_ComboPointHolders;
-};
-#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
+ */
+
+#ifndef __UNIT_H
+#define __UNIT_H
+
+#include "Common.h"
+#include "Object.h"
+#include "Opcodes.h"
+#include "Mthread.h"
+#include "SpellAuraDefines.h"
+#include "UpdateFields.h"
+#include "SharedDefines.h"
+#include "ThreatManager.h"
+#include "HostilRefManager.h"
+#include "FollowerReference.h"
+#include "FollowerRefManager.h"
+#include "Utilities/EventProcessor.h"
+#include "MotionMaster.h"
+#include "Database/DBCStructure.h"
+#include <list>
+
+enum SpellInterruptFlags
+{
+ SPELL_INTERRUPT_FLAG_MOVEMENT = 0x01,
+ SPELL_INTERRUPT_FLAG_DAMAGE = 0x02,
+ SPELL_INTERRUPT_FLAG_INTERRUPT = 0x04,
+ SPELL_INTERRUPT_FLAG_AUTOATTACK = 0x08,
+ //SPELL_INTERRUPT_FLAG_TURNING = 0x10 // not turning - maybe _complete_ interrupt on direct damage?
+};
+
+enum SpellChannelInterruptFlags
+{
+ CHANNEL_FLAG_DAMAGE = 0x0002,
+ CHANNEL_FLAG_MOVEMENT = 0x0008,
+ CHANNEL_FLAG_TURNING = 0x0010,
+ CHANNEL_FLAG_DAMAGE2 = 0x0080,
+ CHANNEL_FLAG_DELAY = 0x4000
+};
+
+enum SpellAuraInterruptFlags
+{
+ AURA_INTERRUPT_FLAG_UNK0 = 0x00000001, // 0 removed when getting hit by a negative spell?
+ AURA_INTERRUPT_FLAG_DAMAGE = 0x00000002, // 1 removed by any damage
+ AURA_INTERRUPT_FLAG_UNK2 = 0x00000004, // 2
+ AURA_INTERRUPT_FLAG_MOVE = 0x00000008, // 3 removed by any movement
+ AURA_INTERRUPT_FLAG_TURNING = 0x00000010, // 4 removed by any turning
+ AURA_INTERRUPT_FLAG_ENTER_COMBAT = 0x00000020, // 5 removed by entering combat
+ AURA_INTERRUPT_FLAG_NOT_MOUNTED = 0x00000040, // 6 removed by unmounting
+ AURA_INTERRUPT_FLAG_NOT_ABOVEWATER = 0x00000080, // 7 removed by entering water
+ AURA_INTERRUPT_FLAG_NOT_UNDERWATER = 0x00000100, // 8 removed by leaving water
+ AURA_INTERRUPT_FLAG_NOT_SHEATHED = 0x00000200, // 9 removed by unsheathing
+ AURA_INTERRUPT_FLAG_UNK10 = 0x00000400, // 10
+ AURA_INTERRUPT_FLAG_UNK11 = 0x00000800, // 11
+ AURA_INTERRUPT_FLAG_UNK12 = 0x00001000, // 12 removed by attack?
+ AURA_INTERRUPT_FLAG_UNK13 = 0x00002000, // 13
+ AURA_INTERRUPT_FLAG_UNK14 = 0x00004000, // 14
+ AURA_INTERRUPT_FLAG_UNK15 = 0x00008000, // 15 removed by casting a spell?
+ AURA_INTERRUPT_FLAG_UNK16 = 0x00010000, // 16
+ AURA_INTERRUPT_FLAG_MOUNTING = 0x00020000, // 17 removed by mounting
+ AURA_INTERRUPT_FLAG_NOT_SEATED = 0x00040000, // 18 removed by standing up
+ AURA_INTERRUPT_FLAG_CHANGE_MAP = 0x00080000, // 19 leaving map/getting teleported
+ AURA_INTERRUPT_FLAG_UNK20 = 0x00100000, // 20
+ AURA_INTERRUPT_FLAG_UNK21 = 0x00200000, // 21
+ AURA_INTERRUPT_FLAG_UNK22 = 0x00400000, // 22
+ AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT = 0x00800000, // 23 removed by entering pvp combat
+ AURA_INTERRUPT_FLAG_DIRECT_DAMAGE = 0x01000000 // 24 removed by any direct damage
+};
+
+enum SpellModOp
+{
+ SPELLMOD_DAMAGE = 0,
+ SPELLMOD_DURATION = 1,
+ SPELLMOD_THREAT = 2,
+ SPELLMOD_EFFECT1 = 3,
+ SPELLMOD_CHARGES = 4,
+ SPELLMOD_RANGE = 5,
+ SPELLMOD_RADIUS = 6,
+ SPELLMOD_CRITICAL_CHANCE = 7,
+ SPELLMOD_ALL_EFFECTS = 8,
+ SPELLMOD_NOT_LOSE_CASTING_TIME = 9,
+ SPELLMOD_CASTING_TIME = 10,
+ SPELLMOD_COOLDOWN = 11,
+ SPELLMOD_EFFECT2 = 12,
+ // spellmod 13 unused
+ SPELLMOD_COST = 14,
+ SPELLMOD_CRIT_DAMAGE_BONUS = 15,
+ SPELLMOD_RESIST_MISS_CHANCE = 16,
+ SPELLMOD_JUMP_TARGETS = 17,
+ SPELLMOD_CHANCE_OF_SUCCESS = 18,
+ SPELLMOD_ACTIVATION_TIME = 19,
+ SPELLMOD_EFFECT_PAST_FIRST = 20,
+ SPELLMOD_CASTING_TIME_OLD = 21,
+ SPELLMOD_DOT = 22,
+ SPELLMOD_EFFECT3 = 23,
+ SPELLMOD_SPELL_BONUS_DAMAGE = 24,
+ // spellmod 25, 26 unused
+ SPELLMOD_MULTIPLE_VALUE = 27,
+ SPELLMOD_RESIST_DISPEL_CHANCE = 28
+};
+
+#define MAX_SPELLMOD 32
+
+enum SpellFacingFlags
+{
+ SPELL_FACING_FLAG_INFRONT = 0x0001
+};
+
+#define BASE_MINDAMAGE 1.0f
+#define BASE_MAXDAMAGE 2.0f
+#define BASE_ATTACK_TIME 2000
+
+// high byte (3 from 0..3) of UNIT_FIELD_BYTES_2
+enum ShapeshiftForm
+{
+ FORM_NONE = 0x00,
+ FORM_CAT = 0x01,
+ FORM_TREE = 0x02,
+ FORM_TRAVEL = 0x03,
+ FORM_AQUA = 0x04,
+ FORM_BEAR = 0x05,
+ FORM_AMBIENT = 0x06,
+ FORM_GHOUL = 0x07,
+ FORM_DIREBEAR = 0x08,
+ FORM_CREATUREBEAR = 0x0E,
+ FORM_CREATURECAT = 0x0F,
+ FORM_GHOSTWOLF = 0x10,
+ FORM_BATTLESTANCE = 0x11,
+ FORM_DEFENSIVESTANCE = 0x12,
+ FORM_BERSERKERSTANCE = 0x13,
+ FORM_TEST = 0x14,
+ FORM_ZOMBIE = 0x15,
+ FORM_FLIGHT_EPIC = 0x1B,
+ FORM_SHADOW = 0x1C,
+ FORM_FLIGHT = 0x1D,
+ FORM_STEALTH = 0x1E,
+ FORM_MOONKIN = 0x1F,
+ FORM_SPIRITOFREDEMPTION = 0x20
+};
+
+// low byte ( 0 from 0..3 ) of UNIT_FIELD_BYTES_2
+enum SheathState
+{
+ SHEATH_STATE_UNARMED = 0, // non prepared weapon
+ SHEATH_STATE_MELEE = 1, // prepared melee weapon
+ SHEATH_STATE_RANGED = 2 // prepared ranged weapon
+};
+
+// byte (1 from 0..3) of UNIT_FIELD_BYTES_2
+enum UnitBytes2_Flags
+{
+ UNIT_BYTE2_FLAG_UNK0 = 0x01,
+ UNIT_BYTE2_FLAG_UNK1 = 0x02,
+ UNIT_BYTE2_FLAG_UNK2 = 0x04,
+ UNIT_BYTE2_FLAG_UNK3 = 0x08,
+ UNIT_BYTE2_FLAG_AURAS = 0x10, // show possitive auras as positive, and allow its dispel
+ UNIT_BYTE2_FLAG_UNK5 = 0x20,
+ UNIT_BYTE2_FLAG_UNK6 = 0x40,
+ UNIT_BYTE2_FLAG_UNK7 = 0x80
+};
+
+// byte (2 from 0..3) of UNIT_FIELD_BYTES_2
+enum UnitRename
+{
+ UNIT_RENAME_NOT_ALLOWED = 0x02,
+ UNIT_RENAME_ALLOWED = 0x03
+};
+
+#define CREATURE_MAX_SPELLS 4
+
+enum Swing
+{
+ NOSWING = 0,
+ SINGLEHANDEDSWING = 1,
+ TWOHANDEDSWING = 2
+};
+
+enum VictimState
+{
+ VICTIMSTATE_UNKNOWN1 = 0,
+ VICTIMSTATE_NORMAL = 1,
+ VICTIMSTATE_DODGE = 2,
+ VICTIMSTATE_PARRY = 3,
+ VICTIMSTATE_INTERRUPT = 4,
+ VICTIMSTATE_BLOCKS = 5,
+ VICTIMSTATE_EVADES = 6,
+ VICTIMSTATE_IS_IMMUNE = 7,
+ VICTIMSTATE_DEFLECTS = 8
+};
+
+enum HitInfo
+{
+ HITINFO_NORMALSWING = 0x00000000,
+ HITINFO_UNK1 = 0x00000001, // req correct packet structure
+ HITINFO_NORMALSWING2 = 0x00000002,
+ HITINFO_LEFTSWING = 0x00000004,
+ HITINFO_MISS = 0x00000010,
+ HITINFO_ABSORB = 0x00000020, // plays absorb sound
+ HITINFO_RESIST = 0x00000040, // resisted atleast some damage
+ HITINFO_CRITICALHIT = 0x00000080,
+ HITINFO_GLANCING = 0x00004000,
+ HITINFO_CRUSHING = 0x00008000,
+ HITINFO_NOACTION = 0x00010000,
+ HITINFO_SWINGNOHITSOUND = 0x00080000
+};
+
+//i would like to remove this: (it is defined in item.h
+enum InventorySlot
+{
+ NULL_BAG = 0,
+ NULL_SLOT = 255
+};
+
+struct FactionTemplateEntry;
+struct Modifier;
+struct SpellEntry;
+struct SpellEntryExt;
+
+class Aura;
+class Creature;
+class Spell;
+class DynamicObject;
+class GameObject;
+class Item;
+class Pet;
+class Path;
+class PetAura;
+
+struct SpellImmune
+{
+ uint32 type;
+ uint32 spellId;
+};
+
+typedef std::list<SpellImmune> SpellImmuneList;
+
+enum UnitModifierType
+{
+ BASE_VALUE = 0,
+ BASE_PCT = 1,
+ TOTAL_VALUE = 2,
+ TOTAL_PCT = 3,
+ MODIFIER_TYPE_END = 4
+};
+
+enum WeaponDamageRange
+{
+ MINDAMAGE,
+ MAXDAMAGE
+};
+
+enum DamageTypeToSchool
+{
+ RESISTANCE,
+ DAMAGE_DEALT,
+ DAMAGE_TAKEN
+};
+
+enum AuraRemoveMode
+{
+ AURA_REMOVE_BY_DEFAULT,
+ AURA_REMOVE_BY_STACK, // at replace by semillar aura
+ AURA_REMOVE_BY_CANCEL,
+ AURA_REMOVE_BY_DISPEL,
+ AURA_REMOVE_BY_DEATH
+};
+
+enum UnitMods
+{
+ UNIT_MOD_STAT_STRENGTH, // UNIT_MOD_STAT_STRENGTH..UNIT_MOD_STAT_SPIRIT must be in existed order, it's accessed by index values of Stats enum.
+ UNIT_MOD_STAT_AGILITY,
+ UNIT_MOD_STAT_STAMINA,
+ UNIT_MOD_STAT_INTELLECT,
+ UNIT_MOD_STAT_SPIRIT,
+ UNIT_MOD_HEALTH,
+ UNIT_MOD_MANA, // UNIT_MOD_MANA..UNIT_MOD_HAPPINESS must be in existed order, it's accessed by index values of Powers enum.
+ UNIT_MOD_RAGE,
+ UNIT_MOD_FOCUS,
+ UNIT_MOD_ENERGY,
+ UNIT_MOD_HAPPINESS,
+ UNIT_MOD_ARMOR, // UNIT_MOD_ARMOR..UNIT_MOD_RESISTANCE_ARCANE must be in existed order, it's accessed by index values of SpellSchools enum.
+ UNIT_MOD_RESISTANCE_HOLY,
+ UNIT_MOD_RESISTANCE_FIRE,
+ UNIT_MOD_RESISTANCE_NATURE,
+ UNIT_MOD_RESISTANCE_FROST,
+ UNIT_MOD_RESISTANCE_SHADOW,
+ UNIT_MOD_RESISTANCE_ARCANE,
+ UNIT_MOD_ATTACK_POWER,
+ UNIT_MOD_ATTACK_POWER_RANGED,
+ UNIT_MOD_DAMAGE_MAINHAND,
+ UNIT_MOD_DAMAGE_OFFHAND,
+ UNIT_MOD_DAMAGE_RANGED,
+ UNIT_MOD_END,
+ // synonyms
+ UNIT_MOD_STAT_START = UNIT_MOD_STAT_STRENGTH,
+ UNIT_MOD_STAT_END = UNIT_MOD_STAT_SPIRIT + 1,
+ UNIT_MOD_RESISTANCE_START = UNIT_MOD_ARMOR,
+ UNIT_MOD_RESISTANCE_END = UNIT_MOD_RESISTANCE_ARCANE + 1,
+ UNIT_MOD_POWER_START = UNIT_MOD_MANA,
+ UNIT_MOD_POWER_END = UNIT_MOD_HAPPINESS + 1
+};
+
+enum BaseModGroup
+{
+ CRIT_PERCENTAGE,
+ RANGED_CRIT_PERCENTAGE,
+ OFFHAND_CRIT_PERCENTAGE,
+ SHIELD_BLOCK_VALUE,
+ BASEMOD_END
+};
+
+enum BaseModType
+{
+ FLAT_MOD,
+ PCT_MOD
+};
+
+#define MOD_END (PCT_MOD+1)
+
+enum DeathState
+{
+ ALIVE = 0,
+ JUST_DIED = 1,
+ CORPSE = 2,
+ DEAD = 3,
+ JUST_ALIVED = 4
+};
+
+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_STUNNED = 0x0008,
+ UNIT_STAT_ROAMING = 0x0010,
+ UNIT_STAT_CHASE = 0x0020,
+ UNIT_STAT_SEARCHING = 0x0040,
+ UNIT_STAT_FLEEING = 0x0080,
+ UNIT_STAT_MOVING = (UNIT_STAT_ROAMING | UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING),
+ UNIT_STAT_IN_FLIGHT = 0x0100, // player is in flight mode
+ UNIT_STAT_FOLLOW = 0x0200,
+ UNIT_STAT_ROOT = 0x0400,
+ UNIT_STAT_CONFUSED = 0x0800,
+ UNIT_STAT_DISTRACTED = 0x1000,
+ UNIT_STAT_ISOLATED = 0x2000, // area auras do not affect other players
+ UNIT_STAT_ATTACK_PLAYER = 0x4000,
+ UNIT_STAT_ALL_STATE = 0xffff //(UNIT_STAT_STOPPED | UNIT_STAT_MOVING | UNIT_STAT_IN_COMBAT | UNIT_STAT_IN_FLIGHT)
+};
+
+enum UnitMoveType
+{
+ MOVE_WALK = 0,
+ MOVE_RUN = 1,
+ MOVE_WALKBACK = 2,
+ MOVE_SWIM = 3,
+ MOVE_SWIMBACK = 4,
+ MOVE_TURN = 5,
+ MOVE_FLY = 6,
+ MOVE_FLYBACK = 7
+};
+
+#define MAX_MOVE_TYPE 8
+
+extern float baseMoveSpeed[MAX_MOVE_TYPE];
+
+enum WeaponAttackType
+{
+ BASE_ATTACK = 0,
+ OFF_ATTACK = 1,
+ RANGED_ATTACK = 2
+};
+
+#define MAX_ATTACK 3
+
+enum CombatRating
+{
+ CR_WEAPON_SKILL = 0,
+ CR_DEFENSE_SKILL = 1,
+ CR_DODGE = 2,
+ CR_PARRY = 3,
+ CR_BLOCK = 4,
+ CR_HIT_MELEE = 5,
+ CR_HIT_RANGED = 6,
+ CR_HIT_SPELL = 7,
+ CR_CRIT_MELEE = 8,
+ CR_CRIT_RANGED = 9,
+ CR_CRIT_SPELL = 10,
+ CR_HIT_TAKEN_MELEE = 11,
+ CR_HIT_TAKEN_RANGED = 12,
+ CR_HIT_TAKEN_SPELL = 13,
+ CR_CRIT_TAKEN_MELEE = 14,
+ CR_CRIT_TAKEN_RANGED = 15,
+ CR_CRIT_TAKEN_SPELL = 16,
+ CR_HASTE_MELEE = 17,
+ CR_HASTE_RANGED = 18,
+ CR_HASTE_SPELL = 19,
+ CR_WEAPON_SKILL_MAINHAND = 20,
+ CR_WEAPON_SKILL_OFFHAND = 21,
+ CR_WEAPON_SKILL_RANGED = 22,
+ CR_EXPERTISE = 23
+};
+
+#define MAX_COMBAT_RATING 24
+
+enum DamageEffectType
+{
+ DIRECT_DAMAGE = 0, // used for normal weapon damage (not for class abilities or spells)
+ SPELL_DIRECT_DAMAGE = 1, // spell/class abilities damage
+ DOT = 2,
+ HEAL = 3,
+ NODAMAGE = 4, // used also in case when damage applied to health but not applied to spell channelInterruptFlags/etc
+ SELF_DAMAGE = 5
+};
+
+enum UnitVisibility
+{
+ VISIBILITY_OFF = 0, // absolute, not detectable, GM-like, can see all other
+ VISIBILITY_ON = 1,
+ VISIBILITY_GROUP_STEALTH = 2, // detect chance, seen and can see group members
+ VISIBILITY_GROUP_INVISIBILITY = 3, // invisibility, can see and can be seen only another invisible unit or invisible detection unit, set only if not stealthed, and in checks not used (mask used instead)
+ VISIBILITY_GROUP_NO_DETECT = 4, // state just at stealth apply for update Grid state. Don't remove, otherwise stealth spells will break
+ VISIBILITY_RESPAWN = 5 // special totally not detectable visibility for force delete object at respawn command
+};
+
+// Value masks for UNIT_FIELD_FLAGS
+enum UnitFlags
+{
+ UNIT_FLAG_UNKNOWN7 = 0x00000001,
+ UNIT_FLAG_NON_ATTACKABLE = 0x00000002, // not attackable
+ UNIT_FLAG_DISABLE_MOVE = 0x00000004,
+ UNIT_FLAG_PVP_ATTACKABLE = 0x00000008, // allow apply pvp rules to attackable state in addition to faction dependent state
+ UNIT_FLAG_RENAME = 0x00000010,
+ UNIT_FLAG_PREPARATION = 0x00000020, // don't take reagents for spells with SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP
+ UNIT_FLAG_UNKNOWN9 = 0x00000040,
+ UNIT_FLAG_NOT_ATTACKABLE_1 = 0x00000080, // ?? (UNIT_FLAG_PVP_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1) is NON_PVP_ATTACKABLE
+ UNIT_FLAG_UNKNOWN2 = 0x00000100, // 2.0.8
+ UNIT_FLAG_UNKNOWN11 = 0x00000200,
+ UNIT_FLAG_LOOTING = 0x00000400, // loot animation
+ UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8
+ UNIT_FLAG_PVP = 0x00001000,
+ UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1
+ UNIT_FLAG_UNKNOWN4 = 0x00004000, // 2.0.8
+ UNIT_FLAG_UNKNOWN13 = 0x00008000,
+ UNIT_FLAG_UNKNOWN14 = 0x00010000,
+ UNIT_FLAG_PACIFIED = 0x00020000,
+ UNIT_FLAG_DISABLE_ROTATE = 0x00040000, // stunned, 2.1.1
+ UNIT_FLAG_IN_COMBAT = 0x00080000,
+ UNIT_FLAG_TAXI_FLIGHT = 0x00100000, // disable casting at client side spell not allowed by taxi flight (mounted?), probably used with 0x4 flag
+ UNIT_FLAG_DISARMED = 0x00200000, // disable melee spells casting..., "Required melee weapon" added to melee spells tooltip.
+ UNIT_FLAG_CONFUSED = 0x00400000,
+ UNIT_FLAG_FLEEING = 0x00800000,
+ UNIT_FLAG_UNKNOWN5 = 0x01000000, // used in spell Eyes of the Beast for pet...
+ UNIT_FLAG_NOT_SELECTABLE = 0x02000000,
+ UNIT_FLAG_SKINNABLE = 0x04000000,
+ UNIT_FLAG_MOUNT = 0x08000000,
+ UNIT_FLAG_UNKNOWN17 = 0x10000000,
+ UNIT_FLAG_UNKNOWN6 = 0x20000000, // used in Feing Death spell
+ UNIT_FLAG_SHEATHE = 0x40000000
+};
+
+// Value masks for UNIT_FIELD_FLAGS_2
+enum UnitFlags2
+{
+ UNIT_FLAG2_FEIGN_DEATH = 0x00000001,
+ UNIT_FLAG2_COMPREHEND_LANG= 0x00000008,
+ UNIT_FLAG2_FORCE_MOVE = 0x00000040
+};
+
+/// Non Player Character flags
+enum NPCFlags
+{
+ UNIT_NPC_FLAG_NONE = 0x00000000,
+ UNIT_NPC_FLAG_GOSSIP = 0x00000001, // 100%
+ UNIT_NPC_FLAG_QUESTGIVER = 0x00000002, // guessed, probably ok
+ UNIT_NPC_FLAG_UNK1 = 0x00000004,
+ UNIT_NPC_FLAG_UNK2 = 0x00000008,
+ UNIT_NPC_FLAG_TRAINER = 0x00000010, // 100%
+ UNIT_NPC_FLAG_TRAINER_CLASS = 0x00000020, // 100%
+ UNIT_NPC_FLAG_TRAINER_PROFESSION = 0x00000040, // 100%
+ UNIT_NPC_FLAG_VENDOR = 0x00000080, // 100%
+ UNIT_NPC_FLAG_VENDOR_AMMO = 0x00000100, // 100%, general goods vendor
+ UNIT_NPC_FLAG_VENDOR_FOOD = 0x00000200, // 100%
+ UNIT_NPC_FLAG_VENDOR_POISON = 0x00000400, // guessed
+ UNIT_NPC_FLAG_VENDOR_REAGENT = 0x00000800, // 100%
+ UNIT_NPC_FLAG_REPAIR = 0x00001000, // 100%
+ UNIT_NPC_FLAG_FLIGHTMASTER = 0x00002000, // 100%
+ UNIT_NPC_FLAG_SPIRITHEALER = 0x00004000, // guessed
+ UNIT_NPC_FLAG_SPIRITGUIDE = 0x00008000, // guessed
+ UNIT_NPC_FLAG_INNKEEPER = 0x00010000, // 100%
+ UNIT_NPC_FLAG_BANKER = 0x00020000, // 100%
+ UNIT_NPC_FLAG_PETITIONER = 0x00040000, // 100% 0xC0000 = guild petitions, 0x40000 = arena team petitions
+ UNIT_NPC_FLAG_TABARDDESIGNER = 0x00080000, // 100%
+ UNIT_NPC_FLAG_BATTLEMASTER = 0x00100000, // 100%
+ UNIT_NPC_FLAG_AUCTIONEER = 0x00200000, // 100%
+ UNIT_NPC_FLAG_STABLEMASTER = 0x00400000, // 100%
+ UNIT_NPC_FLAG_GUILD_BANKER = 0x00800000, // cause client to send 997 opcode
+ UNIT_NPC_FLAG_UNK3 = 0x01000000, // cause client to send 1015 opcode
+ UNIT_NPC_FLAG_GUARD = 0x10000000, // custom flag for guards
+};
+
+enum MovementFlags
+{
+ MOVEMENTFLAG_NONE = 0x00000000,
+ MOVEMENTFLAG_FORWARD = 0x00000001,
+ MOVEMENTFLAG_BACKWARD = 0x00000002,
+ MOVEMENTFLAG_STRAFE_LEFT = 0x00000004,
+ MOVEMENTFLAG_STRAFE_RIGHT = 0x00000008,
+ MOVEMENTFLAG_LEFT = 0x00000010,
+ MOVEMENTFLAG_RIGHT = 0x00000020,
+ MOVEMENTFLAG_PITCH_UP = 0x00000040,
+ MOVEMENTFLAG_PITCH_DOWN = 0x00000080,
+ MOVEMENTFLAG_WALK_MODE = 0x00000100, // Walking
+ MOVEMENTFLAG_ONTRANSPORT = 0x00000200, // Used for flying on some creatures
+ MOVEMENTFLAG_LEVITATING = 0x00000400,
+ MOVEMENTFLAG_FLY_UNK1 = 0x00000800,
+ MOVEMENTFLAG_JUMPING = 0x00001000,
+ MOVEMENTFLAG_UNK4 = 0x00002000,
+ MOVEMENTFLAG_FALLING = 0x00004000,
+ // 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 0x100000
+ MOVEMENTFLAG_SWIMMING = 0x00200000, // appears with fly flag also
+ MOVEMENTFLAG_FLY_UP = 0x00400000,
+ MOVEMENTFLAG_CAN_FLY = 0x00800000,
+ MOVEMENTFLAG_FLYING = 0x01000000,
+ MOVEMENTFLAG_FLYING2 = 0x02000000, // Actual flying mode
+ MOVEMENTFLAG_SPLINE = 0x04000000, // used for flight paths
+ MOVEMENTFLAG_SPLINE2 = 0x08000000, // used for flight paths
+ MOVEMENTFLAG_WATERWALKING = 0x10000000, // prevent unit from falling through water
+ MOVEMENTFLAG_SAFE_FALL = 0x20000000, // active rogue safe fall spell (passive)
+ MOVEMENTFLAG_UNK3 = 0x40000000
+};
+
+enum DiminishingLevels
+{
+ DIMINISHING_LEVEL_1 = 0,
+ DIMINISHING_LEVEL_2 = 1,
+ DIMINISHING_LEVEL_3 = 2,
+ DIMINISHING_LEVEL_IMMUNE = 3
+};
+
+struct DiminishingReturn
+{
+ DiminishingReturn(DiminishingGroup group, uint32 t, uint32 count) : DRGroup(group), hitTime(t), hitCount(count), stack(0) {}
+
+ DiminishingGroup DRGroup:16;
+ uint16 stack:16;
+ uint32 hitTime;
+ uint32 hitCount;
+};
+
+enum MeleeHitOutcome
+{
+ MELEE_HIT_EVADE, MELEE_HIT_MISS, MELEE_HIT_DODGE, MELEE_HIT_BLOCK, MELEE_HIT_PARRY,
+ MELEE_HIT_GLANCING, MELEE_HIT_CRIT, MELEE_HIT_CRUSHING, MELEE_HIT_NORMAL, MELEE_HIT_BLOCK_CRIT
+};
+struct CleanDamage
+{
+ CleanDamage(uint32 _damage, WeaponAttackType _attackType, MeleeHitOutcome _hitOutCome) :
+ damage(_damage), attackType(_attackType), hitOutCome(_hitOutCome) {}
+
+ uint32 damage;
+ WeaponAttackType attackType;
+ MeleeHitOutcome hitOutCome;
+};
+
+struct UnitActionBarEntry
+{
+ uint32 Type;
+ uint32 SpellOrAction;
+};
+
+#define MAX_DECLINED_NAME_CASES 5
+
+struct DeclinedName
+{
+ std::string name[MAX_DECLINED_NAME_CASES];
+};
+
+enum CurrentSpellTypes
+{
+ CURRENT_MELEE_SPELL = 0,
+ CURRENT_FIRST_NON_MELEE_SPELL = 1, // just counter
+ CURRENT_GENERIC_SPELL = 1,
+ CURRENT_AUTOREPEAT_SPELL = 2,
+ CURRENT_CHANNELED_SPELL = 3,
+ CURRENT_MAX_SPELL = 4 // just counter
+};
+
+enum ActiveStates
+{
+ ACT_ENABLED = 0xC100,
+ ACT_DISABLED = 0x8100,
+ ACT_COMMAND = 0x0700,
+ ACT_REACTION = 0x0600,
+ ACT_CAST = 0x0100,
+ ACT_PASSIVE = 0x0000,
+ ACT_DECIDE = 0x0001
+};
+
+enum ReactStates
+{
+ REACT_PASSIVE = 0,
+ REACT_DEFENSIVE = 1,
+ REACT_AGGRESSIVE = 2
+};
+
+enum CommandStates
+{
+ COMMAND_STAY = 0,
+ COMMAND_FOLLOW = 1,
+ COMMAND_ATTACK = 2,
+ COMMAND_ABANDON = 3
+};
+
+struct CharmSpellEntry
+{
+ uint16 spellId;
+ uint16 active;
+};
+
+struct CharmInfo
+{
+ public:
+ explicit CharmInfo(Unit* unit);
+ uint32 GetPetNumber() const { return m_petnumber; }
+ void SetPetNumber(uint32 petnumber, bool statwindow);
+
+ void SetCommandState(CommandStates st) { m_CommandState = st; }
+ CommandStates GetCommandState() { return m_CommandState; }
+ bool HasCommandState(CommandStates state) { return (m_CommandState == state); }
+ void SetReactState(ReactStates st) { m_ReactSate = st; }
+ ReactStates GetReactState() { return m_ReactSate; }
+ bool HasReactState(ReactStates state) { return (m_ReactSate == state); }
+
+ void InitPossessCreateSpells();
+ void InitCharmCreateSpells();
+ void InitPetActionBar();
+ void InitEmptyActionBar();
+ //return true if successful
+ bool AddSpellToAB(uint32 oldid, uint32 newid, ActiveStates newstate = ACT_DECIDE);
+ void ToggleCreatureAutocast(uint32 spellid, bool apply);
+
+ UnitActionBarEntry* GetActionBarEntry(uint8 index) { return &(PetActionBar[index]); }
+ CharmSpellEntry* GetCharmSpell(uint8 index) { return &(m_charmspells[index]); }
+ private:
+ Unit* m_unit;
+ UnitActionBarEntry PetActionBar[10];
+ CharmSpellEntry m_charmspells[4];
+ CommandStates m_CommandState;
+ ReactStates m_ReactSate;
+ uint32 m_petnumber;
+};
+
+// for clearing special attacks
+#define REACTIVE_TIMER_START 4000
+
+enum ReactiveType
+{
+ REACTIVE_DEFENSE = 1,
+ REACTIVE_HUNTER_PARRY = 2,
+ REACTIVE_CRIT = 3,
+ REACTIVE_HUNTER_CRIT = 4,
+ REACTIVE_OVERPOWER = 5
+};
+
+#define MAX_REACTIVE 6
+#define MAX_TOTEM 4
+
+// delay time next attack to prevent client attack animation problems
+#define ATTACK_DISPLAY_DELAY 200
+
+class MANGOS_DLL_SPEC Unit : public WorldObject
+{
+ public:
+ typedef std::set<Unit*> AttackerSet;
+ typedef std::pair<uint32, uint8> spellEffectPair;
+ typedef std::multimap< spellEffectPair, Aura*> AuraMap;
+ typedef std::list<Aura *> AuraList;
+ typedef std::list<DiminishingReturn> Diminishing;
+ typedef std::set<AuraType> AuraTypeSet;
+ typedef std::set<uint32> ComboPointHolderSet;
+
+ virtual ~Unit ( );
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ void CleanupsBeforeDelete(); // used in ~Creature/~Player (or before mass creature delete to remove cross-references to already deleted units)
+
+ DiminishingLevels GetDiminishing(DiminishingGroup group);
+ void IncrDiminishing(DiminishingGroup group);
+ void ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster, DiminishingLevels Level);
+ void ApplyDiminishingAura(DiminishingGroup group, bool apply);
+ void ClearDiminishings() { m_Diminishing.clear(); }
+
+ virtual void Update( uint32 time );
+
+ void setAttackTimer(WeaponAttackType type, uint32 time) { m_attackTimer[type] = time; }
+ void resetAttackTimer(WeaponAttackType type = BASE_ATTACK);
+ uint32 getAttackTimer(WeaponAttackType type) const { return m_attackTimer[type]; }
+ bool isAttackReady(WeaponAttackType type = BASE_ATTACK) const { return m_attackTimer[type] == 0; }
+ bool haveOffhandWeapon() const;
+ bool canReachWithAttack(Unit *pVictim) const;
+ uint32 m_extraAttacks;
+
+ void _addAttacker(Unit *pAttacker) // must be called only from Unit::Attack(Unit*)
+ {
+ AttackerSet::iterator itr = m_attackers.find(pAttacker);
+ if(itr == m_attackers.end())
+ m_attackers.insert(pAttacker);
+ }
+ void _removeAttacker(Unit *pAttacker) // must be called only from Unit::AttackStop()
+ {
+ AttackerSet::iterator itr = m_attackers.find(pAttacker);
+ if(itr != m_attackers.end())
+ m_attackers.erase(itr);
+ }
+ Unit * getAttackerForHelper() // If someone wants to help, who to give them
+ {
+ if (getVictim() != NULL)
+ return getVictim();
+
+ if (!m_attackers.empty())
+ return *(m_attackers.begin());
+
+ return NULL;
+ }
+ bool Attack(Unit *victim, bool meleeAttack);
+ void CastStop(uint32 except_spellid = 0);
+ bool AttackStop();
+ void RemoveAllAttackers();
+ AttackerSet const& getAttackers() const { return m_attackers; }
+ bool isAttackingPlayer() const;
+ Unit* getVictim() const { return m_attacking; }
+ void CombatStop(bool cast = false);
+ void CombatStopWithPets(bool cast = false);
+ Unit* SelectNearbyTarget() const;
+
+ void addUnitState(uint32 f) { m_state |= f; }
+ bool hasUnitState(const uint32 f) const { return (m_state & f); }
+ void clearUnitState(uint32 f) { m_state &= ~f; }
+ bool CanFreeMove() const
+ {
+ return !hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_FLEEING | UNIT_STAT_IN_FLIGHT |
+ UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED ) && GetOwnerGUID()==0;
+ }
+
+ uint32 getLevel() const { return GetUInt32Value(UNIT_FIELD_LEVEL); }
+ virtual uint32 getLevelForTarget(Unit const* /*target*/) const { return getLevel(); }
+ void SetLevel(uint32 lvl);
+ uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, 0); }
+ uint32 getRaceMask() const { return 1 << (getRace()-1); }
+ uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, 1); }
+ uint32 getClassMask() const { return 1 << (getClass()-1); }
+ uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, 2); }
+
+ float GetStat(Stats stat) const { return float(GetUInt32Value(UNIT_FIELD_STAT0+stat)); }
+ void SetStat(Stats stat, int32 val) { SetStatInt32Value(UNIT_FIELD_STAT0+stat, val); }
+ uint32 GetArmor() const { return GetResistance(SPELL_SCHOOL_NORMAL) ; }
+ void SetArmor(int32 val) { SetResistance(SPELL_SCHOOL_NORMAL, val); }
+
+ uint32 GetResistance(SpellSchools school) const { return GetUInt32Value(UNIT_FIELD_RESISTANCES+school); }
+ void SetResistance(SpellSchools school, int32 val) { SetStatInt32Value(UNIT_FIELD_RESISTANCES+school,val); }
+
+ uint32 GetHealth() const { return GetUInt32Value(UNIT_FIELD_HEALTH); }
+ uint32 GetMaxHealth() const { return GetUInt32Value(UNIT_FIELD_MAXHEALTH); }
+ void SetHealth( uint32 val);
+ void SetMaxHealth(uint32 val);
+ int32 ModifyHealth(int32 val);
+
+ Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, 3)); }
+ void setPowerType(Powers power);
+ uint32 GetPower( Powers power) const { return GetUInt32Value(UNIT_FIELD_POWER1 +power); }
+ uint32 GetMaxPower(Powers power) const { return GetUInt32Value(UNIT_FIELD_MAXPOWER1+power); }
+ void SetPower( Powers power, uint32 val);
+ void SetMaxPower(Powers power, uint32 val);
+ int32 ModifyPower(Powers power, int32 val);
+ void ApplyPowerMod(Powers power, uint32 val, bool apply);
+ void ApplyMaxPowerMod(Powers power, uint32 val, bool apply);
+
+ uint32 GetAttackTime(WeaponAttackType att) const { return (uint32)(GetFloatValue(UNIT_FIELD_BASEATTACKTIME+att)/m_modAttackSpeedPct[att]); }
+ void SetAttackTime(WeaponAttackType att, uint32 val) { SetFloatValue(UNIT_FIELD_BASEATTACKTIME+att,val*m_modAttackSpeedPct[att]); }
+ void ApplyAttackTimePercentMod(WeaponAttackType att,float val, bool apply);
+ void ApplyCastTimePercentMod(float val, bool apply);
+
+ // faction template id
+ uint32 getFaction() const { return GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); }
+ void setFaction(uint32 faction) { SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, faction ); }
+ FactionTemplateEntry const* getFactionTemplateEntry() const;
+ bool IsHostileTo(Unit const* unit) const;
+ bool IsHostileToPlayers() const;
+ bool IsFriendlyTo(Unit const* unit) const;
+ bool IsNeutralToAll() const;
+ bool IsContestedGuard() const
+ {
+ if(FactionTemplateEntry const* entry = getFactionTemplateEntry())
+ return entry->IsContestedGuardFaction();
+
+ return false;
+ }
+ bool IsPvP() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); }
+ void SetPvP(bool state) { if(state) SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); else RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); }
+ uint32 GetCreatureType() const;
+ uint32 GetCreatureTypeMask() const
+ {
+ uint32 creatureType = GetCreatureType();
+ return (creatureType >= 1) ? (1 << (creatureType - 1)) : 0;
+ }
+
+ uint8 getStandState() const { return GetByteValue(UNIT_FIELD_BYTES_1, 0); }
+ bool IsSitState() const;
+ bool IsStandState() const;
+ void SetStandState(uint8 state);
+
+ bool IsMounted() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT ); }
+ uint32 GetMountID() const { return GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID); }
+ void Mount(uint32 mount);
+ void Unmount();
+
+ uint16 GetMaxSkillValueForLevel(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; }
+ uint32 DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss);
+ void DealFlatDamage(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit = false, bool isTriggeredSpell = false);
+ void DoAttackDamage(Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchoolMask damageSchoolMask, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false);
+
+ void CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, SpellSchoolMask damageSchoolMask, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false);
+ void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage = 0, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NONE, SpellEntry const *procSpell = NULL, bool isTriggeredSpell = false, WeaponAttackType attType = BASE_ATTACK);
+ void HandleEmoteCommand(uint32 anim_id);
+ void AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType = BASE_ATTACK, bool extra = false );
+
+ float MeleeMissChanceCalc(const Unit *pVictim, WeaponAttackType attType) const;
+ SpellMissInfo MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell);
+ SpellMissInfo SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool canReflect = false);
+
+ float GetUnitDodgeChance() const;
+ float GetUnitParryChance() const;
+ float GetUnitBlockChance() const;
+ float GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVictim) const;
+
+ virtual uint32 GetShieldBlockValue() const =0;
+ uint32 GetUnitMeleeSkill(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; }
+ uint32 GetDefenseSkillValue(Unit const* target = NULL) const;
+ uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = NULL) const;
+ float GetWeaponProcChance() const;
+ float GetPPMProcChance(uint32 WeaponSpeed, float PPM) const;
+ MeleeHitOutcome RollPhysicalOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, SpellEntry const *spellInfo);
+ MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType) const;
+ MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance, bool SpellCasted ) const;
+
+ bool isVendor() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR ); }
+ bool isTrainer() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER ); }
+ bool isQuestGiver() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER ); }
+ bool isGossip() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP ); }
+ bool isTaxi() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_FLIGHTMASTER ); }
+ bool isGuildMaster() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PETITIONER ); }
+ bool isBattleMaster() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_BATTLEMASTER ); }
+ bool isBanker() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_BANKER ); }
+ bool isInnkeeper() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_INNKEEPER ); }
+ bool isSpiritHealer() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER ); }
+ bool isSpiritGuide() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITGUIDE ); }
+ bool isTabardDesigner()const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TABARDDESIGNER ); }
+ bool isAuctioner() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_AUCTIONEER ); }
+ bool isArmorer() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_REPAIR ); }
+ bool isServiceProvider() const
+ {
+ return HasFlag( UNIT_NPC_FLAGS,
+ UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_FLIGHTMASTER |
+ UNIT_NPC_FLAG_PETITIONER | UNIT_NPC_FLAG_BATTLEMASTER | UNIT_NPC_FLAG_BANKER |
+ UNIT_NPC_FLAG_INNKEEPER | UNIT_NPC_FLAG_GUARD | UNIT_NPC_FLAG_SPIRITHEALER |
+ UNIT_NPC_FLAG_SPIRITGUIDE | UNIT_NPC_FLAG_TABARDDESIGNER | UNIT_NPC_FLAG_AUCTIONEER );
+ }
+ bool isSpiritService() const { return HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPIRITHEALER | UNIT_NPC_FLAG_SPIRITGUIDE ); }
+
+ //Need fix or use this
+ bool isGuard() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GUARD); }
+
+ bool isInFlight() const { return hasUnitState(UNIT_STAT_IN_FLIGHT); }
+
+ bool isInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); }
+ void SetInCombatState(bool PvP);
+ void SetInCombatWith(Unit* enemy);
+ void ClearInCombat();
+ uint32 GetCombatTimer() const { return m_CombatTimer; }
+
+ bool HasAuraType(AuraType auraType) const;
+ bool HasAura(uint32 spellId, uint32 effIndex) const
+ { return m_Auras.find(spellEffectPair(spellId, effIndex)) != m_Auras.end(); }
+
+ bool virtual HasSpell(uint32 /*spellID*/) const { return false; }
+
+ bool HasStealthAura() const { return HasAuraType(SPELL_AURA_MOD_STEALTH); }
+ bool HasInvisibilityAura() const { return HasAuraType(SPELL_AURA_MOD_INVISIBILITY); }
+ bool isFeared() const { return HasAuraType(SPELL_AURA_MOD_FEAR); }
+ bool isInRoots() const { return HasAuraType(SPELL_AURA_MOD_ROOT); }
+ bool IsPolymorphed() const;
+
+ bool isFrozen() const;
+
+ void RemoveSpellbyDamageTaken(AuraType auraType, uint32 damage);
+
+ bool isTargetableForAttack() const;
+ virtual bool IsInWater() const;
+ virtual bool IsUnderWater() const;
+ bool isInAccessablePlaceFor(Creature const* c) const;
+
+ void SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, bool critical = false);
+ void SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage,Powers powertype, bool critical = false);
+ uint32 SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool isTriggeredSpell = false, bool useSpellDamage = true);
+ void CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+ void CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+ void CastCustomSpell(Unit* Victim, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+ void CastCustomSpell(Unit* Victim,SpellEntry const *spellInfo, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem= NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+ void CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem = NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+ void CastSpell(float x, float y, float z, SpellEntry const *spellInfo, bool triggered, Item *castItem = NULL, Aura* triggredByAura = NULL, uint64 originalCaster = 0);
+
+ bool IsDamageToThreatSpell(SpellEntry const * spellInfo) const;
+
+ void DeMorph();
+
+ void SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount);
+ void SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchoolMask damageSchoolMask,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false);
+ void SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo);
+
+ void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL);
+ void SendMonsterMoveByPath(Path const& path, uint32 start, uint32 end, uint32 MovementFlags);
+ void SendMonsterMoveWithSpeed(float x, float y, float z, uint32 MovementFlags, uint32 transitTime = 0, Player* player = NULL);
+ void SendMonsterMoveWithSpeedToCurrentDestination(Player* player = NULL);
+
+ virtual void MoveOutOfRange(Player &) { };
+
+ bool isAlive() const { return (m_deathState == ALIVE); };
+ bool isDead() const { return ( m_deathState == DEAD || m_deathState == CORPSE ); };
+ DeathState getDeathState() { return m_deathState; };
+ virtual void setDeathState(DeathState s); // overwrited in Creature/Player/Pet
+
+ uint64 const& GetOwnerGUID() const { return GetUInt64Value(UNIT_FIELD_SUMMONEDBY); }
+ uint64 GetPetGUID() const { return GetUInt64Value(UNIT_FIELD_SUMMON); }
+ uint64 GetCharmerGUID() const { return GetUInt64Value(UNIT_FIELD_CHARMEDBY); }
+ uint64 GetCharmGUID() const { return GetUInt64Value(UNIT_FIELD_CHARM); }
+ void SetCharmerGUID(uint64 owner) { SetUInt64Value(UNIT_FIELD_CHARMEDBY, owner); }
+
+ uint64 GetCharmerOrOwnerGUID() const { return GetCharmerGUID() ? GetCharmerGUID() : GetOwnerGUID(); }
+ uint64 GetCharmerOrOwnerOrOwnGUID() const
+ {
+ if(uint64 guid = GetCharmerOrOwnerGUID())
+ return guid;
+ return GetGUID();
+ }
+ bool isCharmedOwnedByPlayerOrPlayer() const { return IS_PLAYER_GUID(GetCharmerOrOwnerOrOwnGUID()); }
+
+ Player* GetSpellModOwner();
+
+ Unit* GetOwner() const;
+ Pet* GetPet() const;
+ Unit* GetCharmer() const;
+ Unit* GetCharm() const;
+ Unit* GetCharmerOrOwner() const { return GetCharmerGUID() ? GetCharmer() : GetOwner(); }
+ Unit* GetCharmerOrOwnerOrSelf()
+ {
+ if(Unit* u = GetCharmerOrOwner())
+ return u;
+
+ return this;
+ }
+ Player* GetCharmerOrOwnerPlayerOrPlayerItself();
+
+ void SetPet(Pet* pet);
+ void SetCharm(Unit* pet);
+ bool isCharmed() const { return GetCharmerGUID() != 0; }
+
+ CharmInfo* GetCharmInfo() { return m_charmInfo; }
+ CharmInfo* InitCharmInfo(Unit* charm);
+
+ bool AddAura(Aura *aur);
+
+ void RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT);
+ void RemoveAura(uint32 spellId, uint32 effindex, Aura* except = NULL);
+ void RemoveSingleAuraFromStack(uint32 spellId, uint32 effindex);
+ void RemoveAurasDueToSpell(uint32 spellId, Aura* except = NULL);
+ void RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId);
+ void RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit *dispeler);
+ void RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit *stealer);
+ void RemoveAurasDueToSpellByCancel(uint32 spellId);
+ void RemoveNotOwnSingleTargetAuras();
+
+ void RemoveSpellsCausingAura(AuraType auraType);
+ void RemoveRankAurasDueToSpell(uint32 spellId);
+ bool RemoveNoStackAurasDueToAura(Aura *Aur);
+ void RemoveAurasWithInterruptFlags(uint32 flags);
+ void RemoveAurasWithDispelType( DispelType type );
+
+ void RemoveAllAuras();
+ void RemoveArenaAuras(bool onleave = false);
+ void RemoveAllAurasOnDeath();
+ void DelayAura(uint32 spellId, uint32 effindex, int32 delaytime);
+
+ float GetResistanceBuffMods(SpellSchools school, bool positive) const { return GetFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school ); }
+ void SetResistanceBuffMods(SpellSchools school, bool positive, float val) { SetFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school,val); }
+ void ApplyResistanceBuffModsMod(SpellSchools school, bool positive, float val, bool apply) { ApplyModSignedFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school, val, apply); }
+ void ApplyResistanceBuffModsPercentMod(SpellSchools school, bool positive, float val, bool apply) { ApplyPercentModFloatValue(positive ? UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE+school : UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE+school, val, apply); }
+ void InitStatBuffMods()
+ {
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) SetFloatValue(UNIT_FIELD_POSSTAT0+i, 0);
+ for(int i = STAT_STRENGTH; i < MAX_STATS; ++i) SetFloatValue(UNIT_FIELD_NEGSTAT0+i, 0);
+ }
+ void ApplyStatBuffMod(Stats stat, float val, bool apply) { ApplyModSignedFloatValue((val > 0 ? UNIT_FIELD_POSSTAT0+stat : UNIT_FIELD_NEGSTAT0+stat), val, apply); }
+ void ApplyStatPercentBuffMod(Stats stat, float val, bool apply)
+ {
+ ApplyPercentModFloatValue(UNIT_FIELD_POSSTAT0+stat, val, apply);
+ ApplyPercentModFloatValue(UNIT_FIELD_NEGSTAT0+stat, val, apply);
+ }
+ void SetCreateStat(Stats stat, float val) { m_createStats[stat] = val; }
+ void SetCreateHealth(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_HEALTH, val); }
+ uint32 GetCreateHealth() const { return GetUInt32Value(UNIT_FIELD_BASE_HEALTH); }
+ void SetCreateMana(uint32 val) { SetUInt32Value(UNIT_FIELD_BASE_MANA, val); }
+ uint32 GetCreateMana() const { return GetUInt32Value(UNIT_FIELD_BASE_MANA); }
+ uint32 GetCreatePowers(Powers power) const;
+ float GetPosStat(Stats stat) const { return GetFloatValue(UNIT_FIELD_POSSTAT0+stat); }
+ float GetNegStat(Stats stat) const { return GetFloatValue(UNIT_FIELD_NEGSTAT0+stat); }
+ float GetCreateStat(Stats stat) const { return m_createStats[stat]; }
+
+ void SetCurrentCastedSpell(Spell * pSpell);
+ virtual void ProhibitSpellScholl(SpellSchoolMask /*idSchoolMask*/, uint32 /*unTimeMs*/ ) { }
+ void InterruptSpell(uint32 spellType, bool withDelayed = true);
+
+ // set withDelayed to true to account delayed spells as casted
+ // delayed+channeled spells are always accounted as casted
+ // we can skip channeled or delayed checks using flags
+ bool IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled = false, bool skipAutorepeat = false) const;
+
+ // set withDelayed to true to interrupt delayed spells too
+ // delayed+channeled spells are always interrupted
+ void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid = 0);
+
+ Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
+
+ Spell* m_currentSpells[CURRENT_MAX_SPELL];
+
+ uint32 m_addDmgOnce;
+ uint64 m_TotemSlot[MAX_TOTEM];
+ uint64 m_ObjectSlot[4];
+ uint32 m_detectInvisibilityMask;
+ uint32 m_invisibilityMask;
+ uint32 m_ShapeShiftFormSpellId;
+ ShapeshiftForm m_form;
+ float m_modMeleeHitChance;
+ float m_modRangedHitChance;
+ float m_modSpellHitChance;
+ int32 m_baseSpellCritChance;
+
+ float m_threatModifier[MAX_SPELL_SCHOOL];
+ float m_modAttackSpeedPct[3];
+
+ // Event handler
+ EventProcessor m_Events;
+
+ // stat system
+ bool HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, float amount, bool apply);
+ void SetModifierValue(UnitMods unitMod, UnitModifierType modifierType, float value) { m_auraModifiersGroup[unitMod][modifierType] = value; }
+ float GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) const;
+ float GetTotalStatValue(Stats stat) const;
+ float GetTotalAuraModValue(UnitMods unitMod) const;
+ SpellSchools GetSpellSchoolByAuraGroup(UnitMods unitMod) const;
+ Stats GetStatByAuraGroup(UnitMods unitMod) const;
+ Powers GetPowerTypeByAuraGroup(UnitMods unitMod) const;
+ bool CanModifyStats() const { return m_canModifyStats; }
+ void SetCanModifyStats(bool modifyStats) { m_canModifyStats = modifyStats; }
+ virtual bool UpdateStats(Stats stat) = 0;
+ virtual bool UpdateAllStats() = 0;
+ virtual void UpdateResistances(uint32 school) = 0;
+ virtual void UpdateArmor() = 0;
+ virtual void UpdateMaxHealth() = 0;
+ virtual void UpdateMaxPower(Powers power) = 0;
+ virtual void UpdateAttackPowerAndDamage(bool ranged = false) = 0;
+ virtual void UpdateDamagePhysical(WeaponAttackType attType) = 0;
+ float GetTotalAttackPowerValue(WeaponAttackType attType) const;
+ float GetWeaponDamageRange(WeaponAttackType attType ,WeaponDamageRange type) const;
+ void SetBaseWeaponDamage(WeaponAttackType attType ,WeaponDamageRange damageRange, float value) { m_weaponDamage[attType][damageRange] = value; }
+
+ bool isInFront(Unit const* target,float distance, float arc = M_PI) const;
+ void SetInFront(Unit const* target);
+ bool isInBack(Unit const* target, float distance, float arc = M_PI) const;
+
+ // Visibility system
+ UnitVisibility GetVisibility() const { return m_Visibility; }
+ void SetVisibility(UnitVisibility x);
+
+ // common function for visibility checks for player/creatures with detection code
+ bool isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList = false) const;
+ bool canDetectInvisibilityOf(Unit const* u) const;
+
+ // virtual functions for all world objects types
+ bool isVisibleForInState(Player const* u, bool inVisibleList) const;
+ // function for low level grid visibility checks in player/creature cases
+ virtual bool IsVisibleInGridForPlayer(Player* pl) const = 0;
+
+ bool waterbreath;
+ AuraList & GetSingleCastAuras() { return m_scAuras; }
+ AuraList const& GetSingleCastAuras() const { return m_scAuras; }
+ SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY];
+
+ // Threat related methodes
+ bool CanHaveThreatList() const;
+ void AddThreat(Unit* pVictim, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL);
+ float ApplyTotalThreatModifier(float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL);
+ void DeleteThreatList();
+ bool SelectHostilTarget();
+ void TauntApply(Unit* pVictim);
+ void TauntFadeOut(Unit *taunter);
+ ThreatManager& getThreatManager() { return m_ThreatManager; }
+ void addHatedBy(HostilReference* pHostilReference) { m_HostilRefManager.insertFirst(pHostilReference); };
+ void removeHatedBy(HostilReference* /*pHostilReference*/ ) { /* nothing to do yet */ }
+ HostilRefManager& getHostilRefManager() { return m_HostilRefManager; }
+
+ Aura* GetAura(uint32 spellId, uint32 effindex);
+ AuraMap & GetAuras() { return m_Auras; }
+ AuraMap const& GetAuras() const { return m_Auras; }
+ AuraList const& GetAurasByType(AuraType type) const { return m_modAuras[type]; }
+ void ApplyAuraProcTriggerDamage(Aura* aura, bool apply);
+
+ int32 GetTotalAuraModifier(AuraType auratype) const;
+ float GetTotalAuraMultiplier(AuraType auratype) const;
+ int32 GetMaxPositiveAuraModifier(AuraType auratype) const;
+ int32 GetMaxNegativeAuraModifier(AuraType auratype) const;
+
+ int32 GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const;
+ float GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask) const;
+ int32 GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const;
+ int32 GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const;
+
+ int32 GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const;
+ float GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 misc_value) const;
+ int32 GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const;
+ int32 GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const;
+
+ Aura* GetDummyAura(uint32 spell_id) const;
+
+ uint32 GetDisplayId() { return GetUInt32Value(UNIT_FIELD_DISPLAYID); }
+ void SetDisplayId(uint32 modelId);
+ uint32 GetNativeDisplayId() { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); }
+ void SetNativeDisplayId(uint32 modelId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, modelId); }
+ void setTransForm(uint32 spellid) { m_transform = spellid;}
+ uint32 getTransForm() const { return m_transform;}
+ void AddDynObject(DynamicObject* dynObj);
+ void RemoveDynObject(uint32 spellid);
+ void RemoveDynObjectWithGUID(uint64 guid) { m_dynObjGUIDs.remove(guid); }
+ void RemoveAllDynObjects();
+ void AddGameObject(GameObject* gameObj);
+ void RemoveGameObject(GameObject* gameObj, bool del);
+ void RemoveGameObject(uint32 spellid, bool del);
+ void RemoveAllGameObjects();
+ DynamicObject *GetDynObject(uint32 spellId, uint32 effIndex);
+ DynamicObject *GetDynObject(uint32 spellId);
+ uint32 CalculateDamage(WeaponAttackType attType, bool normalized);
+ float GetAPMultiplier(WeaponAttackType attType, bool normalized);
+ void ModifyAuraState(AuraState flag, bool apply);
+ bool HasAuraState(AuraState flag) const { return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1)); }
+ void UnsummonAllTotems();
+ int32 SpellBaseDamageBonus(SpellSchoolMask schoolMask);
+ int32 SpellBaseHealingBonus(SpellSchoolMask schoolMask);
+ int32 SpellBaseDamageBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim);
+ int32 SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim);
+ uint32 SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 damage, DamageEffectType damagetype);
+ uint32 SpellHealingBonus(SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, Unit *pVictim);
+ bool isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType);
+ uint32 SpellCriticalBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim);
+
+ void SetLastManaUse(uint32 spellCastTime) { m_lastManaUse = spellCastTime; }
+ bool IsUnderLastManaUseEffect() const;
+
+ void SetContestedPvP(Player *attackedPlayer = NULL);
+
+ void MeleeDamageBonus(Unit *pVictim, uint32 *damage, WeaponAttackType attType, SpellEntry const *spellProto = NULL);
+ uint32 GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectType damagetype, uint32 CastingTime );
+
+ void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply);
+ void ApplySpellDispelImmunity(const SpellEntry * spellProto, DispelType type, bool apply);
+ virtual bool IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges = false);
+ // redefined in Creature
+ bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask, bool useCharges = false);
+ virtual bool IsImmunedToSpellEffect(uint32 effect, uint32 mechanic) const;
+ // redefined in Creature
+
+ uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage);
+ void CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist);
+
+ void UpdateSpeed(UnitMoveType mtype, bool forced);
+ float GetSpeed( UnitMoveType mtype ) const;
+ float GetSpeedRate( UnitMoveType mtype ) const { return m_speed_rate[mtype]; }
+ void SetSpeed(UnitMoveType mtype, float rate, bool forced = false);
+
+ void SetHover(bool on);
+ bool isHover() const { return HasAuraType(SPELL_AURA_HOVER); }
+
+ void _RemoveAllAuraMods();
+ void _ApplyAllAuraMods();
+
+ int32 CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_index, int32 basePoints, Unit const* target);
+ int32 CalculateSpellDuration(SpellEntry const* spellProto, uint8 effect_index, Unit const* target);
+ float CalculateLevelPenalty(SpellEntry const* spellProto) const;
+
+ void addFollower(FollowerReference* pRef) { m_FollowingRefManager.insertFirst(pRef); }
+ void removeFollower(FollowerReference* /*pRef*/ ) { /* nothing to do yet */ }
+ static Unit* GetUnit(WorldObject& object, uint64 guid);
+
+ MotionMaster* GetMotionMaster() { return &i_motionMaster; }
+
+ bool IsStopped() const { return !(hasUnitState(UNIT_STAT_MOVING)); }
+ void StopMoving();
+
+ void AddUnitMovementFlag(uint32 f) { m_unit_movement_flags |= f; }
+ void RemoveUnitMovementFlag(uint32 f)
+ {
+ uint32 oldval = m_unit_movement_flags;
+ m_unit_movement_flags = oldval & ~f;
+ }
+ uint32 HasUnitMovementFlag(uint32 f) const { return m_unit_movement_flags & f; }
+ uint32 GetUnitMovementFlags() const { return m_unit_movement_flags; }
+ void SetUnitMovementFlags(uint32 f) { m_unit_movement_flags = f; }
+
+ void SetFeared(bool apply, uint64 casterGUID = 0, uint32 spellID = 0);
+ void SetConfused(bool apply, uint64 casterGUID = 0, uint32 spellID = 0);
+
+ void AddComboPointHolder(uint32 lowguid) { m_ComboPointHolders.insert(lowguid); }
+ void RemoveComboPointHolder(uint32 lowguid) { m_ComboPointHolders.erase(lowguid); }
+ void ClearComboPointHolders();
+
+ ///----------Pet responses methods-----------------
+ void SendPetCastFail(uint32 spellid, uint8 msg);
+ void SendPetActionFeedback (uint8 msg);
+ void SendPetTalk (uint32 pettalk);
+ void SendPetSpellCooldown (uint32 spellid, time_t cooltime);
+ void SendPetClearCooldown (uint32 spellid);
+ void SendPetAIReaction(uint64 guid);
+ ///----------End of Pet responses methods----------
+
+ void propagateSpeedChange() { GetMotionMaster()->propagateSpeedChange(); }
+
+ // reactive attacks
+ void ClearAllReactives();
+ void StartReactiveTimer( ReactiveType reactive ) { m_reactiveTimer[reactive] = REACTIVE_TIMER_START;}
+ void UpdateReactives(uint32 p_time);
+
+ // group updates
+ void UpdateAuraForGroup(uint8 slot);
+
+ // pet auras
+ typedef std::set<PetAura const*> PetAuraSet;
+ PetAuraSet m_petAuras;
+ void AddPetAura(PetAura const* petSpell);
+ void RemovePetAura(PetAura const* petSpell);
+
+ protected:
+ explicit Unit ();
+
+ void _UpdateSpells(uint32 time);
+
+ void _UpdateAutoRepeatSpell();
+ bool m_AutoRepeatFirstCast;
+
+ uint32 m_attackTimer[MAX_ATTACK];
+
+ float m_createStats[MAX_STATS];
+
+ AttackerSet m_attackers;
+ Unit* m_attacking;
+
+ DeathState m_deathState;
+
+ AuraMap m_Auras;
+
+ std::list<Aura *> m_scAuras; // casted singlecast auras
+
+ typedef std::list<uint64> DynObjectGUIDs;
+ DynObjectGUIDs m_dynObjGUIDs;
+
+ std::list<GameObject*> m_gameObj;
+ bool m_isSorted;
+ uint32 m_transform;
+ uint32 m_removedAuras;
+
+ AuraList m_modAuras[TOTAL_AURAS];
+ float m_auraModifiersGroup[UNIT_MOD_END][MODIFIER_TYPE_END];
+ float m_weaponDamage[MAX_ATTACK][2];
+ bool m_canModifyStats;
+ //std::list< spellEffectPair > AuraSpells[TOTAL_AURAS]; // TODO: use this if ok for mem
+
+ float m_speed_rate[MAX_MOVE_TYPE];
+
+ CharmInfo *m_charmInfo;
+
+ virtual SpellSchoolMask GetMeleeDamageSchoolMask() const;
+
+ MotionMaster i_motionMaster;
+ uint32 m_unit_movement_flags;
+
+ uint32 m_reactiveTimer[MAX_REACTIVE];
+
+ private:
+ void SendAttackStop(Unit* victim); // only from AttackStop(Unit*)
+ void SendAttackStart(Unit* pVictim); // only from Unit::AttackStart(Unit*)
+
+ void ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, AuraTypeSet const& procAuraTypes, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage, SpellSchoolMask damageSchoolMask );
+ bool HandleDummyAuraProc(Unit *pVictim, SpellEntry const *spellProto, uint32 effIndex, uint32 damage, Aura* triggredByAura, SpellEntry const * procSpell, uint32 procFlag,uint32 cooldown);
+ bool HandleProcTriggerSpell(Unit *pVictim,uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlags,WeaponAttackType attType,uint32 cooldown);
+ bool HandleHasteAuraProc(Unit *pVictim, SpellEntry const *spellProto, uint32 effIndex, uint32 damage, Aura* triggredByAura, SpellEntry const * procSpell, uint32 procFlag,uint32 cooldown);
+ bool HandleOverrideClassScriptAuraProc(Unit *pVictim, int32 scriptId, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell,uint32 cooldown);
+ uint32 m_state; // Even derived shouldn't modify
+ uint32 m_CombatTimer;
+ uint32 m_lastManaUse; // msecs
+
+ UnitVisibility m_Visibility;
+
+ Diminishing m_Diminishing;
+ // Manage all Units threatening us
+ ThreatManager m_ThreatManager;
+ // Manage all Units that are threatened by us
+ HostilRefManager m_HostilRefManager;
+
+ FollowerRefManager m_FollowingRefManager;
+
+ ComboPointHolderSet m_ComboPointHolders;
+};
+#endif
diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp
index fee4c216293..ee9f7961f18 100644
--- a/src/game/WaypointMovementGenerator.cpp
+++ b/src/game/WaypointMovementGenerator.cpp
@@ -1,652 +1,652 @@
-/*
- * 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
- */
-
-/*
-creature_movement Table
-
-alter table creature_movement add `text1` varchar(255) default NULL;
-alter table creature_movement add `text2` varchar(255) default NULL;
-alter table creature_movement add `text3` varchar(255) default NULL;
-alter table creature_movement add `text4` varchar(255) default NULL;
-alter table creature_movement add `text5` varchar(255) default NULL;
-alter table creature_movement add `emote` int(10) unsigned default '0';
-alter table creature_movement add `spell` int(5) unsigned default '0';
-alter table creature_movement add `wpguid` int(11) default '0';
-
-*/
-
-#include <ctime>
-
-#include "WaypointMovementGenerator.h"
-#include "ObjectMgr.h"
-#include "Creature.h"
-#include "DestinationHolderImp.h"
-#include "CreatureAI.h"
-#include "WaypointManager.h"
-
-#include <cassert>
-
-//-----------------------------------------------//
-void
-WaypointMovementGenerator<Creature>::LoadPath(Creature &c)
-{
- sLog.outDetail("LoadPath: loading waypoint path for creature %d,%d", c.GetGUIDLow(), c.GetDBTableGUIDLow());
-
- i_path = WaypointMgr.GetPath(c.GetDBTableGUIDLow());
- if(!i_path)
- {
- sLog.outErrorDb("WaypointMovementGenerator::LoadPath: creature %s(%d) doesn't have waypoint path", c.GetName(), c.GetDBTableGUIDLow());
- return;
- }
-
- uint32 node_count = i_path->size();
- i_hasDone.resize(node_count);
- for(uint32 i = 0; i < node_count-1; i++)
- i_hasDone[i] = false;
-
- // to prevent a misbehaviour inside "update"
- // update is always called with the next wp - but the wpSys needs the current
- // so when the routine is called the first time, wpSys gets the last waypoint
- // and this prevents the system from performing text/emote, etc
- i_hasDone[node_count - 1] = true;
-}
-
-void
-WaypointMovementGenerator<Creature>::ClearWaypoints()
-{
- i_path = NULL;
-}
-
-void
-WaypointMovementGenerator<Creature>::Initialize()
-{
-}
-
-bool
-WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
-{
- if(!&creature)
- return true;
-
- // 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_STUNNED | UNIT_STAT_DISTRACTED))
- return true;
-
- // prevent a crash at empty waypoint path.
- if(!i_path || i_path->empty())
- return true;
-
- // i_path was modified by chat commands for example
- if(i_path->size() != i_hasDone.size())
- i_hasDone.resize(i_path->size());
- if(i_currentNode >= i_path->size())
- i_currentNode = 0;
-
- CreatureTraveller traveller(creature);
-
- i_nextMoveTime.Update(diff);
- i_destinationHolder.UpdateTraveller(traveller, diff, false, true);
-
- // creature has been stoped in middle of the waypoint segment
- if (!i_destinationHolder.HasArrived() && creature.IsStopped())
- {
- if( i_nextMoveTime.Passed()) // Timer has elapsed, meaning this part controlled it
- {
- SetStopedByPlayer(false);
- // Now we re-set destination to same node and start travel
- creature.addUnitState(UNIT_STAT_ROAMING);
- if (creature.canFly())
- creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
- const WaypointNode &node = i_path->at(i_currentNode);
- i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
- i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
- }
- else // if( !i_nextMoveTime.Passed())
- { // unexpected end of timer && creature stopped && not at end of segment
- if (!IsStopedByPlayer())
- { // Put 30 seconds delay
- i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER);
- i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER);
- SetStopedByPlayer(true); // Mark we did it
- }
- }
- return true; // Abort here this update
- }
-
- if( creature.IsStopped())
- {
- uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1;
-
- if (!i_hasDone[idx])
- {
- if (i_path->at(idx).orientation !=100)
- creature.SetOrientation(i_path->at(idx).orientation);
-
- if(WaypointBehavior *behavior = i_path->at(idx).behavior)
- {
- if(behavior->emote != 0)
- creature.SetUInt32Value(UNIT_NPC_EMOTESTATE,behavior->emote);
- if(behavior->spell != 0)
- creature.CastSpell(&creature,behavior->spell, false);
- if(behavior->model1 != 0)
- creature.SetDisplayId(behavior->model1);
- if(!behavior->text[0].empty())
- {
- // Only one text is set
- if( !behavior->text[1].empty() )
- {
- // Select one from max 5 texts (0 and 1 laready checked)
- int i = 2;
- for( ; i < 5; ++i )
- if( behavior->text[i].empty() )
- break;
-
- creature.Say(behavior->text[rand() % i].c_str(), 0, 0);
-
- }
- else
- creature.Say(behavior->text[0].c_str(), 0, 0);
- }
-
- i_hasDone[idx] = true;
- MovementInform(creature);
- } // wpBehaviour found
- } // HasDone == false
- } // i_creature.IsStopped()
-
- if( i_nextMoveTime.Passed() ) // This is at the end of waypoint segment or has been stopped by player
- {
- if( creature.IsStopped() ) // If stopped then begin a new move segment
- {
- creature.addUnitState(UNIT_STAT_ROAMING);
- if (creature.canFly())
- creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
- const WaypointNode &node = i_path->at(i_currentNode);
- i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
- i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
- uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1;
-
- if (i_path->at(idx).orientation !=100)
- creature.SetOrientation(i_path->at(idx).orientation);
-
- if(WaypointBehavior *behavior = i_path->at(idx).behavior )
- {
- i_hasDone[idx] = false;
- if(behavior->model2 != 0)
- creature.SetDisplayId(behavior->model2);
-
- creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
- }
- }
- else // If not stopped then stop it and set the reset of TimeTracker to waittime
- {
- creature.StopMoving();
- SetStopedByPlayer(false);
- i_nextMoveTime.Reset(i_path->at(i_currentNode).delay);
- ++i_currentNode;
- if( i_currentNode >= i_path->size() )
- i_currentNode = 0;
- }
- }
- return true;
-}
-
-void WaypointMovementGenerator<Creature>::MovementInform(Creature &unit)
-{
- if(unit.AI())
- unit.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
-}
-
-//----------------------------------------------------//
-void
-FlightPathMovementGenerator::LoadPath(Player &)
-{
- objmgr.GetTaxiPathNodes(i_pathId, i_path,i_mapIds);
-}
-
-uint32
-FlightPathMovementGenerator::GetPathAtMapEnd() const
-{
- if(i_currentNode >= i_mapIds.size())
- return i_mapIds.size();
-
- uint32 curMapId = i_mapIds[i_currentNode];
- for(uint32 i = i_currentNode; i < i_mapIds.size(); ++i)
- {
- if(i_mapIds[i] != curMapId)
- return i;
- }
-
- return i_mapIds.size();
-}
-
-void
-FlightPathMovementGenerator::Initialize(Player &player)
-{
- player.getHostilRefManager().setOnlineOfflineState(false);
- player.addUnitState(UNIT_STAT_IN_FLIGHT);
- player.SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
- LoadPath(player);
- Traveller<Player> traveller(player);
- // do not send movement, it was sent already
- i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
-
- player.SendMonsterMoveByPath(GetPath(),GetCurrentNode(),GetPathAtMapEnd(),MOVEMENTFLAG_WALK_MODE|MOVEMENTFLAG_ONTRANSPORT);
-}
-
-void FlightPathMovementGenerator::Finalize(Player & player)
-{
-
- float x, y, z;
- i_destinationHolder.GetLocationNow(player.GetMapId(), x, y, z);
- player.SetPosition(x, y, z, player.GetOrientation());
-
- player.clearUnitState(UNIT_STAT_IN_FLIGHT);
- player.Unmount();
- player.RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
-
- if(player.m_taxi.empty())
- {
- player.getHostilRefManager().setOnlineOfflineState(true);
- if(player.pvpInfo.inHostileArea)
- player.CastSpell(&player, 2479, true);
-
- player.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE);
- player.StopMoving();
- }
-}
-
-bool
-FlightPathMovementGenerator::Update(Player &player, const uint32 &diff)
-{
- if( MovementInProgress() )
- {
- Traveller<Player> traveller(player);
- if( i_destinationHolder.UpdateTraveller(traveller, diff, false) )
- {
- i_destinationHolder.ResetUpdate(FLIGHT_TRAVEL_UPDATE);
- if( i_destinationHolder.HasArrived() )
- {
- uint32 curMap = i_mapIds[i_currentNode];
- ++i_currentNode;
- if( MovementInProgress() )
- {
- DEBUG_LOG("loading node %u for player %s", i_currentNode, player.GetName());
- if(i_mapIds[i_currentNode]==curMap)
- {
- // do not send movement, it was sent already
- i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
- }
- return true;
- }
- //else HasArrived()
- }
- else
- return true;
- }
- else
- return true;
- }
-
- // we have arrived at the end of the path
- return false;
-}
-
-void
-FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
-{
- if(i_mapIds.empty())
- return;
-
- uint32 map0 = i_mapIds[0];
- for(int i = 1; i < i_mapIds.size(); ++i)
- {
- if(i_mapIds[i]!=map0)
- {
- i_currentNode = i;
- return;
- }
- }
-}
-
-//
-// Unique1's ASTAR Pathfinding Code... For future use & reference...
-//
-
-#ifdef __PATHFINDING__
-
-int GetFCost(int to, int num, int parentNum, float *gcost); // Below...
-
-int ShortenASTARRoute(short int *pathlist, int number)
-{ // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
- short int temppathlist[MAX_PATHLIST_NODES];
- int count = 0;
- // int count2 = 0;
- int temp, temp2;
- int link;
- int upto = 0;
-
- for (temp = number; temp >= 0; temp--)
- {
- qboolean shortened = qfalse;
-
- for (temp2 = 0; temp2 < temp; temp2++)
- {
- for (link = 0; link < nodes[pathlist[temp]].enodenum; link++)
- {
- if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED)
- continue;
-
- //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
- // continue;
-
- //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
- // continue;
-
- if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2])
- { // Found a shorter route...
- //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
- {
- temppathlist[count] = pathlist[temp2];
- temp = temp2;
- ++count;
- shortened = qtrue;
- }
- }
- }
- }
-
- if (!shortened)
- {
- temppathlist[count] = pathlist[temp];
- ++count;
- }
- }
-
- upto = count;
-
- for (temp = 0; temp < count; temp++)
- {
- pathlist[temp] = temppathlist[upto];
- --upto;
- }
-
- G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count);
- return count;
-}
-
-/*
-===========================================================================
-CreatePathAStar
-This function uses the A* pathfinding algorithm to determine the
-shortest path between any two nodes.
-It's fairly complex, so I'm not really going to explain it much.
-Look up A* and binary heaps for more info.
-pathlist stores the ideal path between the nodes, in reverse order,
-and the return value is the number of nodes in that path
-===========================================================================
-*/
-int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
-{
- //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
- //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
- short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index
- float gcost[MAX_NODES];
- int fcost[MAX_NODES];
- char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
- short int parent[MAX_NODES];
-
- short int numOpen = 0;
- short int atNode, temp, newnode=-1;
- qboolean found = qfalse;
- int count = -1;
- float gc;
- int i, u, v, m;
- vec3_t vec;
-
- //clear out all the arrays
- memset(openlist, 0, sizeof(short int)*(MAX_NODES+1));
- memset(fcost, 0, sizeof(int)*MAX_NODES);
- memset(list, 0, sizeof(char)*MAX_NODES);
- memset(parent, 0, sizeof(short int)*MAX_NODES);
- memset(gcost, -1, sizeof(float)*MAX_NODES);
-
- //make sure we have valid data before calculating everything
- if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to))
- return -1;
-
- openlist[1] = from; //add the starting node to the open list
- ++numOpen;
- gcost[from] = 0; //its f and g costs are obviously 0
- fcost[from] = 0;
-
- while (1)
- {
- if (numOpen != 0) //if there are still items in the open list
- {
- //pop the top item off of the list
- atNode = openlist[1];
- list[atNode] = 2; //put the node on the closed list so we don't check it again
- --numOpen;
-
- openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position
- v = 1;
-
- //this while loop reorders the list so that the new lowest fcost is at the top again
- while (1)
- {
- u = v;
- if ((2*u+1) < numOpen) //if both children exist
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- if (fcost[openlist[v]] >= fcost[openlist[2*u+1]])
- v = 2*u+1;
- }
- else
- {
- if ((2*u) < numOpen) //if only one child exists
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- }
- }
-
- if (u != v) //if they're out of order, swap this item with its parent
- {
- temp = openlist[u];
- openlist[u] = openlist[v];
- openlist[v] = temp;
- }
- else
- break;
- }
-
- for (i = 0; i < nodes[atNode].enodenum; i++) //loop through all the links for this node
- {
- newnode = nodes[atNode].links[i].targetNode;
-
- //if this path is blocked, skip it
- if (nodes[atNode].links[i].flags & PATH_BLOCKED)
- continue;
- //if this path is blocked, skip it
- if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS)
- continue;
- //skip any unreachable nodes
- if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES))
- continue;
- if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS))
- continue;
-
- if (list[newnode] == 2) //if this node is on the closed list, skip it
- continue;
-
- if (list[newnode] != 1) //if this node is not already on the open list
- {
- openlist[++numOpen] = newnode; //add the new node to the open list
- list[newnode] = 1;
- parent[newnode] = atNode; //record the node's parent
-
- if (newnode == to) //if we've found the goal, don't keep computing paths!
- break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
-
- //store it's f cost value
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //this loop re-orders the heap so that the lowest fcost is at the top
- m = numOpen;
- while (m != 1) //while this item isn't at the top of the heap already
- {
- //if it has a lower fcost than its parent
- if (fcost[openlist[m]] <= fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- }
- else //if this node is already on the open list
- {
- gc = gcost[atNode];
- VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec);
- gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path
-
- if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before)
- {
- parent[newnode] = atNode; //set the new parent for this node
- gcost[newnode] = gc; //and the new g cost
-
- for (i = 1; i < numOpen; i++) //loop through all the items on the open list
- {
- if (openlist[i] == newnode) //find this node in the list
- {
- //calculate the new fcost and store it
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //reorder the list again, with the lowest fcost item on top
- m = i;
- while (m != 1)
- {
- //if the item has a lower fcost than it's parent
- if (fcost[openlist[m]] < fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- break; //exit the 'for' loop because we already changed this node
- } //if
- } //for
- } //if (gc < gcost[newnode])
- } //if (list[newnode] != 1) --> else
- } //for (loop through links)
- } //if (numOpen != 0)
- else
- {
- found = qfalse; //there is no path between these nodes
- break;
- }
-
- if (list[to] == 1) //if the destination node is on the open list, we're done
- {
- found = qtrue;
- break;
- }
- } //while (1)
-
- if (found == qtrue) //if we found a path
- {
- //G_Printf("%s - path found!n", bot->client->pers.netname);
- count = 0;
-
- temp = to; //start at the end point
- while (temp != from) //travel along the path (backwards) until we reach the starting point
- {
- pathlist[count++] = temp; //add the node to the pathlist and increment the count
- temp = parent[temp]; //move to the parent of this node to continue the path
- }
-
- pathlist[count++] = from; //add the beginning node to the end of the pathlist
-
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- }
- else
- {
- //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
- count = CreateDumbRoute(from, to, pathlist);
-
- if (count > 0)
- {
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- return count;
- }
- }
-
- return count; //return the number of nodes in the path, -1 if not found
-}
-
-/*
-===========================================================================
-GetFCost
-Utility function used by A* pathfinding to calculate the
-cost to move between nodes towards a goal. Using the A*
-algorithm F = G + H, G here is the distance along the node
-paths the bot must travel, and H is the straight-line distance
-to the goal node.
-Returned as an int because more precision is unnecessary and it
-will slightly speed up heap access
-===========================================================================
-*/
-int GetFCost(int to, int num, int parentNum, float *gcost)
-{
- float gc = 0;
- float hc = 0;
- vec3_t v;
-
- if (gcost[num] == -1)
- {
- if (parentNum != -1)
- {
- gc = gcost[parentNum];
- VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v);
- gc += VectorLength(v);
- }
- gcost[num] = gc;
- }
- else
- gc = gcost[num];
-
- VectorSubtract(nodes[to].origin, nodes[num].origin, v);
- hc = VectorLength(v);
-
- return (int)(gc + hc);
-}
-#endif //__PATHFINDING__
+/*
+ * 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
+ */
+
+/*
+creature_movement Table
+
+alter table creature_movement add `text1` varchar(255) default NULL;
+alter table creature_movement add `text2` varchar(255) default NULL;
+alter table creature_movement add `text3` varchar(255) default NULL;
+alter table creature_movement add `text4` varchar(255) default NULL;
+alter table creature_movement add `text5` varchar(255) default NULL;
+alter table creature_movement add `emote` int(10) unsigned default '0';
+alter table creature_movement add `spell` int(5) unsigned default '0';
+alter table creature_movement add `wpguid` int(11) default '0';
+
+*/
+
+#include <ctime>
+
+#include "WaypointMovementGenerator.h"
+#include "ObjectMgr.h"
+#include "Creature.h"
+#include "DestinationHolderImp.h"
+#include "CreatureAI.h"
+#include "WaypointManager.h"
+
+#include <cassert>
+
+//-----------------------------------------------//
+void
+WaypointMovementGenerator<Creature>::LoadPath(Creature &c)
+{
+ sLog.outDetail("LoadPath: loading waypoint path for creature %d,%d", c.GetGUIDLow(), c.GetDBTableGUIDLow());
+
+ i_path = WaypointMgr.GetPath(c.GetDBTableGUIDLow());
+ if(!i_path)
+ {
+ sLog.outErrorDb("WaypointMovementGenerator::LoadPath: creature %s(%d) doesn't have waypoint path", c.GetName(), c.GetDBTableGUIDLow());
+ return;
+ }
+
+ uint32 node_count = i_path->size();
+ i_hasDone.resize(node_count);
+ for(uint32 i = 0; i < node_count-1; i++)
+ i_hasDone[i] = false;
+
+ // to prevent a misbehaviour inside "update"
+ // update is always called with the next wp - but the wpSys needs the current
+ // so when the routine is called the first time, wpSys gets the last waypoint
+ // and this prevents the system from performing text/emote, etc
+ i_hasDone[node_count - 1] = true;
+}
+
+void
+WaypointMovementGenerator<Creature>::ClearWaypoints()
+{
+ i_path = NULL;
+}
+
+void
+WaypointMovementGenerator<Creature>::Initialize()
+{
+}
+
+bool
+WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
+{
+ if(!&creature)
+ return true;
+
+ // 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_STUNNED | UNIT_STAT_DISTRACTED))
+ return true;
+
+ // prevent a crash at empty waypoint path.
+ if(!i_path || i_path->empty())
+ return true;
+
+ // i_path was modified by chat commands for example
+ if(i_path->size() != i_hasDone.size())
+ i_hasDone.resize(i_path->size());
+ if(i_currentNode >= i_path->size())
+ i_currentNode = 0;
+
+ CreatureTraveller traveller(creature);
+
+ i_nextMoveTime.Update(diff);
+ i_destinationHolder.UpdateTraveller(traveller, diff, false, true);
+
+ // creature has been stoped in middle of the waypoint segment
+ if (!i_destinationHolder.HasArrived() && creature.IsStopped())
+ {
+ if( i_nextMoveTime.Passed()) // Timer has elapsed, meaning this part controlled it
+ {
+ SetStopedByPlayer(false);
+ // Now we re-set destination to same node and start travel
+ creature.addUnitState(UNIT_STAT_ROAMING);
+ if (creature.canFly())
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+ const WaypointNode &node = i_path->at(i_currentNode);
+ i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+ }
+ else // if( !i_nextMoveTime.Passed())
+ { // unexpected end of timer && creature stopped && not at end of segment
+ if (!IsStopedByPlayer())
+ { // Put 30 seconds delay
+ i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER);
+ i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER);
+ SetStopedByPlayer(true); // Mark we did it
+ }
+ }
+ return true; // Abort here this update
+ }
+
+ if( creature.IsStopped())
+ {
+ uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1;
+
+ if (!i_hasDone[idx])
+ {
+ if (i_path->at(idx).orientation !=100)
+ creature.SetOrientation(i_path->at(idx).orientation);
+
+ if(WaypointBehavior *behavior = i_path->at(idx).behavior)
+ {
+ if(behavior->emote != 0)
+ creature.SetUInt32Value(UNIT_NPC_EMOTESTATE,behavior->emote);
+ if(behavior->spell != 0)
+ creature.CastSpell(&creature,behavior->spell, false);
+ if(behavior->model1 != 0)
+ creature.SetDisplayId(behavior->model1);
+ if(!behavior->text[0].empty())
+ {
+ // Only one text is set
+ if( !behavior->text[1].empty() )
+ {
+ // Select one from max 5 texts (0 and 1 laready checked)
+ int i = 2;
+ for( ; i < 5; ++i )
+ if( behavior->text[i].empty() )
+ break;
+
+ creature.Say(behavior->text[rand() % i].c_str(), 0, 0);
+
+ }
+ else
+ creature.Say(behavior->text[0].c_str(), 0, 0);
+ }
+
+ i_hasDone[idx] = true;
+ MovementInform(creature);
+ } // wpBehaviour found
+ } // HasDone == false
+ } // i_creature.IsStopped()
+
+ if( i_nextMoveTime.Passed() ) // This is at the end of waypoint segment or has been stopped by player
+ {
+ if( creature.IsStopped() ) // If stopped then begin a new move segment
+ {
+ creature.addUnitState(UNIT_STAT_ROAMING);
+ if (creature.canFly())
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+ const WaypointNode &node = i_path->at(i_currentNode);
+ i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+ uint32 idx = i_currentNode > 0 ? i_currentNode-1 : i_path->size()-1;
+
+ if (i_path->at(idx).orientation !=100)
+ creature.SetOrientation(i_path->at(idx).orientation);
+
+ if(WaypointBehavior *behavior = i_path->at(idx).behavior )
+ {
+ i_hasDone[idx] = false;
+ if(behavior->model2 != 0)
+ creature.SetDisplayId(behavior->model2);
+
+ creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
+ }
+ }
+ else // If not stopped then stop it and set the reset of TimeTracker to waittime
+ {
+ creature.StopMoving();
+ SetStopedByPlayer(false);
+ i_nextMoveTime.Reset(i_path->at(i_currentNode).delay);
+ ++i_currentNode;
+ if( i_currentNode >= i_path->size() )
+ i_currentNode = 0;
+ }
+ }
+ return true;
+}
+
+void WaypointMovementGenerator<Creature>::MovementInform(Creature &unit)
+{
+ if(unit.AI())
+ unit.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
+}
+
+//----------------------------------------------------//
+void
+FlightPathMovementGenerator::LoadPath(Player &)
+{
+ objmgr.GetTaxiPathNodes(i_pathId, i_path,i_mapIds);
+}
+
+uint32
+FlightPathMovementGenerator::GetPathAtMapEnd() const
+{
+ if(i_currentNode >= i_mapIds.size())
+ return i_mapIds.size();
+
+ uint32 curMapId = i_mapIds[i_currentNode];
+ for(uint32 i = i_currentNode; i < i_mapIds.size(); ++i)
+ {
+ if(i_mapIds[i] != curMapId)
+ return i;
+ }
+
+ return i_mapIds.size();
+}
+
+void
+FlightPathMovementGenerator::Initialize(Player &player)
+{
+ player.getHostilRefManager().setOnlineOfflineState(false);
+ player.addUnitState(UNIT_STAT_IN_FLIGHT);
+ player.SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ LoadPath(player);
+ Traveller<Player> traveller(player);
+ // do not send movement, it was sent already
+ i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
+
+ player.SendMonsterMoveByPath(GetPath(),GetCurrentNode(),GetPathAtMapEnd(),MOVEMENTFLAG_WALK_MODE|MOVEMENTFLAG_ONTRANSPORT);
+}
+
+void FlightPathMovementGenerator::Finalize(Player & player)
+{
+
+ float x, y, z;
+ i_destinationHolder.GetLocationNow(player.GetMapId(), x, y, z);
+ player.SetPosition(x, y, z, player.GetOrientation());
+
+ player.clearUnitState(UNIT_STAT_IN_FLIGHT);
+ player.Unmount();
+ player.RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+
+ if(player.m_taxi.empty())
+ {
+ player.getHostilRefManager().setOnlineOfflineState(true);
+ if(player.pvpInfo.inHostileArea)
+ player.CastSpell(&player, 2479, true);
+
+ player.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE);
+ player.StopMoving();
+ }
+}
+
+bool
+FlightPathMovementGenerator::Update(Player &player, const uint32 &diff)
+{
+ if( MovementInProgress() )
+ {
+ Traveller<Player> traveller(player);
+ if( i_destinationHolder.UpdateTraveller(traveller, diff, false) )
+ {
+ i_destinationHolder.ResetUpdate(FLIGHT_TRAVEL_UPDATE);
+ if( i_destinationHolder.HasArrived() )
+ {
+ uint32 curMap = i_mapIds[i_currentNode];
+ ++i_currentNode;
+ if( MovementInProgress() )
+ {
+ DEBUG_LOG("loading node %u for player %s", i_currentNode, player.GetName());
+ if(i_mapIds[i_currentNode]==curMap)
+ {
+ // do not send movement, it was sent already
+ i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
+ }
+ return true;
+ }
+ //else HasArrived()
+ }
+ else
+ return true;
+ }
+ else
+ return true;
+ }
+
+ // we have arrived at the end of the path
+ return false;
+}
+
+void
+FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
+{
+ if(i_mapIds.empty())
+ return;
+
+ uint32 map0 = i_mapIds[0];
+ for(int i = 1; i < i_mapIds.size(); ++i)
+ {
+ if(i_mapIds[i]!=map0)
+ {
+ i_currentNode = i;
+ return;
+ }
+ }
+}
+
+//
+// Unique1's ASTAR Pathfinding Code... For future use & reference...
+//
+
+#ifdef __PATHFINDING__
+
+int GetFCost(int to, int num, int parentNum, float *gcost); // Below...
+
+int ShortenASTARRoute(short int *pathlist, int number)
+{ // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
+ short int temppathlist[MAX_PATHLIST_NODES];
+ int count = 0;
+ // int count2 = 0;
+ int temp, temp2;
+ int link;
+ int upto = 0;
+
+ for (temp = number; temp >= 0; temp--)
+ {
+ qboolean shortened = qfalse;
+
+ for (temp2 = 0; temp2 < temp; temp2++)
+ {
+ for (link = 0; link < nodes[pathlist[temp]].enodenum; link++)
+ {
+ if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED)
+ continue;
+
+ //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
+ // continue;
+
+ //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
+ // continue;
+
+ if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2])
+ { // Found a shorter route...
+ //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
+ {
+ temppathlist[count] = pathlist[temp2];
+ temp = temp2;
+ ++count;
+ shortened = qtrue;
+ }
+ }
+ }
+ }
+
+ if (!shortened)
+ {
+ temppathlist[count] = pathlist[temp];
+ ++count;
+ }
+ }
+
+ upto = count;
+
+ for (temp = 0; temp < count; temp++)
+ {
+ pathlist[temp] = temppathlist[upto];
+ --upto;
+ }
+
+ G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count);
+ return count;
+}
+
+/*
+===========================================================================
+CreatePathAStar
+This function uses the A* pathfinding algorithm to determine the
+shortest path between any two nodes.
+It's fairly complex, so I'm not really going to explain it much.
+Look up A* and binary heaps for more info.
+pathlist stores the ideal path between the nodes, in reverse order,
+and the return value is the number of nodes in that path
+===========================================================================
+*/
+int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
+{
+ //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
+ //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
+ short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index
+ float gcost[MAX_NODES];
+ int fcost[MAX_NODES];
+ char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
+ short int parent[MAX_NODES];
+
+ short int numOpen = 0;
+ short int atNode, temp, newnode=-1;
+ qboolean found = qfalse;
+ int count = -1;
+ float gc;
+ int i, u, v, m;
+ vec3_t vec;
+
+ //clear out all the arrays
+ memset(openlist, 0, sizeof(short int)*(MAX_NODES+1));
+ memset(fcost, 0, sizeof(int)*MAX_NODES);
+ memset(list, 0, sizeof(char)*MAX_NODES);
+ memset(parent, 0, sizeof(short int)*MAX_NODES);
+ memset(gcost, -1, sizeof(float)*MAX_NODES);
+
+ //make sure we have valid data before calculating everything
+ if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to))
+ return -1;
+
+ openlist[1] = from; //add the starting node to the open list
+ ++numOpen;
+ gcost[from] = 0; //its f and g costs are obviously 0
+ fcost[from] = 0;
+
+ while (1)
+ {
+ if (numOpen != 0) //if there are still items in the open list
+ {
+ //pop the top item off of the list
+ atNode = openlist[1];
+ list[atNode] = 2; //put the node on the closed list so we don't check it again
+ --numOpen;
+
+ openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position
+ v = 1;
+
+ //this while loop reorders the list so that the new lowest fcost is at the top again
+ while (1)
+ {
+ u = v;
+ if ((2*u+1) < numOpen) //if both children exist
+ {
+ if (fcost[openlist[u]] >= fcost[openlist[2*u]])
+ v = 2*u;
+ if (fcost[openlist[v]] >= fcost[openlist[2*u+1]])
+ v = 2*u+1;
+ }
+ else
+ {
+ if ((2*u) < numOpen) //if only one child exists
+ {
+ if (fcost[openlist[u]] >= fcost[openlist[2*u]])
+ v = 2*u;
+ }
+ }
+
+ if (u != v) //if they're out of order, swap this item with its parent
+ {
+ temp = openlist[u];
+ openlist[u] = openlist[v];
+ openlist[v] = temp;
+ }
+ else
+ break;
+ }
+
+ for (i = 0; i < nodes[atNode].enodenum; i++) //loop through all the links for this node
+ {
+ newnode = nodes[atNode].links[i].targetNode;
+
+ //if this path is blocked, skip it
+ if (nodes[atNode].links[i].flags & PATH_BLOCKED)
+ continue;
+ //if this path is blocked, skip it
+ if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS)
+ continue;
+ //skip any unreachable nodes
+ if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES))
+ continue;
+ if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS))
+ continue;
+
+ if (list[newnode] == 2) //if this node is on the closed list, skip it
+ continue;
+
+ if (list[newnode] != 1) //if this node is not already on the open list
+ {
+ openlist[++numOpen] = newnode; //add the new node to the open list
+ list[newnode] = 1;
+ parent[newnode] = atNode; //record the node's parent
+
+ if (newnode == to) //if we've found the goal, don't keep computing paths!
+ break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
+
+ //store it's f cost value
+ fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
+
+ //this loop re-orders the heap so that the lowest fcost is at the top
+ m = numOpen;
+ while (m != 1) //while this item isn't at the top of the heap already
+ {
+ //if it has a lower fcost than its parent
+ if (fcost[openlist[m]] <= fcost[openlist[m/2]])
+ {
+ temp = openlist[m/2];
+ openlist[m/2] = openlist[m];
+ openlist[m] = temp; //swap them
+ m /= 2;
+ }
+ else
+ break;
+ }
+ }
+ else //if this node is already on the open list
+ {
+ gc = gcost[atNode];
+ VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec);
+ gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path
+
+ if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before)
+ {
+ parent[newnode] = atNode; //set the new parent for this node
+ gcost[newnode] = gc; //and the new g cost
+
+ for (i = 1; i < numOpen; i++) //loop through all the items on the open list
+ {
+ if (openlist[i] == newnode) //find this node in the list
+ {
+ //calculate the new fcost and store it
+ fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
+
+ //reorder the list again, with the lowest fcost item on top
+ m = i;
+ while (m != 1)
+ {
+ //if the item has a lower fcost than it's parent
+ if (fcost[openlist[m]] < fcost[openlist[m/2]])
+ {
+ temp = openlist[m/2];
+ openlist[m/2] = openlist[m];
+ openlist[m] = temp; //swap them
+ m /= 2;
+ }
+ else
+ break;
+ }
+ break; //exit the 'for' loop because we already changed this node
+ } //if
+ } //for
+ } //if (gc < gcost[newnode])
+ } //if (list[newnode] != 1) --> else
+ } //for (loop through links)
+ } //if (numOpen != 0)
+ else
+ {
+ found = qfalse; //there is no path between these nodes
+ break;
+ }
+
+ if (list[to] == 1) //if the destination node is on the open list, we're done
+ {
+ found = qtrue;
+ break;
+ }
+ } //while (1)
+
+ if (found == qtrue) //if we found a path
+ {
+ //G_Printf("%s - path found!n", bot->client->pers.netname);
+ count = 0;
+
+ temp = to; //start at the end point
+ while (temp != from) //travel along the path (backwards) until we reach the starting point
+ {
+ pathlist[count++] = temp; //add the node to the pathlist and increment the count
+ temp = parent[temp]; //move to the parent of this node to continue the path
+ }
+
+ pathlist[count++] = from; //add the beginning node to the end of the pathlist
+
+ #ifdef __BOT_SHORTEN_ROUTING__
+ count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
+ #endif //__BOT_SHORTEN_ROUTING__
+ }
+ else
+ {
+ //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
+ count = CreateDumbRoute(from, to, pathlist);
+
+ if (count > 0)
+ {
+ #ifdef __BOT_SHORTEN_ROUTING__
+ count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
+ #endif //__BOT_SHORTEN_ROUTING__
+ return count;
+ }
+ }
+
+ return count; //return the number of nodes in the path, -1 if not found
+}
+
+/*
+===========================================================================
+GetFCost
+Utility function used by A* pathfinding to calculate the
+cost to move between nodes towards a goal. Using the A*
+algorithm F = G + H, G here is the distance along the node
+paths the bot must travel, and H is the straight-line distance
+to the goal node.
+Returned as an int because more precision is unnecessary and it
+will slightly speed up heap access
+===========================================================================
+*/
+int GetFCost(int to, int num, int parentNum, float *gcost)
+{
+ float gc = 0;
+ float hc = 0;
+ vec3_t v;
+
+ if (gcost[num] == -1)
+ {
+ if (parentNum != -1)
+ {
+ gc = gcost[parentNum];
+ VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v);
+ gc += VectorLength(v);
+ }
+ gcost[num] = gc;
+ }
+ else
+ gc = gcost[num];
+
+ VectorSubtract(nodes[to].origin, nodes[num].origin, v);
+ hc = VectorLength(v);
+
+ return (int)(gc + hc);
+}
+#endif //__PATHFINDING__
diff --git a/src/game/World.cpp b/src/game/World.cpp
index 90036e46cd1..0e2b535c8d2 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -1,2595 +1,2595 @@
-/*
- * 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 world
-*/
-
-#include "Common.h"
-//#include "WorldSocket.h"
-#include "Database/DatabaseEnv.h"
-#include "Config/ConfigEnv.h"
-#include "SystemConfig.h"
-#include "Log.h"
-#include "Opcodes.h"
-#include "WorldSession.h"
-#include "WorldPacket.h"
-#include "Weather.h"
-#include "Player.h"
-#include "SkillExtraItems.h"
-#include "SkillDiscovery.h"
-#include "World.h"
-#include "ObjectMgr.h"
-#include "SpellMgr.h"
-#include "Chat.h"
-#include "Database/DBCStores.h"
-#include "LootMgr.h"
-#include "ItemEnchantmentMgr.h"
-#include "MapManager.h"
-#include "ScriptCalls.h"
-#include "CreatureAIRegistry.h"
-#include "Policies/SingletonImp.h"
-#include "BattleGroundMgr.h"
-#include "TemporarySummon.h"
-#include "WaypointMovementGenerator.h"
-#include "VMapFactory.h"
-#include "GlobalEvents.h"
-#include "GameEvent.h"
-#include "Database/DatabaseImpl.h"
-#include "GridNotifiersImpl.h"
-#include "CellImpl.h"
-#include "InstanceSaveMgr.h"
-#include "WaypointManager.h"
-#include "Util.h"
-
-INSTANTIATE_SINGLETON_1( World );
-
-volatile bool World::m_stopEvent = false;
-volatile uint32 World::m_worldLoopCounter = 0;
-
-float World::m_PlayerStartGold = 0; // starting gold
-float World::m_MaxVisibleDistanceForCreature = DEFAULT_VISIBILITY_DISTANCE;
-float World::m_MaxVisibleDistanceForPlayer = DEFAULT_VISIBILITY_DISTANCE;
-float World::m_MaxVisibleDistanceForObject = DEFAULT_VISIBILITY_DISTANCE;
-float World::m_MaxVisibleDistanceInFlight = DEFAULT_VISIBILITY_DISTANCE;
-float World::m_VisibleUnitGreyDistance = 0;
-float World::m_VisibleObjectGreyDistance = 0;
-
-// ServerMessages.dbc
-enum ServerMessageType
-{
- SERVER_MSG_SHUTDOWN_TIME = 1,
- SERVER_MSG_RESTART_TIME = 2,
- SERVER_MSG_STRING = 3,
- SERVER_MSG_SHUTDOWN_CANCELLED = 4,
- SERVER_MSG_RESTART_CANCELLED = 5
-};
-
-struct ScriptAction
-{
- uint64 sourceGUID;
- uint64 targetGUID;
- uint64 ownerGUID; // owner of source if source is item
- ScriptInfo const* script; // pointer to static script data
-};
-
-/// World constructor
-World::World()
-{
- m_playerLimit = 0;
- m_allowMovement = true;
- m_ShutdownMask = 0;
- m_ShutdownTimer = 0;
- m_gameTime=time(NULL);
- m_startTime=m_gameTime;
- m_maxActiveSessionCount = 0;
- m_maxQueuedSessionCount = 0;
- m_resultQueue = NULL;
- m_NextDailyQuestReset = 0;
-
- m_defaultDbcLocale = LOCALE_enUS;
- m_availableDbcLocaleMask = 0;
-}
-
-/// World destructor
-World::~World()
-{
- ///- Empty the kicked session set
- for (std::set<WorldSession*>::iterator itr = m_kicked_sessions.begin(); itr != m_kicked_sessions.end(); ++itr)
- delete *itr;
-
- m_kicked_sessions.clear();
-
- ///- Empty the WeatherMap
- for (WeatherMap::iterator itr = m_weathers.begin(); itr != m_weathers.end(); ++itr)
- delete itr->second;
-
- m_weathers.clear();
-
- VMAP::VMapFactory::clear();
-
- if(m_resultQueue) delete m_resultQueue;
-
- //TODO free addSessQueue
-}
-
-/// Find a player in a specified zone
-Player* World::FindPlayerInZone(uint32 zone)
-{
- ///- circle through active sessions and return the first player found in the zone
- SessionMap::iterator itr;
- for (itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
- {
- if(!itr->second)
- continue;
- Player *player = itr->second->GetPlayer();
- if(!player)
- continue;
- if( player->IsInWorld() && player->GetZoneId() == zone )
- {
- // Used by the weather system. We return the player to broadcast the change weather message to him and all players in the zone.
- return player;
- }
- }
- return NULL;
-}
-
-/// Find a session by its id
-WorldSession* World::FindSession(uint32 id) const
-{
- SessionMap::const_iterator itr = m_sessions.find(id);
-
- if(itr != m_sessions.end())
- return itr->second; // also can return NULL for kicked session
- else
- return NULL;
-}
-
-/// Remove a given session
-bool World::RemoveSession(uint32 id)
-{
- ///- Find the session, kick the user, but we can't delete session at this moment to prevent iterator invalidation
- SessionMap::iterator itr = m_sessions.find(id);
-
- if(itr != m_sessions.end() && itr->second)
- {
- if (itr->second->PlayerLoading())
- return false;
- itr->second->KickPlayer();
- }
-
- return true;
-}
-
-void World::AddSession(WorldSession* s)
-{
- addSessQueue.add(s);
-}
-
-void
-World::AddSession_ (WorldSession* s)
-{
- ASSERT (s);
-
- //NOTE - Still there is race condition in WorldSession* being used in the Sockets
-
- ///- kick already loaded player with same account (if any) and remove session
- ///- if player is in loading and want to load again, return
- if (!RemoveSession (s->GetAccountId ()))
- {
- s->KickPlayer ();
- m_kicked_sessions.insert (s);
- return;
- }
-
- WorldSession* old = m_sessions[s->GetAccountId ()];
- m_sessions[s->GetAccountId ()] = s;
-
- // if session already exist, prepare to it deleting at next world update
- // NOTE - KickPlayer() should be called on "old" in RemoveSession()
- if (old)
- m_kicked_sessions.insert (old);
-
- uint32 Sessions = GetActiveAndQueuedSessionCount ();
- uint32 pLimit = GetPlayerAmountLimit ();
- uint32 QueueSize = GetQueueSize (); //number of players in the queue
- bool inQueue = false;
- //so we don't count the user trying to
- //login as a session and queue the socket that we are using
- --Sessions;
-
- if (pLimit > 0 && Sessions >= pLimit && s->GetSecurity () == SEC_PLAYER )
- {
- AddQueuedPlayer (s);
- UpdateMaxSessionCounters ();
- sLog.outDetail ("PlayerQueue: Account id %u is in Queue Position (%u).", s->GetAccountId (), ++QueueSize);
- return;
- }
-
- WorldPacket packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1);
- packet << uint8 (AUTH_OK);
- packet << uint32 (0); // unknown random value...
- packet << uint8 (0);
- packet << uint32 (0);
- packet << uint8 (s->IsTBC () ? 1 : 0); // 0 - normal, 1 - TBC, must be set in database manually for each account
- s->SendPacket (&packet);
-
- UpdateMaxSessionCounters ();
-
- // Updates the population
- if (pLimit > 0)
- {
- float popu = GetActiveSessionCount (); //updated number of users on the server
- popu /= pLimit;
- popu *= 2;
- loginDatabase.PExecute ("UPDATE realmlist SET population = '%f' WHERE id = '%d'", popu, realmID);
- sLog.outDetail ("Server Population (%f).", popu);
- }
-}
-
-int32 World::GetQueuePos(WorldSession* sess)
-{
- uint32 position = 1;
-
- for(Queue::iterator iter = m_QueuedPlayer.begin(); iter != m_QueuedPlayer.end(); ++iter, ++position)
- if((*iter) == sess)
- return position;
-
- return 0;
-}
-
-void World::AddQueuedPlayer(WorldSession* sess)
-{
- m_QueuedPlayer.push_back (sess);
-
- // The 1st SMSG_AUTH_RESPONSE needs to contain other info too.
- WorldPacket packet (SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1);
- packet << uint8 (AUTH_WAIT_QUEUE);
- packet << uint32 (0); // unknown random value...
- packet << uint8 (0);
- packet << uint32 (0);
- packet << uint8 (sess->IsTBC () ? 1 : 0); // 0 - normal, 1 - TBC, must be set in database manually for each account
- packet << uint32(GetQueuePos (sess));
- sess->SendPacket (&packet);
-
- //sess->SendAuthWaitQue (GetQueuePos (sess));
-}
-
-void World::RemoveQueuedPlayer(WorldSession* sess)
-{
- // sessions count including queued to remove (if removed_session set)
- uint32 sessions = GetActiveSessionCount();
-
- uint32 position = 1;
- Queue::iterator iter = m_QueuedPlayer.begin();
-
- // if session not queued then we need decrease sessions count (Remove socked callet before session removing from session list)
- bool decrease_session = true;
-
- // search to remove and count skipped positions
- for(;iter != m_QueuedPlayer.end(); ++iter, ++position)
- {
- if(*iter==sess)
- {
- Queue::iterator iter2 = iter;
- ++iter;
- m_QueuedPlayer.erase(iter2);
- decrease_session = false; // removing queued session
- break;
- }
- }
-
- // iter point to next socked after removed or end()
- // position store position of removed socket and then new position next socket after removed
-
- // decrease for case session queued for removing
- if(decrease_session && sessions)
- --sessions;
-
- // accept first in queue
- if( (!m_playerLimit || sessions < m_playerLimit) && !m_QueuedPlayer.empty() )
- {
- WorldSession * socket = m_QueuedPlayer.front();
- socket->SendAuthWaitQue(0);
- m_QueuedPlayer.pop_front();
-
- // update iter to point first queued socket or end() if queue is empty now
- iter = m_QueuedPlayer.begin();
- position = 1;
- }
-
- // update position from iter to end()
- // iter point to first not updated socket, position store new position
- for(; iter != m_QueuedPlayer.end(); ++iter, ++position)
- (*iter)->SendAuthWaitQue(position);
-}
-
-/// Find a Weather object by the given zoneid
-Weather* World::FindWeather(uint32 id) const
-{
- WeatherMap::const_iterator itr = m_weathers.find(id);
-
- if(itr != m_weathers.end())
- return itr->second;
- else
- return 0;
-}
-
-/// Remove a Weather object for the given zoneid
-void World::RemoveWeather(uint32 id)
-{
- // not called at the moment. Kept for completeness
- WeatherMap::iterator itr = m_weathers.find(id);
-
- if(itr != m_weathers.end())
- {
- delete itr->second;
- m_weathers.erase(itr);
- }
-}
-
-/// Add a Weather object to the list
-Weather* World::AddWeather(uint32 zone_id)
-{
- WeatherZoneChances const* weatherChances = objmgr.GetWeatherChances(zone_id);
-
- // zone not have weather, ignore
- if(!weatherChances)
- return NULL;
-
- Weather* w = new Weather(zone_id,weatherChances);
- m_weathers[w->GetZone()] = w;
- w->ReGenerate();
- w->UpdateWeather();
- return w;
-}
-
-/// Initialize config values
-void World::LoadConfigSettings(bool reload)
-{
- if(reload)
- {
- if(!sConfig.Reload())
- {
- sLog.outError("World settings reload fail: can't read settings from %s.",sConfig.GetFilename().c_str());
- return;
- }
- //TODO Check if config is outdated
- }
-
- ///- Read the player limit and the Message of the day from the config file
- SetPlayerLimit( sConfig.GetIntDefault("PlayerLimit", DEFAULT_PLAYER_LIMIT), true );
- SetMotd( sConfig.GetStringDefault("Motd", "Welcome to the Massive Network Game Object Server." ) );
-
- ///- Read all rates from the config file
- rate_values[RATE_HEALTH] = sConfig.GetFloatDefault("Rate.Health", 1);
- if(rate_values[RATE_HEALTH] < 0)
- {
- sLog.outError("Rate.Health (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_HEALTH]);
- rate_values[RATE_HEALTH] = 1;
- }
- rate_values[RATE_POWER_MANA] = sConfig.GetFloatDefault("Rate.Mana", 1);
- if(rate_values[RATE_POWER_MANA] < 0)
- {
- sLog.outError("Rate.Mana (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_POWER_MANA]);
- rate_values[RATE_POWER_MANA] = 1;
- }
- rate_values[RATE_POWER_RAGE_INCOME] = sConfig.GetFloatDefault("Rate.Rage.Income", 1);
- rate_values[RATE_POWER_RAGE_LOSS] = sConfig.GetFloatDefault("Rate.Rage.Loss", 1);
- if(rate_values[RATE_POWER_RAGE_LOSS] < 0)
- {
- sLog.outError("Rate.Rage.Loss (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_POWER_RAGE_LOSS]);
- rate_values[RATE_POWER_RAGE_LOSS] = 1;
- }
- rate_values[RATE_POWER_FOCUS] = sConfig.GetFloatDefault("Rate.Focus", 1.0f);
- rate_values[RATE_LOYALTY] = sConfig.GetFloatDefault("Rate.Loyalty", 1.0f);
- rate_values[RATE_SKILL_DISCOVERY] = sConfig.GetFloatDefault("Rate.Skill.Discovery", 1.0f);
- rate_values[RATE_DROP_ITEM_POOR] = sConfig.GetFloatDefault("Rate.Drop.Item.Poor", 1.0f);
- rate_values[RATE_DROP_ITEM_NORMAL] = sConfig.GetFloatDefault("Rate.Drop.Item.Normal", 1.0f);
- rate_values[RATE_DROP_ITEM_UNCOMMON] = sConfig.GetFloatDefault("Rate.Drop.Item.Uncommon", 1.0f);
- rate_values[RATE_DROP_ITEM_RARE] = sConfig.GetFloatDefault("Rate.Drop.Item.Rare", 1.0f);
- rate_values[RATE_DROP_ITEM_EPIC] = sConfig.GetFloatDefault("Rate.Drop.Item.Epic", 1.0f);
- rate_values[RATE_DROP_ITEM_LEGENDARY] = sConfig.GetFloatDefault("Rate.Drop.Item.Legendary", 1.0f);
- rate_values[RATE_DROP_ITEM_ARTIFACT] = sConfig.GetFloatDefault("Rate.Drop.Item.Artifact", 1.0f);
- rate_values[RATE_DROP_ITEM_REFERENCED] = sConfig.GetFloatDefault("Rate.Drop.Item.Referenced", 1.0f);
- rate_values[RATE_DROP_MONEY] = sConfig.GetFloatDefault("Rate.Drop.Money", 1.0f);
- rate_values[RATE_XP_KILL] = sConfig.GetFloatDefault("Rate.XP.Kill", 1.0f);
- rate_values[RATE_XP_QUEST] = sConfig.GetFloatDefault("Rate.XP.Quest", 1.0f);
- rate_values[RATE_XP_EXPLORE] = sConfig.GetFloatDefault("Rate.XP.Explore", 1.0f);
- rate_values[RATE_XP_PAST_70] = sConfig.GetFloatDefault("Rate.XP.PastLevel70", 1.0f);
- rate_values[RATE_REPUTATION_GAIN] = sConfig.GetFloatDefault("Rate.Reputation.Gain", 1.0f);
- rate_values[RATE_CREATURE_NORMAL_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Normal.Damage", 1.0f);
- rate_values[RATE_CREATURE_ELITE_ELITE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.Elite.Damage", 1.0f);
- rate_values[RATE_CREATURE_ELITE_RAREELITE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RAREELITE.Damage", 1.0f);
- rate_values[RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.WORLDBOSS.Damage", 1.0f);
- rate_values[RATE_CREATURE_ELITE_RARE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RARE.Damage", 1.0f);
- rate_values[RATE_CREATURE_NORMAL_HP] = sConfig.GetFloatDefault("Rate.Creature.Normal.HP", 1.0f);
- rate_values[RATE_CREATURE_ELITE_ELITE_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.Elite.HP", 1.0f);
- rate_values[RATE_CREATURE_ELITE_RAREELITE_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.RAREELITE.HP", 1.0f);
- rate_values[RATE_CREATURE_ELITE_WORLDBOSS_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.WORLDBOSS.HP", 1.0f);
- rate_values[RATE_CREATURE_ELITE_RARE_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.RARE.HP", 1.0f);
- rate_values[RATE_CREATURE_NORMAL_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Normal.SpellDamage", 1.0f);
- rate_values[RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.Elite.SpellDamage", 1.0f);
- rate_values[RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RAREELITE.SpellDamage", 1.0f);
- rate_values[RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.WORLDBOSS.SpellDamage", 1.0f);
- rate_values[RATE_CREATURE_ELITE_RARE_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RARE.SpellDamage", 1.0f);
- rate_values[RATE_CREATURE_AGGRO] = sConfig.GetFloatDefault("Rate.Creature.Aggro", 1.0f);
- rate_values[RATE_REST_INGAME] = sConfig.GetFloatDefault("Rate.Rest.InGame", 1.0f);
- rate_values[RATE_REST_OFFLINE_IN_TAVERN_OR_CITY] = sConfig.GetFloatDefault("Rate.Rest.Offline.InTavernOrCity", 1.0f);
- rate_values[RATE_REST_OFFLINE_IN_WILDERNESS] = sConfig.GetFloatDefault("Rate.Rest.Offline.InWilderness", 1.0f);
- rate_values[RATE_DAMAGE_FALL] = sConfig.GetFloatDefault("Rate.Damage.Fall", 1.0f);
- rate_values[RATE_AUCTION_TIME] = sConfig.GetFloatDefault("Rate.Auction.Time", 1.0f);
- rate_values[RATE_AUCTION_DEPOSIT] = sConfig.GetFloatDefault("Rate.Auction.Deposit", 1.0f);
- rate_values[RATE_AUCTION_CUT] = sConfig.GetFloatDefault("Rate.Auction.Cut", 1.0f);
- rate_values[RATE_HONOR] = sConfig.GetFloatDefault("Rate.Honor",1.0f);
- rate_values[RATE_MINING_AMOUNT] = sConfig.GetFloatDefault("Rate.Mining.Amount",1.0f);
- rate_values[RATE_MINING_NEXT] = sConfig.GetFloatDefault("Rate.Mining.Next",1.0f);
- rate_values[RATE_INSTANCE_RESET_TIME] = sConfig.GetFloatDefault("Rate.InstanceResetTime",1.0f);
- rate_values[RATE_TALENT] = sConfig.GetFloatDefault("Rate.Talent",1.0f);
- if(rate_values[RATE_TALENT] < 0.0f)
- {
- sLog.outError("Rate.Talent (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_TALENT]);
- rate_values[RATE_TALENT] = 1.0f;
- }
- rate_values[RATE_CORPSE_DECAY_LOOTED] = sConfig.GetFloatDefault("Rate.Corpse.Decay.Looted",0.1f);
-
- rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = sConfig.GetFloatDefault("TargetPosRecalculateRange",1.5f);
- if(rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] < CONTACT_DISTANCE)
- {
- sLog.outError("TargetPosRecalculateRange (%f) must be >= %f. Using %f instead.",rate_values[RATE_TARGET_POS_RECALCULATION_RANGE],CONTACT_DISTANCE,CONTACT_DISTANCE);
- rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = CONTACT_DISTANCE;
- }
- else if(rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] > ATTACK_DISTANCE)
- {
- sLog.outError("TargetPosRecalculateRange (%f) must be <= %f. Using %f instead.",rate_values[RATE_TARGET_POS_RECALCULATION_RANGE],ATTACK_DISTANCE,ATTACK_DISTANCE);
- rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = ATTACK_DISTANCE;
- }
-
- rate_values[RATE_DURABILITY_LOSS_DAMAGE] = sConfig.GetFloatDefault("DurabilityLossChance.Damage",0.5f);
- if(rate_values[RATE_DURABILITY_LOSS_DAMAGE] < 0.0f)
- {
- sLog.outError("DurabilityLossChance.Damage (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_DAMAGE]);
- rate_values[RATE_DURABILITY_LOSS_DAMAGE] = 0.0f;
- }
- rate_values[RATE_DURABILITY_LOSS_ABSORB] = sConfig.GetFloatDefault("DurabilityLossChance.Absorb",0.5f);
- if(rate_values[RATE_DURABILITY_LOSS_ABSORB] < 0.0f)
- {
- sLog.outError("DurabilityLossChance.Absorb (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_ABSORB]);
- rate_values[RATE_DURABILITY_LOSS_ABSORB] = 0.0f;
- }
- rate_values[RATE_DURABILITY_LOSS_PARRY] = sConfig.GetFloatDefault("DurabilityLossChance.Parry",0.05f);
- if(rate_values[RATE_DURABILITY_LOSS_PARRY] < 0.0f)
- {
- sLog.outError("DurabilityLossChance.Parry (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_PARRY]);
- rate_values[RATE_DURABILITY_LOSS_PARRY] = 0.0f;
- }
- rate_values[RATE_DURABILITY_LOSS_BLOCK] = sConfig.GetFloatDefault("DurabilityLossChance.Block",0.05f);
- if(rate_values[RATE_DURABILITY_LOSS_BLOCK] < 0.0f)
- {
- sLog.outError("DurabilityLossChance.Block (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_BLOCK]);
- rate_values[RATE_DURABILITY_LOSS_BLOCK] = 0.0f;
- }
-
- ///- Read other configuration items from the config file
-
- m_configs[CONFIG_COMPRESSION] = sConfig.GetIntDefault("Compression", 1);
- if(m_configs[CONFIG_COMPRESSION] < 1 || m_configs[CONFIG_COMPRESSION] > 9)
- {
- sLog.outError("Compression level (%i) must be in range 1..9. Using default compression level (1).",m_configs[CONFIG_COMPRESSION]);
- m_configs[CONFIG_COMPRESSION] = 1;
- }
- m_configs[CONFIG_ADDON_CHANNEL] = sConfig.GetBoolDefault("AddonChannel", true);
- m_configs[CONFIG_GRID_UNLOAD] = sConfig.GetBoolDefault("GridUnload", true);
- m_configs[CONFIG_INTERVAL_SAVE] = sConfig.GetIntDefault("PlayerSaveInterval", 900000);
-
- m_configs[CONFIG_INTERVAL_GRIDCLEAN] = sConfig.GetIntDefault("GridCleanUpDelay", 300000);
- if(m_configs[CONFIG_INTERVAL_GRIDCLEAN] < MIN_GRID_DELAY)
- {
- sLog.outError("GridCleanUpDelay (%i) must be greater %u. Use this minimal value.",m_configs[CONFIG_INTERVAL_GRIDCLEAN],MIN_GRID_DELAY);
- m_configs[CONFIG_INTERVAL_GRIDCLEAN] = MIN_GRID_DELAY;
- }
- if(reload)
- MapManager::Instance().SetGridCleanUpDelay(m_configs[CONFIG_INTERVAL_GRIDCLEAN]);
-
- m_configs[CONFIG_INTERVAL_MAPUPDATE] = sConfig.GetIntDefault("MapUpdateInterval", 100);
- if(m_configs[CONFIG_INTERVAL_MAPUPDATE] < MIN_MAP_UPDATE_DELAY)
- {
- sLog.outError("MapUpdateInterval (%i) must be greater %u. Use this minimal value.",m_configs[CONFIG_INTERVAL_MAPUPDATE],MIN_MAP_UPDATE_DELAY);
- m_configs[CONFIG_INTERVAL_MAPUPDATE] = MIN_MAP_UPDATE_DELAY;
- }
- if(reload)
- MapManager::Instance().SetMapUpdateInterval(m_configs[CONFIG_INTERVAL_MAPUPDATE]);
-
- m_configs[CONFIG_INTERVAL_CHANGEWEATHER] = sConfig.GetIntDefault("ChangeWeatherInterval", 600000);
-
- if(reload)
- {
- uint32 val = sConfig.GetIntDefault("WorldServerPort", DEFAULT_WORLDSERVER_PORT);
- if(val!=m_configs[CONFIG_PORT_WORLD])
- sLog.outError("WorldServerPort option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_PORT_WORLD]);
- }
- else
- m_configs[CONFIG_PORT_WORLD] = sConfig.GetIntDefault("WorldServerPort", DEFAULT_WORLDSERVER_PORT);
-
- if(reload)
- {
- uint32 val = sConfig.GetIntDefault("SocketSelectTime", DEFAULT_SOCKET_SELECT_TIME);
- if(val!=m_configs[CONFIG_SOCKET_SELECTTIME])
- sLog.outError("SocketSelectTime option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[DEFAULT_SOCKET_SELECT_TIME]);
- }
- else
- m_configs[CONFIG_SOCKET_SELECTTIME] = sConfig.GetIntDefault("SocketSelectTime", DEFAULT_SOCKET_SELECT_TIME);
-
-
- m_configs[CONFIG_TCP_NO_DELAY] = sConfig.GetBoolDefault("TcpNoDelay", false);
- m_configs[CONFIG_GROUP_XP_DISTANCE] = sConfig.GetIntDefault("MaxGroupXPDistance", 74);
- /// \todo Add MonsterSight and GuarderSight (with meaning) in mangosd.conf or put them as define
- m_configs[CONFIG_SIGHT_MONSTER] = sConfig.GetIntDefault("MonsterSight", 50);
- m_configs[CONFIG_SIGHT_GUARDER] = sConfig.GetIntDefault("GuarderSight", 50);
-
- if(reload)
- {
- uint32 val = sConfig.GetIntDefault("GameType", 0);
- if(val!=m_configs[CONFIG_GAME_TYPE])
- sLog.outError("GameType option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_GAME_TYPE]);
- }
- else
- m_configs[CONFIG_GAME_TYPE] = sConfig.GetIntDefault("GameType", 0);
-
- if(reload)
- {
- uint32 val = sConfig.GetIntDefault("RealmZone", REALM_ZONE_DEVELOPMENT);
- if(val!=m_configs[CONFIG_REALM_ZONE])
- sLog.outError("RealmZone option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_REALM_ZONE]);
- }
- else
- m_configs[CONFIG_REALM_ZONE] = sConfig.GetIntDefault("RealmZone", REALM_ZONE_DEVELOPMENT);
-
- m_configs[CONFIG_ALLOW_TWO_SIDE_ACCOUNTS] = sConfig.GetBoolDefault("AllowTwoSide.Accounts", false);
- m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Chat",false);
- m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Channel",false);
- m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Group",false);
- m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Guild",false);
- m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Auction",false);
- m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Mail",false);
- m_configs[CONFIG_ALLOW_TWO_SIDE_WHO_LIST] = sConfig.GetBoolDefault("AllowTwoSide.WhoList", false);
- m_configs[CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND] = sConfig.GetBoolDefault("AllowTwoSide.AddFriend", false);
- m_configs[CONFIG_STRICT_PLAYER_NAMES] = sConfig.GetIntDefault("StrictPlayerNames", 0);
- m_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfig.GetIntDefault("StrictCharterNames", 0);
- m_configs[CONFIG_STRICT_PET_NAMES] = sConfig.GetIntDefault("StrictPetNames", 0);
-
- m_configs[CONFIG_CHARACTERS_CREATING_DISABLED] = sConfig.GetIntDefault("CharactersCreatingDisabled", 0);
-
- m_configs[CONFIG_CHARACTERS_PER_REALM] = sConfig.GetIntDefault("CharactersPerRealm", 10);
- if(m_configs[CONFIG_CHARACTERS_PER_REALM] < 1 || m_configs[CONFIG_CHARACTERS_PER_REALM] > 10)
- {
- sLog.outError("CharactersPerRealm (%i) must be in range 1..10. Set to 10.",m_configs[CONFIG_CHARACTERS_PER_REALM]);
- m_configs[CONFIG_CHARACTERS_PER_REALM] = 10;
- }
-
- // must be after CONFIG_CHARACTERS_PER_REALM
- m_configs[CONFIG_CHARACTERS_PER_ACCOUNT] = sConfig.GetIntDefault("CharactersPerAccount", 50);
- if(m_configs[CONFIG_CHARACTERS_PER_ACCOUNT] < m_configs[CONFIG_CHARACTERS_PER_REALM])
- {
- sLog.outError("CharactersPerAccount (%i) can't be less than CharactersPerRealm (%i).",m_configs[CONFIG_CHARACTERS_PER_ACCOUNT],m_configs[CONFIG_CHARACTERS_PER_REALM]);
- m_configs[CONFIG_CHARACTERS_PER_ACCOUNT] = m_configs[CONFIG_CHARACTERS_PER_REALM];
- }
-
- m_configs[CONFIG_SKIP_CINEMATICS] = sConfig.GetIntDefault("SkipCinematics", 0);
- if(m_configs[CONFIG_SKIP_CINEMATICS] < 0 || m_configs[CONFIG_SKIP_CINEMATICS] > 2)
- {
- sLog.outError("SkipCinematics (%i) must be in range 0..2. Set to 0.",m_configs[CONFIG_SKIP_CINEMATICS]);
- m_configs[CONFIG_SKIP_CINEMATICS] = 0;
- }
-
-
- if(reload)
- {
- uint32 val = sConfig.GetIntDefault("MaxPlayerLevel", 60);
- if(val!=m_configs[CONFIG_MAX_PLAYER_LEVEL])
- sLog.outError("MaxPlayerLevel option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_MAX_PLAYER_LEVEL]);
- }
- else
- m_configs[CONFIG_MAX_PLAYER_LEVEL] = sConfig.GetIntDefault("MaxPlayerLevel", 60);
- if(m_configs[CONFIG_MAX_PLAYER_LEVEL] > 255)
- {
- sLog.outError("MaxPlayerLevel (%i) must be in range 1..255. Set to 255.",m_configs[CONFIG_MAX_PLAYER_LEVEL]);
- m_configs[CONFIG_MAX_PLAYER_LEVEL] = 255;
- }
-
- m_configs[CONFIG_START_PLAYER_LEVEL] = sConfig.GetIntDefault("StartPlayerLevel", 1);
- if(m_configs[CONFIG_START_PLAYER_LEVEL] < 1)
- {
- sLog.outError("StartPlayerLevel (%i) must be in range 1..MaxPlayerLevel(%u). Set to 1.",m_configs[CONFIG_START_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL]);
- m_configs[CONFIG_START_PLAYER_LEVEL] = 1;
- }
- else if(m_configs[CONFIG_START_PLAYER_LEVEL] > m_configs[CONFIG_MAX_PLAYER_LEVEL])
- {
- sLog.outError("StartPlayerLevel (%i) must be in range 1..MaxPlayerLevel(%u). Set to %u.",m_configs[CONFIG_START_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL]);
- m_configs[CONFIG_START_PLAYER_LEVEL] = m_configs[CONFIG_MAX_PLAYER_LEVEL];
- }
- m_configs[CONFIG_MAX_HONOR_POINTS] = sConfig.GetIntDefault("MaxHonorPoints", 75000);
- m_configs[CONFIG_MAX_ARENA_POINTS] = sConfig.GetIntDefault("MaxArenaPoints", 5000);
-
- m_configs[CONFIG_INSTANCE_IGNORE_LEVEL] = sConfig.GetBoolDefault("Instance.IgnoreLevel", false);
- m_configs[CONFIG_INSTANCE_IGNORE_RAID] = sConfig.GetBoolDefault("Instance.IgnoreRaid", false);
-
- m_configs[CONFIG_BATTLEGROUND_CAST_DESERTER] = sConfig.GetBoolDefault("Battleground.CastDeserter", true);
-
- m_configs[CONFIG_CAST_UNSTUCK] = sConfig.GetBoolDefault("CastUnstuck", true);
- m_configs[CONFIG_INSTANCE_RESET_TIME_HOUR] = sConfig.GetIntDefault("Instance.ResetTimeHour", 4);
- m_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfig.GetIntDefault("Instance.UnloadDelay", 1800000);
-
- m_configs[CONFIG_MAX_PRIMARY_TRADE_SKILL] = sConfig.GetIntDefault("MaxPrimaryTradeSkill", 2);
- m_configs[CONFIG_MIN_PETITION_SIGNS] = sConfig.GetIntDefault("MinPetitionSigns", 9);
- if(m_configs[CONFIG_MIN_PETITION_SIGNS] > 9)
- {
- sLog.outError("MinPetitionSigns (%i) must be in range 0..9. Set to 9.",m_configs[CONFIG_MIN_PETITION_SIGNS]);
- m_configs[CONFIG_MIN_PETITION_SIGNS] = 9;
- }
-
- 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);
-
- m_configs[CONFIG_MAIL_DELIVERY_DELAY] = sConfig.GetIntDefault("MailDeliveryDelay",HOUR);
-
- m_configs[CONFIG_UPTIME_UPDATE] = sConfig.GetIntDefault("UpdateUptimeInterval", 10);
- if(m_configs[CONFIG_UPTIME_UPDATE]<=0)
- {
- sLog.outError("UpdateUptimeInterval (%i) must be > 0, set to default 10.",m_configs[CONFIG_UPTIME_UPDATE]);
- m_configs[CONFIG_UPTIME_UPDATE] = 10;
- }
- if(reload)
- {
- m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*1000);
- m_timers[WUPDATE_UPTIME].Reset();
- }
-
- m_configs[CONFIG_SKILL_CHANCE_ORANGE] = sConfig.GetIntDefault("SkillChance.Orange",100);
- m_configs[CONFIG_SKILL_CHANCE_YELLOW] = sConfig.GetIntDefault("SkillChance.Yellow",75);
- m_configs[CONFIG_SKILL_CHANCE_GREEN] = sConfig.GetIntDefault("SkillChance.Green",25);
- m_configs[CONFIG_SKILL_CHANCE_GREY] = sConfig.GetIntDefault("SkillChance.Grey",0);
-
- m_configs[CONFIG_SKILL_CHANCE_MINING_STEPS] = sConfig.GetIntDefault("SkillChance.MiningSteps",75);
- m_configs[CONFIG_SKILL_CHANCE_SKINNING_STEPS] = sConfig.GetIntDefault("SkillChance.SkinningSteps",75);
-
- m_configs[CONFIG_SKILL_PROSPECTING] = sConfig.GetBoolDefault("SkillChance.Prospecting",false);
-
- m_configs[CONFIG_SKILL_GAIN_CRAFTING] = sConfig.GetIntDefault("SkillGain.Crafting", 1);
- if(m_configs[CONFIG_SKILL_GAIN_CRAFTING] < 0)
- {
- sLog.outError("SkillGain.Crafting (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_CRAFTING]);
- m_configs[CONFIG_SKILL_GAIN_CRAFTING] = 1;
- }
-
- m_configs[CONFIG_SKILL_GAIN_DEFENSE] = sConfig.GetIntDefault("SkillGain.Defense", 1);
- if(m_configs[CONFIG_SKILL_GAIN_DEFENSE] < 0)
- {
- sLog.outError("SkillGain.Defense (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_DEFENSE]);
- m_configs[CONFIG_SKILL_GAIN_DEFENSE] = 1;
- }
-
- m_configs[CONFIG_SKILL_GAIN_GATHERING] = sConfig.GetIntDefault("SkillGain.Gathering", 1);
- if(m_configs[CONFIG_SKILL_GAIN_GATHERING] < 0)
- {
- sLog.outError("SkillGain.Gathering (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_GATHERING]);
- m_configs[CONFIG_SKILL_GAIN_GATHERING] = 1;
- }
-
- m_configs[CONFIG_SKILL_GAIN_WEAPON] = sConfig.GetIntDefault("SkillGain.Weapon", 1);
- if(m_configs[CONFIG_SKILL_GAIN_WEAPON] < 0)
- {
- sLog.outError("SkillGain.Weapon (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_WEAPON]);
- m_configs[CONFIG_SKILL_GAIN_WEAPON] = 1;
- }
-
- m_configs[CONFIG_MAX_OVERSPEED_PINGS] = sConfig.GetIntDefault("MaxOverspeedPings",2);
- if(m_configs[CONFIG_MAX_OVERSPEED_PINGS] != 0 && m_configs[CONFIG_MAX_OVERSPEED_PINGS] < 2)
- {
- sLog.outError("MaxOverspeedPings (%i) must be in range 2..infinity (or 0 to disable check. Set to 2.",m_configs[CONFIG_MAX_OVERSPEED_PINGS]);
- m_configs[CONFIG_MAX_OVERSPEED_PINGS] = 2;
- }
-
- m_configs[CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY] = sConfig.GetBoolDefault("SaveRespawnTimeImmediately",true);
- m_configs[CONFIG_WEATHER] = sConfig.GetBoolDefault("ActivateWeather",true);
-
- if(reload)
- {
- uint32 val = sConfig.GetIntDefault("Expansion",1);
- if(val!=m_configs[CONFIG_EXPANSION])
- sLog.outError("Expansion option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_EXPANSION]);
- }
- else
- m_configs[CONFIG_EXPANSION] = sConfig.GetIntDefault("Expansion",1);
-
- m_configs[CONFIG_CHATFLOOD_MESSAGE_COUNT] = sConfig.GetIntDefault("ChatFlood.MessageCount",10);
- m_configs[CONFIG_CHATFLOOD_MESSAGE_DELAY] = sConfig.GetIntDefault("ChatFlood.MessageDelay",1);
- m_configs[CONFIG_CHATFLOOD_MUTE_TIME] = sConfig.GetIntDefault("ChatFlood.MuteTime",10);
-
- m_configs[CONFIG_EVENT_ANNOUNCE] = sConfig.GetIntDefault("Event.Announce",0);
-
- m_configs[CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS] = sConfig.GetIntDefault("CreatureFamilyAssistenceRadius",10);
-
- m_configs[CONFIG_WORLD_BOSS_LEVEL_DIFF] = sConfig.GetIntDefault("WorldBossLevelDiff",3);
-
- // note: disable value (-1) will assigned as 0xFFFFFFF, to prevent overflow at calculations limit it to max possible player level (255)
- m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] = sConfig.GetIntDefault("Quests.LowLevelHideDiff",4);
- if(m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] > 255)
- m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] = 255;
- m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] = sConfig.GetIntDefault("Quests.HighLevelHideDiff",7);
- if(m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] > 255)
- m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] = 255;
-
- m_configs[CONFIG_RESTRICTED_LFG_CHANNEL] = sConfig.GetBoolDefault("Channel.RestrictedLfg", true);
- m_configs[CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL] = sConfig.GetBoolDefault("Channel.SilentlyGMJoin", false);
-
- m_configs[CONFIG_TALENTS_INSPECTING] = sConfig.GetBoolDefault("TalentsInspecting", true);
- m_configs[CONFIG_CHAT_FAKE_MESSAGE_PREVENTING] = sConfig.GetBoolDefault("ChatFakeMessagePreventing", false);
-
- m_configs[CONFIG_CORPSE_DECAY_NORMAL] = sConfig.GetIntDefault("Corpse.Decay.NORMAL", 60);
- m_configs[CONFIG_CORPSE_DECAY_RARE] = sConfig.GetIntDefault("Corpse.Decay.RARE", 300);
- m_configs[CONFIG_CORPSE_DECAY_ELITE] = sConfig.GetIntDefault("Corpse.Decay.ELITE", 300);
- m_configs[CONFIG_CORPSE_DECAY_RAREELITE] = sConfig.GetIntDefault("Corpse.Decay.RAREELITE", 300);
- m_configs[CONFIG_CORPSE_DECAY_WORLDBOSS] = sConfig.GetIntDefault("Corpse.Decay.WORLDBOSS", 3600);
-
- m_configs[CONFIG_DEATH_SICKNESS_LEVEL] = sConfig.GetIntDefault("Death.SicknessLevel", 11);
- m_configs[CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP] = sConfig.GetBoolDefault("Death.CorpseReclaimDelay.PvP", true);
- m_configs[CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE] = sConfig.GetBoolDefault("Death.CorpseReclaimDelay.PvE", true);
-
- m_configs[CONFIG_THREAT_RADIUS] = sConfig.GetIntDefault("ThreatRadius", 100);
-
- // always use declined names in the russian client
- m_configs[CONFIG_DECLINED_NAMES_USED] =
- (m_configs[CONFIG_REALM_ZONE] == REALM_ZONE_RUSSIAN) ? true : sConfig.GetBoolDefault("DeclinedNames", false);
-
- m_configs[CONFIG_LISTEN_RANGE_SAY] = sConfig.GetIntDefault("ListenRange.Say", 25);
- m_configs[CONFIG_LISTEN_RANGE_TEXTEMOTE] = sConfig.GetIntDefault("ListenRange.TextEmote", 25);
- m_configs[CONFIG_LISTEN_RANGE_YELL] = sConfig.GetIntDefault("ListenRange.Yell", 300);
-
- m_PlayerStartGold = sConfig.GetFloatDefault("PlayerStart.Gold", 0);
- if(m_PlayerStartGold < 0)
- m_PlayerStartGold = 0;
-
- if(m_PlayerStartGold > MAX_MONEY_AMOUNT)
- m_PlayerStartGold = MAX_MONEY_AMOUNT;
-
- m_configs[CONFIG_PLAYER_START_HONOR] = sConfig.GetIntDefault("PlayerStart.HonorPoints", 0);
- if(m_configs[CONFIG_PLAYER_START_HONOR] < 0)
- m_configs[CONFIG_PLAYER_START_HONOR] = 0;
-
- m_configs[CONFIG_PLAYER_START_ARENAPTS] = sConfig.GetIntDefault("PlayerStart.ArenaPoints", 0);
- if(m_configs[CONFIG_PLAYER_START_ARENAPTS] < 0)
- m_configs[CONFIG_PLAYER_START_ARENAPTS] = 0;
-
- m_configs[CONFIG_GM_START_LEVEL] = sConfig.GetIntDefault("GamemasterStartLevel", 70);
- if(m_configs[CONFIG_GM_START_LEVEL] < 1)
- m_configs[CONFIG_GM_START_LEVEL] = 1;
-
- m_configs[CONFIG_INSTANT_LOGOUT] = sConfig.GetBoolDefault("PlayerInstantLogout", false);
- m_configs[CONFIG_BG_START_MUSIC] = sConfig.GetBoolDefault("MusicInBattleground", false);
- m_configs[CONFIG_START_ALL_SPELLS] = sConfig.GetBoolDefault("PlayerStart.AllSpells", false);
- // Leaving GM queue option out for now, it's not 100% functional with the ACE patch
- //m_configs[CONFIG_QUEUE_FOR_GM] = sConfig.GetBoolDefault("EnableQueueForGMs", false);
- m_configs[CONFIG_HONOR_AFTER_DUEL] = sConfig.GetIntDefault("HonorPointsAfterDuel", 0);
- if(m_configs[CONFIG_HONOR_AFTER_DUEL] < 0)
- m_configs[CONFIG_HONOR_AFTER_DUEL]= 0;
- m_configs[CONFIG_KICK_FROM_GMISLAND] = sConfig.GetBoolDefault("AntiCheat.GMIsland", false);
- m_configs[CONFIG_START_ALL_EXPLORED] = sConfig.GetBoolDefault("PlayerStart.MapsExplored", false);
- m_configs[CONFIG_DISABLE_BREATHING] = sConfig.GetBoolDefault("DisableWaterBreath", false);
- m_configs[CONFIG_DISABLE_RES_SICKNESS] = sConfig.GetBoolDefault("DisableResurrectSickness", false);
- m_configs[CONFIG_START_ALL_REP] = sConfig.GetBoolDefault("PlayerStart.AllReputation", false);
- m_configs[CONFIG_ALWAYS_MAXSKILL] = sConfig.GetBoolDefault("AlwaysMaxWeaponSkill", false);
- m_configs[CONFIG_START_ALL_TAXI] = sConfig.GetBoolDefault("PlayerStart.AllFlightPaths", false);
- m_configs[CONFIG_PVP_TOKEN_ENABLE] = sConfig.GetBoolDefault("PvPToken.Enable", false);
- m_configs[CONFIG_PVP_TOKEN_MAP_TYPE] = sConfig.GetIntDefault("PvPToken.MapAllowType", 4);
- m_configs[CONFIG_PVP_TOKEN_ID] = sConfig.GetIntDefault("PvPToken.ItemID", 29434);
- m_configs[CONFIG_PVP_TOKEN_COUNT] = sConfig.GetIntDefault("PvPToken.ItemCount", 1);
- if(m_configs[CONFIG_PVP_TOKEN_COUNT] < 1)
- m_configs[CONFIG_PVP_TOKEN_COUNT] = 1;
- m_configs[CONFIG_NO_RESET_TALENT_COST] = sConfig.GetBoolDefault("NoResetTalentsCost", false);
-
- m_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfig.GetIntDefault("Arena.MaxRatingDifference", 0);
- m_configs[CONFIG_ARENA_RATING_DISCARD_TIMER] = sConfig.GetIntDefault("Arena.RatingDiscardTimer",300000);
- m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfig.GetBoolDefault("Arena.AutoDistributePoints", false);
- m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfig.GetIntDefault("Arena.AutoDistributeInterval", 7);
-
- m_configs[CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER] = sConfig.GetIntDefault("BattleGround.PrematureFinishTimer", 0);
-
- m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1);
- if(m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
- {
- sLog.outError("Visibility.Distance.Grey.Unit can't be greater %f",MAX_VISIBILITY_DISTANCE);
- m_VisibleUnitGreyDistance = MAX_VISIBILITY_DISTANCE;
- }
- m_VisibleObjectGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Object", 10);
- if(m_VisibleObjectGreyDistance > MAX_VISIBILITY_DISTANCE)
- {
- sLog.outError("Visibility.Distance.Grey.Object can't be greater %f",MAX_VISIBILITY_DISTANCE);
- m_VisibleObjectGreyDistance = MAX_VISIBILITY_DISTANCE;
- }
-
- m_MaxVisibleDistanceForCreature = sConfig.GetFloatDefault("Visibility.Distance.Creature", DEFAULT_VISIBILITY_DISTANCE);
- if(m_MaxVisibleDistanceForCreature < 45*sWorld.getRate(RATE_CREATURE_AGGRO))
- {
- sLog.outError("Visibility.Distance.Creature can't be less max aggro radius %f",45*sWorld.getRate(RATE_CREATURE_AGGRO));
- m_MaxVisibleDistanceForCreature = 45*sWorld.getRate(RATE_CREATURE_AGGRO);
- }
- else if(m_MaxVisibleDistanceForCreature + m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
- {
- sLog.outError("Visibility. Distance .Creature can't be greater %f",MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance);
- m_MaxVisibleDistanceForCreature = MAX_VISIBILITY_DISTANCE-m_VisibleUnitGreyDistance;
- }
- m_MaxVisibleDistanceForPlayer = sConfig.GetFloatDefault("Visibility.Distance.Player", DEFAULT_VISIBILITY_DISTANCE);
- if(m_MaxVisibleDistanceForPlayer < 45*sWorld.getRate(RATE_CREATURE_AGGRO))
- {
- sLog.outError("Visibility.Distance.Player can't be less max aggro radius %f",45*sWorld.getRate(RATE_CREATURE_AGGRO));
- m_MaxVisibleDistanceForPlayer = 45*sWorld.getRate(RATE_CREATURE_AGGRO);
- }
- else if(m_MaxVisibleDistanceForPlayer + m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
- {
- sLog.outError("Visibility.Distance.Player can't be greater %f",MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance);
- m_MaxVisibleDistanceForPlayer = MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance;
- }
- m_MaxVisibleDistanceForObject = sConfig.GetFloatDefault("Visibility.Distance.Gameobject", DEFAULT_VISIBILITY_DISTANCE);
- if(m_MaxVisibleDistanceForObject < INTERACTION_DISTANCE)
- {
- sLog.outError("Visibility.Distance.Object can't be less max aggro radius %f",float(INTERACTION_DISTANCE));
- m_MaxVisibleDistanceForObject = INTERACTION_DISTANCE;
- }
- else if(m_MaxVisibleDistanceForObject + m_VisibleObjectGreyDistance > MAX_VISIBILITY_DISTANCE)
- {
- sLog.outError("Visibility.Distance.Object can't be greater %f",MAX_VISIBILITY_DISTANCE-m_VisibleObjectGreyDistance);
- m_MaxVisibleDistanceForObject = MAX_VISIBILITY_DISTANCE - m_VisibleObjectGreyDistance;
- }
- m_MaxVisibleDistanceInFlight = sConfig.GetFloatDefault("Visibility.Distance.InFlight", DEFAULT_VISIBILITY_DISTANCE);
- if(m_MaxVisibleDistanceInFlight + m_VisibleObjectGreyDistance > MAX_VISIBILITY_DISTANCE)
- {
- sLog.outError("Visibility.Distance.InFlight can't be greater %f",MAX_VISIBILITY_DISTANCE-m_VisibleObjectGreyDistance);
- m_MaxVisibleDistanceInFlight = MAX_VISIBILITY_DISTANCE - m_VisibleObjectGreyDistance;
- }
-
- ///- Read the "Data" directory from the config file
- std::string dataPath = sConfig.GetStringDefault("DataDir","./");
- if( dataPath.at(dataPath.length()-1)!='/' && dataPath.at(dataPath.length()-1)!='\\' )
- dataPath.append("/");
-
- if(reload)
- {
- if(dataPath!=m_dataPath)
- sLog.outError("DataDir option can't be changed at mangosd.conf reload, using current value (%s).",m_dataPath.c_str());
- }
- else
- {
- m_dataPath = dataPath;
- sLog.outString("Using DataDir %s",m_dataPath.c_str());
- }
-
- bool enableLOS = sConfig.GetBoolDefault("vmap.enableLOS", false);
- bool enableHeight = sConfig.GetBoolDefault("vmap.enableHeight", false);
- std::string ignoreMapIds = sConfig.GetStringDefault("vmap.ignoreMapIds", "");
- std::string ignoreSpellIds = sConfig.GetStringDefault("vmap.ignoreSpellIds", "");
- VMAP::VMapFactory::createOrGetVMapManager()->setEnableLineOfSightCalc(enableLOS);
- VMAP::VMapFactory::createOrGetVMapManager()->setEnableHeightCalc(enableHeight);
- VMAP::VMapFactory::createOrGetVMapManager()->preventMapsFromBeingUsed(ignoreMapIds.c_str());
- VMAP::VMapFactory::preventSpellsFromBeingTestedForLoS(ignoreSpellIds.c_str());
- sLog.outString( "WORLD: VMap support included. LineOfSight:%i, getHeight:%i",enableLOS, enableHeight);
- sLog.outString( "WORLD: VMap data directory is: %svmaps",m_dataPath.c_str());
- sLog.outString( "WORLD: VMap config keys are: vmap.enableLOS, vmap.enableHeight, vmap.ignoreMapIds, vmap.ignoreSpellIds");
-}
-
-/// Initialize the World
-void World::SetInitialWorldSettings()
-{
- ///- Initialize the random number generator
- srand((unsigned int)time(NULL));
-
- ///- Initialize config settings
- LoadConfigSettings();
-
- ///- Init highest guids before any table loading to prevent using not initialized guids in some code.
- objmgr.SetHighestGuids();
-
- ///- Check the existence of the map files for all races' startup areas.
- if( !MapManager::ExistMapAndVMap(0,-6240.32f, 331.033f)
- ||!MapManager::ExistMapAndVMap(0,-8949.95f,-132.493f)
- ||!MapManager::ExistMapAndVMap(0,-8949.95f,-132.493f)
- ||!MapManager::ExistMapAndVMap(1,-618.518f,-4251.67f)
- ||!MapManager::ExistMapAndVMap(0, 1676.35f, 1677.45f)
- ||!MapManager::ExistMapAndVMap(1, 10311.3f, 832.463f)
- ||!MapManager::ExistMapAndVMap(1,-2917.58f,-257.98f)
- ||m_configs[CONFIG_EXPANSION] && (
- !MapManager::ExistMapAndVMap(530,10349.6f,-6357.29f) || !MapManager::ExistMapAndVMap(530,-3961.64f,-13931.2f) ) )
- {
- sLog.outError("Correct *.map files not found in path '%smaps' or *.vmap/*vmdir files in '%svmaps'. Please place *.map/*.vmap/*.vmdir files in appropriate directories or correct the DataDir value in the mangosd.conf file.",m_dataPath.c_str(),m_dataPath.c_str());
- exit(1);
- }
-
- ///- Loading strings. Getting no records means core load has to be canceled because no error message can be output.
- sLog.outString( "" );
- sLog.outString( "Loading MaNGOS strings..." );
- if (!objmgr.LoadMangosStrings())
- exit(1); // Error message displayed in function already
-
- ///- Update the realm entry in the database with the realm type from the config file
- //No SQL injection as values are treated as integers
-
- // not send custom type REALM_FFA_PVP to realm list
- uint32 server_type = IsFFAPvPRealm() ? REALM_TYPE_PVP : getConfig(CONFIG_GAME_TYPE);
- uint32 realm_zone = getConfig(CONFIG_REALM_ZONE);
- loginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realmID);
-
- ///- Remove the bones after a restart
- CharacterDatabase.PExecute("DELETE FROM corpse WHERE corpse_type = '0'");
-
- ///- Load the DBC files
- sLog.outString("Initialize data stores...");
- LoadDBCStores(m_dataPath);
- DetectDBCLang();
-
- sLog.outString( "Loading InstanceTemplate" );
- objmgr.LoadInstanceTemplate();
-
- sLog.outString( "Loading SkillLineAbilityMultiMap Data..." );
- spellmgr.LoadSkillLineAbilityMap();
-
- ///- Clean up and pack instances
- sLog.outString( "Cleaning up instances..." );
- sInstanceSaveManager.CleanupInstances(); // must be called before `creature_respawn`/`gameobject_respawn` tables
-
- sLog.outString( "Packing instances..." );
- sInstanceSaveManager.PackInstances();
-
- sLog.outString( "Loading Localization strings..." );
- objmgr.LoadCreatureLocales();
- objmgr.LoadGameObjectLocales();
- objmgr.LoadItemLocales();
- objmgr.LoadQuestLocales();
- objmgr.LoadNpcTextLocales();
- objmgr.LoadPageTextLocales();
- objmgr.SetDBCLocaleIndex(GetDefaultDbcLocale()); // Get once for all the locale index of DBC language (console/broadcasts)
-
- sLog.outString( "Loading Page Texts..." );
- objmgr.LoadPageTexts();
-
- sLog.outString( "Loading Game Object Templates..." ); // must be after LoadPageTexts
- objmgr.LoadGameobjectInfo();
-
- sLog.outString( "Loading Spell Chain Data..." );
- spellmgr.LoadSpellChains();
-
- sLog.outString( "Loading Spell Elixir types..." );
- spellmgr.LoadSpellElixirs();
-
- sLog.outString( "Loading Spell Learn Skills..." );
- spellmgr.LoadSpellLearnSkills(); // must be after LoadSpellChains
-
- sLog.outString( "Loading Spell Learn Spells..." );
- spellmgr.LoadSpellLearnSpells();
-
- sLog.outString( "Loading Spell Proc Event conditions..." );
- spellmgr.LoadSpellProcEvents();
-
- sLog.outString( "Loading Aggro Spells Definitions...");
- spellmgr.LoadSpellThreats();
-
- sLog.outString( "Loading NPC Texts..." );
- objmgr.LoadGossipText();
-
- sLog.outString( "Loading Item Random Enchantments Table..." );
- LoadRandomEnchantmentsTable();
-
- sLog.outString( "Loading Items..." ); // must be after LoadRandomEnchantmentsTable and LoadPageTexts
- objmgr.LoadItemPrototypes();
-
- sLog.outString( "Loading Item Texts..." );
- objmgr.LoadItemTexts();
-
- sLog.outString( "Loading Creature Model Based Info Data..." );
- objmgr.LoadCreatureModelInfo();
-
- sLog.outString( "Loading Equipment templates...");
- objmgr.LoadEquipmentTemplates();
-
- sLog.outString( "Loading Creature templates..." );
- objmgr.LoadCreatureTemplates();
-
- sLog.outString( "Loading SpellsScriptTarget...");
- spellmgr.LoadSpellScriptTarget(); // must be after LoadCreatureTemplates and LoadGameobjectInfo
-
- sLog.outString( "Loading Creature Reputation OnKill Data..." );
- objmgr.LoadReputationOnKill();
-
- sLog.outString( "Loading Pet Create Spells..." );
- objmgr.LoadPetCreateSpells();
-
- sLog.outString( "Loading Creature Data..." );
- objmgr.LoadCreatures();
-
- sLog.outString( "Loading Creature Addon Data..." );
- objmgr.LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures()
-
- sLog.outString( "Loading Creature Respawn Data..." ); // must be after PackInstances()
- objmgr.LoadCreatureRespawnTimes();
-
- sLog.outString( "Loading Gameobject Data..." );
- objmgr.LoadGameobjects();
-
- sLog.outString( "Loading Gameobject Respawn Data..." ); // must be after PackInstances()
- objmgr.LoadGameobjectRespawnTimes();
-
- sLog.outString( "Loading Game Event Data...");
- gameeventmgr.LoadFromDB();
-
- sLog.outString( "Loading Weather Data..." );
- objmgr.LoadWeatherZoneChances();
-
- sLog.outString( "Loading Quests..." );
- objmgr.LoadQuests(); // must be loaded after DBCs, creature_template, item_template, gameobject tables
-
- sLog.outString( "Loading Quests Relations..." );
- objmgr.LoadQuestRelations(); // must be after quest load
-
- sLog.outString( "Loading AreaTrigger definitions..." );
- objmgr.LoadAreaTriggerTeleports(); // must be after item template load
-
- sLog.outString( "Loading Quest Area Triggers..." );
- objmgr.LoadQuestAreaTriggers(); // must be after LoadQuests
-
- sLog.outString( "Loading Tavern Area Triggers..." );
- objmgr.LoadTavernAreaTriggers();
-
- sLog.outString( "Loading AreaTrigger script names..." );
- objmgr.LoadAreaTriggerScripts();
-
-
- sLog.outString( "Loading Graveyard-zone links...");
- objmgr.LoadGraveyardZones();
-
- sLog.outString( "Loading Spell target coordinates..." );
- spellmgr.LoadSpellTargetPositions();
-
- sLog.outString( "Loading SpellAffect definitions..." );
- spellmgr.LoadSpellAffects();
-
- sLog.outString( "Loading spell pet auras..." );
- spellmgr.LoadSpellPetAuras();
-
- sLog.outString( "Loading player Create Info & Level Stats..." );
- objmgr.LoadPlayerInfo();
-
- sLog.outString( "Loading Exploration BaseXP Data..." );
- objmgr.LoadExplorationBaseXP();
-
- sLog.outString( "Loading Pet Name Parts..." );
- objmgr.LoadPetNames();
-
- sLog.outString( "Loading the max pet number..." );
- objmgr.LoadPetNumber();
-
- sLog.outString( "Loading pet level stats..." );
- objmgr.LoadPetLevelInfo();
-
- sLog.outString( "Loading Player Corpses..." );
- objmgr.LoadCorpses();
-
- sLog.outString( "Loading Loot Tables..." );
- LoadLootTables();
-
- sLog.outString( "Loading Skill Discovery Table..." );
- LoadSkillDiscoveryTable();
-
- sLog.outString( "Loading Skill Extra Item Table..." );
- LoadSkillExtraItemTable();
-
- sLog.outString( "Loading Skill Fishing base level requirements..." );
- objmgr.LoadFishingBaseSkillLevel();
-
- ///- Load dynamic data tables from the database
- sLog.outString( "Loading Auctions..." );
- objmgr.LoadAuctionItems();
- objmgr.LoadAuctions();
-
- sLog.outString( "Loading Guilds..." );
- objmgr.LoadGuilds();
-
- sLog.outString( "Loading ArenaTeams..." );
- objmgr.LoadArenaTeams();
-
- sLog.outString( "Loading Groups..." );
- objmgr.LoadGroups();
-
- sLog.outString( "Loading ReservedNames..." );
- objmgr.LoadReservedPlayersNames();
-
- sLog.outString( "Loading GameObject for quests..." );
- objmgr.LoadGameObjectForQuests();
-
- sLog.outString( "Loading BattleMasters..." );
- objmgr.LoadBattleMastersEntry();
-
- sLog.outString( "Loading GameTeleports..." );
- objmgr.LoadGameTele();
-
- sLog.outString( "Loading Npc Text Id..." );
- objmgr.LoadNpcTextId(); // must be after load Creature and NpcText
-
- sLog.outString( "Loading vendors..." );
- objmgr.LoadVendors(); // must be after load CreatureTemplate and ItemTemplate
-
- sLog.outString( "Loading trainers..." );
- objmgr.LoadTrainerSpell(); // must be after load CreatureTemplate
-
- sLog.outString( "Loading Waypoints..." );
- WaypointMgr.Load();
-
- ///- Handle outdated emails (delete/return)
- sLog.outString( "Returning old mails..." );
- objmgr.ReturnOrDeleteOldMails(false);
-
- ///- Load and initialize scripts
- sLog.outString( "Loading Scripts..." );
- objmgr.LoadQuestStartScripts(); // must be after load Creature/Gameobject(Template/Data) and QuestTemplate
- objmgr.LoadQuestEndScripts(); // must be after load Creature/Gameobject(Template/Data) and QuestTemplate
- objmgr.LoadSpellScripts(); // must be after load Creature/Gameobject(Template/Data)
- objmgr.LoadGameObjectScripts(); // must be after load Creature/Gameobject(Template/Data)
- objmgr.LoadEventScripts(); // must be after load Creature/Gameobject(Template/Data)
-
- sLog.outString( "Initializing Scripts..." );
- if(!LoadScriptingModule())
- exit(1);
-
- ///- Initialize game time and timers
- sLog.outString( "DEBUG:: Initialize game time and timers" );
- m_gameTime = time(NULL);
- m_startTime=m_gameTime;
-
- tm local;
- time_t curr;
- time(&curr);
- local=*(localtime(&curr)); // dereference and assign
- char isoDate[128];
- sprintf( isoDate, "%04d-%02d-%02d %02d:%02d:%02d",
- local.tm_year+1900, local.tm_mon+1, local.tm_mday, local.tm_hour, local.tm_min, local.tm_sec);
-
- WorldDatabase.PExecute("INSERT INTO uptime (startstring, starttime, uptime) VALUES('%s', %ld, 0)", isoDate, m_startTime );
-
- m_timers[WUPDATE_OBJECTS].SetInterval(0);
- m_timers[WUPDATE_SESSIONS].SetInterval(0);
- m_timers[WUPDATE_WEATHERS].SetInterval(1000);
- m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*1000); //set auction update interval to 1 minute
- m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*1000);
- //Update "uptime" table based on configuration entry in minutes.
- m_timers[WUPDATE_CORPSES].SetInterval(20*MINUTE*1000); //erase corpses every 20 minutes
-
- //to set mailtimer to return mails every day between 4 and 5 am
- //mailtimer is increased when updating auctions
- //one second is 1000 -(tested on win system)
- mail_timer = ((((localtime( &m_gameTime )->tm_hour + 20) % 24)* HOUR * 1000) / m_timers[WUPDATE_AUCTIONS].GetInterval() );
- //1440
- mail_timer_expires = ( (DAY * 1000) / (m_timers[WUPDATE_AUCTIONS].GetInterval()));
- sLog.outDebug("Mail timer set to: %u, mail return is called every %u minutes", mail_timer, mail_timer_expires);
-
- ///- Initilize static helper structures
- AIRegistry::Initialize();
- WaypointMovementGenerator<Creature>::Initialize();
- Player::InitVisibleBits();
-
- ///- Initialize MapManager
- sLog.outString( "Starting Map System" );
- MapManager::Instance().Initialize();
-
- ///- Initialize Battlegrounds
- sLog.outString( "Starting BattleGround System" );
- sBattleGroundMgr.CreateInitialBattleGrounds();
- sBattleGroundMgr.InitAutomaticArenaPointDistribution();
-
- //Not sure if this can be moved up in the sequence (with static data loading) as it uses MapManager
- sLog.outString( "Loading Transports..." );
- MapManager::Instance().LoadTransports();
-
- sLog.outString("Deleting expired bans..." );
- loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
-
- sLog.outString("Calculate next daily quest reset time..." );
- InitDailyQuestResetTime();
-
- sLog.outString("Starting Game Event system..." );
- uint32 nextGameEvent = gameeventmgr.Initialize();
- m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent); //depend on next event
-
- sLog.outString( "WORLD: World initialized" );
-}
-void World::DetectDBCLang()
-{
- uint32 m_lang_confid = sConfig.GetIntDefault("DBC.Locale", 255);
-
- if(m_lang_confid != 255 && m_lang_confid >= MAX_LOCALE)
- {
- sLog.outError("Incorrect DBC.Locale! Must be >= 0 and < %d (set to 0)",MAX_LOCALE);
- m_lang_confid = LOCALE_enUS;
- }
-
- ChrRacesEntry const* race = sChrRacesStore.LookupEntry(1);
-
- std::string availableLocalsStr;
-
- int default_locale = MAX_LOCALE;
- for (int i = MAX_LOCALE-1; i >= 0; --i)
- {
- if ( strlen(race->name[i]) > 0) // check by race names
- {
- default_locale = i;
- m_availableDbcLocaleMask |= (1 << i);
- availableLocalsStr += localeNames[i];
- availableLocalsStr += " ";
- }
- }
-
- if( default_locale != m_lang_confid && m_lang_confid < MAX_LOCALE &&
- (m_availableDbcLocaleMask & (1 << m_lang_confid)) )
- {
- default_locale = m_lang_confid;
- }
-
- if(default_locale >= MAX_LOCALE)
- {
- sLog.outError("Unable to determine your DBC Locale! (corrupt DBC?)");
- exit(1);
- }
-
- m_defaultDbcLocale = LocaleConstant(default_locale);
-
- sLog.outString("Using %s DBC Locale as default. All available DBC locales: %s",localeNames[m_defaultDbcLocale],availableLocalsStr.empty() ? "<none>" : availableLocalsStr.c_str());
-}
-
-/// Update the World !
-void World::Update(time_t diff)
-{
- ///- Update the different timers
- for(int i = 0; i < WUPDATE_COUNT; i++)
- if(m_timers[i].GetCurrent()>=0)
- m_timers[i].Update(diff);
- else m_timers[i].SetCurrent(0);
-
- ///- Update the game time and check for shutdown time
- _UpdateGameTime();
-
- /// Handle daily quests reset time
- if(m_gameTime > m_NextDailyQuestReset)
- {
- ResetDailyQuests();
- m_NextDailyQuestReset += DAY;
- }
-
- /// <ul><li> Handle auctions when the timer has passed
- if (m_timers[WUPDATE_AUCTIONS].Passed())
- {
- m_timers[WUPDATE_AUCTIONS].Reset();
-
- ///- Update mails (return old mails with item, or delete them)
- //(tested... works on win)
- if (++mail_timer > mail_timer_expires)
- {
- mail_timer = 0;
- objmgr.ReturnOrDeleteOldMails(true);
- }
-
- AuctionHouseObject* AuctionMap;
- for (int i = 0; i < 3; i++)
- {
- switch (i)
- {
- case 0:
- AuctionMap = objmgr.GetAuctionsMap( 6 );//horde
- break;
- case 1:
- AuctionMap = objmgr.GetAuctionsMap( 2 );//alliance
- break;
- case 2:
- AuctionMap = objmgr.GetAuctionsMap( 7 );//neutral
- break;
- }
-
- ///- Handle expired auctions
- AuctionHouseObject::AuctionEntryMap::iterator itr,next;
- for (itr = AuctionMap->GetAuctionsBegin(); itr != AuctionMap->GetAuctionsEnd();itr = next)
- {
- next = itr;
- ++next;
- if (m_gameTime > (itr->second->time))
- {
- ///- Either cancel the auction if there was no bidder
- if (itr->second->bidder == 0)
- {
- objmgr.SendAuctionExpiredMail( itr->second );
- }
- ///- Or perform the transaction
- else
- {
- //we should send an "item sold" message if the seller is online
- //we send the item to the winner
- //we send the money to the seller
- objmgr.SendAuctionSuccessfulMail( itr->second );
- objmgr.SendAuctionWonMail( itr->second );
- }
-
- ///- In any case clear the auction
- //No SQL injection (Id is integer)
- CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",itr->second->Id);
- objmgr.RemoveAItem(itr->second->item_guidlow);
- delete itr->second;
- AuctionMap->RemoveAuction(itr->first);
- }
- }
- }
- }
-
- /// <li> Handle session updates when the timer has passed
- if (m_timers[WUPDATE_SESSIONS].Passed())
- {
- m_timers[WUPDATE_SESSIONS].Reset();
-
- UpdateSessions(diff);
- }
-
- /// <li> Handle weather updates when the timer has passed
- if (m_timers[WUPDATE_WEATHERS].Passed())
- {
- m_timers[WUPDATE_WEATHERS].Reset();
-
- ///- Send an update signal to Weather objects
- WeatherMap::iterator itr, next;
- for (itr = m_weathers.begin(); itr != m_weathers.end(); itr = next)
- {
- next = itr;
- ++next;
-
- ///- and remove Weather objects for zones with no player
- //As interval > WorldTick
- if(!itr->second->Update(m_timers[WUPDATE_WEATHERS].GetInterval()))
- {
- delete itr->second;
- m_weathers.erase(itr);
- }
- }
- }
- /// <li> Update uptime table
- if (m_timers[WUPDATE_UPTIME].Passed())
- {
- uint32 tmpDiff = (m_gameTime - m_startTime);
- uint32 maxClientsNum = sWorld.GetMaxActiveSessionCount();
-
- m_timers[WUPDATE_UPTIME].Reset();
- WorldDatabase.PExecute("UPDATE uptime SET uptime = %d, maxplayers = %d WHERE starttime = " I64FMTD, tmpDiff, maxClientsNum, uint64(m_startTime));
- }
-
- /// <li> Handle all other objects
- if (m_timers[WUPDATE_OBJECTS].Passed())
- {
- m_timers[WUPDATE_OBJECTS].Reset();
- ///- Update objects when the timer has passed (maps, transport, creatures,...)
- MapManager::Instance().Update(diff); // As interval = 0
-
- ///- Process necessary scripts
- if (!m_scriptSchedule.empty())
- ScriptsProcess();
-
- sBattleGroundMgr.Update(diff);
- }
-
- // execute callbacks from sql queries that were queued recently
- UpdateResultQueue();
-
- ///- Erase corpses once every 20 minutes
- if (m_timers[WUPDATE_CORPSES].Passed())
- {
- m_timers[WUPDATE_CORPSES].Reset();
-
- CorpsesErase();
- }
-
- ///- Process Game events when necessary
- if (m_timers[WUPDATE_EVENTS].Passed())
- {
- m_timers[WUPDATE_EVENTS].Reset(); // to give time for Update() to be processed
- uint32 nextGameEvent = gameeventmgr.Update();
- m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent);
- m_timers[WUPDATE_EVENTS].Reset();
- }
-
- /// </ul>
- ///- Move all creatures with "delayed move" and remove and delete all objects with "delayed remove"
- MapManager::Instance().DoDelayedMovesAndRemoves();
-
- // update the instance reset times
- sInstanceSaveManager.Update();
-
- // And last, but not least handle the issued cli commands
- ProcessCliCommands();
-}
-
-/// Put scripts in the execution queue
-void World::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, Object* target)
-{
- ///- Find the script map
- ScriptMapMap::const_iterator s = scripts.find(id);
- if (s == scripts.end())
- return;
-
- // prepare static data
- uint64 sourceGUID = source->GetGUID();
- uint64 targetGUID = target ? target->GetGUID() : (uint64)0;
- uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0;
-
- ///- Schedule script execution for all scripts in the script map
- ScriptMap const *s2 = &(s->second);
- bool immedScript = false;
- for (ScriptMap::const_iterator iter = s2->begin(); iter != s2->end(); ++iter)
- {
- ScriptAction sa;
- sa.sourceGUID = sourceGUID;
- sa.targetGUID = targetGUID;
- sa.ownerGUID = ownerGUID;
-
- sa.script = &iter->second;
- m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(m_gameTime + iter->first, sa));
- if (iter->first == 0)
- immedScript = true;
- }
- ///- If one of the effects should be immediate, launch the script execution
- if (immedScript)
- ScriptsProcess();
-}
-
-void World::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target)
-{
- // NOTE: script record _must_ exist until command executed
-
- // prepare static data
- uint64 sourceGUID = source->GetGUID();
- uint64 targetGUID = target ? target->GetGUID() : (uint64)0;
- uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0;
-
- ScriptAction sa;
- sa.sourceGUID = sourceGUID;
- sa.targetGUID = targetGUID;
- sa.ownerGUID = ownerGUID;
-
- sa.script = &script;
- m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(m_gameTime + delay, sa));
-
- ///- If effects should be immediate, launch the script execution
- if(delay == 0)
- ScriptsProcess();
-}
-
-/// Process queued scripts
-void World::ScriptsProcess()
-{
- if (m_scriptSchedule.empty())
- return;
-
- ///- Process overdue queued scripts
- std::multimap<time_t, ScriptAction>::iterator iter = m_scriptSchedule.begin();
- // ok as multimap is a *sorted* associative container
- while (!m_scriptSchedule.empty() && (iter->first <= m_gameTime))
- {
- ScriptAction const& step = iter->second;
-
- Object* source = NULL;
-
- if(step.sourceGUID)
- {
- switch(GUID_HIPART(step.sourceGUID))
- {
- case HIGHGUID_ITEM:
- // case HIGHGUID_CONTAINER: ==HIGHGUID_ITEM
- {
- Player* player = HashMapHolder<Player>::Find(step.ownerGUID);
- if(player)
- source = player->GetItemByGuid(step.sourceGUID);
- break;
- }
- case HIGHGUID_UNIT:
- source = HashMapHolder<Creature>::Find(step.sourceGUID);
- break;
- case HIGHGUID_PET:
- source = HashMapHolder<Pet>::Find(step.sourceGUID);
- break;
- case HIGHGUID_PLAYER:
- source = HashMapHolder<Player>::Find(step.sourceGUID);
- break;
- case HIGHGUID_GAMEOBJECT:
- source = HashMapHolder<GameObject>::Find(step.sourceGUID);
- break;
- case HIGHGUID_CORPSE:
- source = HashMapHolder<Corpse>::Find(step.sourceGUID);
- break;
- default:
- sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.sourceGUID));
- break;
- }
- }
-
- Object* target = NULL;
-
- if(step.targetGUID)
- {
- switch(GUID_HIPART(step.targetGUID))
- {
- case HIGHGUID_UNIT:
- target = HashMapHolder<Creature>::Find(step.targetGUID);
- break;
- case HIGHGUID_PET:
- target = HashMapHolder<Pet>::Find(step.targetGUID);
- break;
- case HIGHGUID_PLAYER: // empty GUID case also
- target = HashMapHolder<Player>::Find(step.targetGUID);
- break;
- case HIGHGUID_GAMEOBJECT:
- target = HashMapHolder<GameObject>::Find(step.targetGUID);
- break;
- case HIGHGUID_CORPSE:
- target = HashMapHolder<Corpse>::Find(step.targetGUID);
- break;
- default:
- sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.targetGUID));
- break;
- }
- }
-
- switch (step.script->command)
- {
- case SCRIPT_COMMAND_TALK:
- {
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_TALK call for NULL creature.");
- break;
- }
-
- if(source->GetTypeId()!=TYPEID_UNIT)
- {
- sLog.outError("SCRIPT_COMMAND_TALK call for non-creature (TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
- if(step.script->datalong > 3)
- {
- sLog.outError("SCRIPT_COMMAND_TALK invalid chat type (%u), skipping.",step.script->datalong);
- break;
- }
-
- uint64 unit_target = target ? target->GetGUID() : 0;
-
- //datalong 0=normal say, 1=whisper, 2=yell, 3=emote text
- switch(step.script->datalong)
- {
- case 0: // Say
- ((Creature *)source)->Say(step.script->datatext.c_str(), LANG_UNIVERSAL, unit_target);
- break;
- case 1: // Whisper
- if(!unit_target)
- {
- sLog.outError("SCRIPT_COMMAND_TALK attempt to whisper (%u) NULL, skipping.",step.script->datalong);
- break;
- }
- ((Creature *)source)->Whisper(step.script->datatext.c_str(),unit_target);
- break;
- case 2: // Yell
- ((Creature *)source)->Yell(step.script->datatext.c_str(), LANG_UNIVERSAL, unit_target);
- break;
- case 3: // Emote text
- ((Creature *)source)->TextEmote(step.script->datatext.c_str(), unit_target);
- break;
- default:
- break; // must be already checked at load
- }
- break;
- }
-
- case SCRIPT_COMMAND_EMOTE:
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_EMOTE call for NULL creature.");
- break;
- }
-
- if(source->GetTypeId()!=TYPEID_UNIT)
- {
- sLog.outError("SCRIPT_COMMAND_EMOTE call for non-creature (TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
-
- ((Creature *)source)->HandleEmoteCommand(step.script->datalong);
- break;
- case SCRIPT_COMMAND_FIELD_SET:
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_FIELD_SET call for NULL object.");
- break;
- }
- if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount())
- {
- sLog.outError("SCRIPT_COMMAND_FIELD_SET call for wrong field %u (max count: %u) in object (TypeId: %u).",
- step.script->datalong,source->GetValuesCount(),source->GetTypeId());
- break;
- }
-
- source->SetUInt32Value(step.script->datalong, step.script->datalong2);
- break;
- case SCRIPT_COMMAND_MOVE_TO:
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_MOVE_TO call for NULL creature.");
- break;
- }
-
- if(source->GetTypeId()!=TYPEID_UNIT)
- {
- sLog.outError("SCRIPT_COMMAND_MOVE_TO call for non-creature (TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
- ((Unit *)source)->SendMonsterMoveWithSpeed(step.script->x, step.script->y, step.script->z, ((Unit *)source)->GetUnitMovementFlags(), step.script->datalong2 );
- MapManager::Instance().GetMap(((Unit *)source)->GetMapId(), ((Unit *)source))->CreatureRelocation(((Creature *)source), step.script->x, step.script->y, step.script->z, 0);
- break;
- case SCRIPT_COMMAND_FLAG_SET:
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_FLAG_SET call for NULL object.");
- break;
- }
- if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount())
- {
- sLog.outError("SCRIPT_COMMAND_FLAG_SET call for wrong field %u (max count: %u) in object (TypeId: %u).",
- step.script->datalong,source->GetValuesCount(),source->GetTypeId());
- break;
- }
-
- source->SetFlag(step.script->datalong, step.script->datalong2);
- break;
- case SCRIPT_COMMAND_FLAG_REMOVE:
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for NULL object.");
- break;
- }
- if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount())
- {
- sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for wrong field %u (max count: %u) in object (TypeId: %u).",
- step.script->datalong,source->GetValuesCount(),source->GetTypeId());
- break;
- }
-
- source->RemoveFlag(step.script->datalong, step.script->datalong2);
- break;
-
- case SCRIPT_COMMAND_TELEPORT_TO:
- {
- // accept player in any one from target/source arg
- if (!target && !source)
- {
- sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for NULL object.");
- break;
- }
-
- // must be only Player
- if((!target || target->GetTypeId() != TYPEID_PLAYER) && (!source || source->GetTypeId() != TYPEID_PLAYER))
- {
- sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for non-player (TypeIdSource: %u)(TypeIdTarget: %u), skipping.", source ? source->GetTypeId() : 0, target ? target->GetTypeId() : 0);
- break;
- }
-
- Player* pSource = target && target->GetTypeId() == TYPEID_PLAYER ? (Player*)target : (Player*)source;
-
- pSource->TeleportTo(step.script->datalong, step.script->x, step.script->y, step.script->z, step.script->o);
- break;
- }
-
- case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
- {
- if(!step.script->datalong) // creature not specified
- {
- sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL creature.");
- break;
- }
-
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL world object.");
- break;
- }
-
- WorldObject* summoner = dynamic_cast<WorldObject*>(source);
-
- if(!summoner)
- {
- sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
-
- float x = step.script->x;
- float y = step.script->y;
- float z = step.script->z;
- float o = step.script->o;
-
- Creature* pCreature = summoner->SummonCreature(step.script->datalong, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,step.script->datalong2);
- if (!pCreature)
- {
- sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON failed for creature (entry: %u).",step.script->datalong);
- break;
- }
-
- break;
- }
-
- case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
- {
- if(!step.script->datalong) // gameobject not specified
- {
- sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL gameobject.");
- break;
- }
-
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL world object.");
- break;
- }
-
- WorldObject* summoner = dynamic_cast<WorldObject*>(source);
-
- if(!summoner)
- {
- sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
-
- GameObject *go = NULL;
- int32 time_to_despawn = step.script->datalong2<5 ? 5 : (int32)step.script->datalong2;
-
- CellPair p(MaNGOS::ComputeCellPair(summoner->GetPositionX(), summoner->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
-
- MaNGOS::GameObjectWithDbGUIDCheck go_check(*summoner,step.script->datalong);
- MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(go,go_check);
-
- TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(summoner->GetMapId(), summoner));
-
- if ( !go )
- {
- sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT failed for gameobject(guid: %u).", step.script->datalong);
- break;
- }
-
- if( go->GetGoType()==GAMEOBJECT_TYPE_FISHINGNODE ||
- go->GetGoType()==GAMEOBJECT_TYPE_FISHINGNODE ||
- go->GetGoType()==GAMEOBJECT_TYPE_DOOR ||
- go->GetGoType()==GAMEOBJECT_TYPE_BUTTON ||
- go->GetGoType()==GAMEOBJECT_TYPE_TRAP )
- {
- sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT can not be used with gameobject of type %u (guid: %u).", uint32(go->GetGoType()), step.script->datalong);
- break;
- }
-
- if( go->isSpawned() )
- break; //gameobject already spawned
-
- go->SetLootState(GO_READY);
- go->SetRespawnTime(time_to_despawn); //despawn object in ? seconds
-
- MapManager::Instance().GetMap(go->GetMapId(), go)->Add(go);
- break;
- }
- case SCRIPT_COMMAND_OPEN_DOOR:
- {
- if(!step.script->datalong) // door not specified
- {
- sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL door.");
- break;
- }
-
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL unit.");
- break;
- }
-
- if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player)
- {
- sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
-
- Unit* caster = (Unit*)source;
-
- GameObject *door = NULL;
- int32 time_to_close = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2;
-
- CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
-
- MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong);
- MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(door,go_check);
-
- TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(caster->GetMapId(), (Unit*)source));
-
- if ( !door )
- {
- sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for gameobject(guid: %u).", step.script->datalong);
- break;
- }
- if ( door->GetGoType() != GAMEOBJECT_TYPE_DOOR )
- {
- sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for non-door(GoType: %u).", door->GetGoType());
- break;
- }
-
- if( !door->GetGoState() )
- break; //door already open
-
- door->UseDoorOrButton(time_to_close);
-
- if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON)
- ((GameObject*)target)->UseDoorOrButton(time_to_close);
- break;
- }
- case SCRIPT_COMMAND_CLOSE_DOOR:
- {
- if(!step.script->datalong) // guid for door not specified
- {
- sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL door.");
- break;
- }
-
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL unit.");
- break;
- }
-
- if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player)
- {
- sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
-
- Unit* caster = (Unit*)source;
-
- GameObject *door = NULL;
- int32 time_to_open = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2;
-
- CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY()));
- Cell cell(p);
- cell.data.Part.reserved = ALL_DISTRICT;
-
- MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong);
- MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(door,go_check);
-
- TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
- CellLock<GridReadGuard> cell_lock(cell, p);
- cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(caster->GetMapId(), (Unit*)source));
-
- if ( !door )
- {
- sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for gameobject(guid: %u).", step.script->datalong);
- break;
- }
- if ( door->GetGoType() != GAMEOBJECT_TYPE_DOOR )
- {
- sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for non-door(GoType: %u).", door->GetGoType());
- break;
- }
-
- if( door->GetGoState() )
- break; //door already closed
-
- door->UseDoorOrButton(time_to_open);
-
- if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON)
- ((GameObject*)target)->UseDoorOrButton(time_to_open);
-
- break;
- }
- case SCRIPT_COMMAND_QUEST_EXPLORED:
- {
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL source.");
- break;
- }
-
- if(!target)
- {
- sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL target.");
- break;
- }
-
- // when script called for item spell casting then target == (unit or GO) and source is player
- WorldObject* worldObject;
- Player* player;
-
- if(target->GetTypeId()==TYPEID_PLAYER)
- {
- if(source->GetTypeId()!=TYPEID_UNIT && source->GetTypeId()!=TYPEID_GAMEOBJECT)
- {
- sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
-
- worldObject = (WorldObject*)source;
- player = (Player*)target;
- }
- else
- {
- if(target->GetTypeId()!=TYPEID_UNIT && target->GetTypeId()!=TYPEID_GAMEOBJECT)
- {
- sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",target->GetTypeId());
- break;
- }
-
- if(source->GetTypeId()!=TYPEID_PLAYER)
- {
- sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-player(TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
-
- worldObject = (WorldObject*)target;
- player = (Player*)source;
- }
-
- // quest id and flags checked at script loading
- if( (worldObject->GetTypeId()!=TYPEID_UNIT || ((Unit*)worldObject)->isAlive()) &&
- (step.script->datalong2==0 || worldObject->IsWithinDistInMap(player,float(step.script->datalong2))) )
- player->AreaExploredOrEventHappens(step.script->datalong);
- else
- player->FailQuest(step.script->datalong);
-
- break;
- }
-
- case SCRIPT_COMMAND_ACTIVATE_OBJECT:
- {
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT must have source caster.");
- break;
- }
-
- if(!source->isType(TYPEMASK_UNIT))
- {
- sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT source caster isn't unit (TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
-
- if(!target)
- {
- sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for NULL gameobject.");
- break;
- }
-
- if(target->GetTypeId()!=TYPEID_GAMEOBJECT)
- {
- sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for non-gameobject (TypeId: %u), skipping.",target->GetTypeId());
- break;
- }
-
- Unit* caster = (Unit*)source;
-
- GameObject *go = (GameObject*)target;
-
- go->Use(caster);
- break;
- }
-
- case SCRIPT_COMMAND_REMOVE_AURA:
- {
- Object* cmdTarget = step.script->datalong2 ? source : target;
-
- if(!cmdTarget)
- {
- sLog.outError("SCRIPT_COMMAND_REMOVE_AURA call for NULL %s.",step.script->datalong2 ? "source" : "target");
- break;
- }
-
- if(!cmdTarget->isType(TYPEMASK_UNIT))
- {
- sLog.outError("SCRIPT_COMMAND_REMOVE_AURA %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId());
- break;
- }
-
- ((Unit*)cmdTarget)->RemoveAurasDueToSpell(step.script->datalong);
- break;
- }
-
- case SCRIPT_COMMAND_CAST_SPELL:
- {
- if(!source)
- {
- sLog.outError("SCRIPT_COMMAND_CAST_SPELL must have source caster.");
- break;
- }
-
- if(!source->isType(TYPEMASK_UNIT))
- {
- sLog.outError("SCRIPT_COMMAND_CAST_SPELL source caster isn't unit (TypeId: %u), skipping.",source->GetTypeId());
- break;
- }
-
- Object* cmdTarget = step.script->datalong2 ? source : target;
-
- if(!cmdTarget)
- {
- sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 ? "source" : "target");
- break;
- }
-
- if(!cmdTarget->isType(TYPEMASK_UNIT))
- {
- sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId());
- break;
- }
-
- Unit* spellTarget = (Unit*)cmdTarget;
-
- //TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast
- ((Unit*)source)->CastSpell(spellTarget,step.script->datalong,false);
-
- break;
- }
-
- default:
- sLog.outError("Unknown script command %u called.",step.script->command);
- break;
- }
-
- m_scriptSchedule.erase(iter);
-
- iter = m_scriptSchedule.begin();
- }
- return;
-}
-
-/// Send a packet to all players (except self if mentioned)
-void World::SendGlobalMessage(WorldPacket *packet, WorldSession *self, uint32 team)
-{
- SessionMap::iterator itr;
- for (itr = m_sessions.begin(); itr != m_sessions.end(); itr++)
- {
- if (itr->second &&
- itr->second->GetPlayer() &&
- itr->second->GetPlayer()->IsInWorld() &&
- itr->second != self &&
- (team == 0 || itr->second->GetPlayer()->GetTeam() == team) )
- {
- itr->second->SendPacket(packet);
- }
- }
-}
-
-/// Send a System Message to all players (except self if mentioned)
-void World::SendWorldText(int32 string_id, ...)
-{
- std::vector<std::vector<WorldPacket*> > data_cache; // 0 = default, i => i-1 locale index
-
- for(SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
- {
- if(!itr->second || !itr->second->GetPlayer() || !itr->second->GetPlayer()->IsInWorld() )
- continue;
-
- uint32 loc_idx = itr->second->GetSessionDbLocaleIndex();
- uint32 cache_idx = loc_idx+1;
-
- std::vector<WorldPacket*>* data_list;
-
- // create if not cached yet
- if(data_cache.size() < cache_idx+1 || data_cache[cache_idx].empty())
- {
- if(data_cache.size() < cache_idx+1)
- data_cache.resize(cache_idx+1);
-
- data_list = &data_cache[cache_idx];
-
- char const* text = objmgr.GetMangosString(string_id,loc_idx);
-
- char buf[1000];
-
- va_list argptr;
- va_start( argptr, string_id );
- vsnprintf( buf,1000, text, argptr );
- va_end( argptr );
-
- char* pos = &buf[0];
-
- while(char* line = ChatHandler::LineFromMessage(pos))
- {
- WorldPacket* data = new WorldPacket();
- ChatHandler::FillMessageData(data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, 0, line, NULL);
- data_list->push_back(data);
- }
- }
- else
- data_list = &data_cache[cache_idx];
-
- for(int i = 0; i < data_list->size(); ++i)
- itr->second->SendPacket((*data_list)[i]);
- }
-
- // free memory
- for(int i = 0; i < data_cache.size(); ++i)
- for(int j = 0; j < data_cache[i].size(); ++j)
- delete data_cache[i][j];
-}
-
-/// Send a packet to all players (or players selected team) in the zone (except self if mentioned)
-void World::SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self, uint32 team)
-{
- SessionMap::iterator itr;
- for (itr = m_sessions.begin(); itr != m_sessions.end(); itr++)
- {
- if (itr->second &&
- itr->second->GetPlayer() &&
- itr->second->GetPlayer()->IsInWorld() &&
- itr->second->GetPlayer()->GetZoneId() == zone &&
- itr->second != self &&
- (team == 0 || itr->second->GetPlayer()->GetTeam() == team) )
- {
- itr->second->SendPacket(packet);
- }
- }
-}
-
-/// Send a System Message to all players in the zone (except self if mentioned)
-void World::SendZoneText(uint32 zone, const char* text, WorldSession *self, uint32 team)
-{
- WorldPacket data;
- ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, 0, text, NULL);
- SendZoneMessage(zone, &data, self,team);
-}
-
-/// Kick (and save) all players
-void World::KickAll()
-{
- // session not removed at kick and will removed in next update tick
- for (SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
- itr->second->KickPlayer();
-}
-
-/// Kick (and save) all players with security level less `sec`
-void World::KickAllLess(AccountTypes sec)
-{
- // session not removed at kick and will removed in next update tick
- for (SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
- if(itr->second->GetSecurity() < sec)
- itr->second->KickPlayer();
-}
-
-/// Kick all queued players
-void World::KickAllQueued()
-{
- // session not removed at kick and will removed in next update tick
- //TODO here
-// for (Queue::iterator itr = m_QueuedPlayer.begin(); itr != m_QueuedPlayer.end(); ++itr)
-// if(WorldSession* session = (*itr)->GetSession())
-// session->KickPlayer();
-
- m_QueuedPlayer.empty();
-}
-
-/// Kick (and save) the designated player
-bool World::KickPlayer(std::string playerName)
-{
- SessionMap::iterator itr;
-
- // session not removed at kick and will removed in next update tick
- for (itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
- {
- if(!itr->second)
- continue;
- Player *player = itr->second->GetPlayer();
- if(!player)
- continue;
- if( player->IsInWorld() )
- {
- if (playerName == player->GetName())
- {
- itr->second->KickPlayer();
- return true;
- }
- }
- }
- return false;
-}
-
-/// Ban an account or ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
-uint8 World::BanAccount(std::string type, std::string nameOrIP, std::string duration, std::string reason, std::string author)
-{
- loginDatabase.escape_string(nameOrIP);
- loginDatabase.escape_string(reason);
- std::string safe_author=author;
- loginDatabase.escape_string(safe_author);
-
- if(type != "ip" && !normalizePlayerName(nameOrIP))
- return BAN_NOTFOUND; // Nobody to ban
-
- uint32 duration_secs = TimeStringToSecs(duration);
- QueryResult *resultAccounts = NULL; //used for kicking
-
- ///- Update the database with ban information
-
- if(type=="ip")
- {
- //No SQL injection as strings are escaped
- resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE last_ip = '%s'",nameOrIP.c_str());
- loginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+%u,'%s','%s')",nameOrIP.c_str(),duration_secs,safe_author.c_str(),reason.c_str());
- }
- else if(type=="account")
- {
- //No SQL injection as string is escaped
- resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",nameOrIP.c_str());
- }
- else if(type=="character")
- {
- //No SQL injection as string is escaped
- resultAccounts = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'",nameOrIP.c_str());
- }
- else
- return BAN_SYNTAX_ERROR; //Syntax problem
-
- if(!resultAccounts)
- if(type=="ip")
- return BAN_SUCCESS; // ip correctly banned but nobody affected (yet)
- else
- return BAN_NOTFOUND; // Nobody to ban
-
- ///- Disconnect all affected players (for IP it can be several)
- do
- {
- Field* fieldsAccount = resultAccounts->Fetch();
- uint32 account = fieldsAccount->GetUInt32();
-
- if(type != "ip")
- //No SQL injection as strings are escaped
- loginDatabase.PExecute("INSERT INTO account_banned VALUES ('%u', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+%u, '%s', '%s', '1')",
- account,duration_secs,safe_author.c_str(),reason.c_str());
-
- WorldSession* sess = FindSession(account);
- if( sess )
- if(std::string(sess->GetPlayerName()) != author)
- sess->KickPlayer();
- }
- while( resultAccounts->NextRow() );
-
- delete resultAccounts;
- return BAN_SUCCESS;
-}
-
-/// Remove a ban from an account or IP address
-bool World::RemoveBanAccount(std::string type, std::string nameOrIP)
-{
- if(type == "ip")
- {
- loginDatabase.escape_string(nameOrIP);
- loginDatabase.PExecute("DELETE FROM ip_banned WHERE ip = '%s'",nameOrIP.c_str());
- }
- else
- {
- uint32 account=0;
- if(type == "account")
- {
- //NO SQL injection as name is escaped
- loginDatabase.escape_string(nameOrIP);
- QueryResult *resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",nameOrIP.c_str());
- if(!resultAccounts)
- return false;
- Field* fieldsAccount = resultAccounts->Fetch();
- account = fieldsAccount->GetUInt32();
-
- delete resultAccounts;
- }
- else if(type == "character")
- {
- if(!normalizePlayerName(nameOrIP))
- return false;
-
- //NO SQL injection as name is escaped
- loginDatabase.escape_string(nameOrIP);
- QueryResult *resultAccounts = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'",nameOrIP.c_str());
- if(!resultAccounts)
- return false;
- Field* fieldsAccount = resultAccounts->Fetch();
- account = fieldsAccount->GetUInt32();
-
- delete resultAccounts;
- }
- if(!account)
- return false;
- //NO SQL injection as account is uint32
- loginDatabase.PExecute("UPDATE account_banned SET active = '0' WHERE id = '%u'",account);
- }
- return true;
-}
-
-/// Update the game time
-void World::_UpdateGameTime()
-{
- ///- update the time
- time_t thisTime = time(NULL);
- uint32 elapsed = uint32(thisTime - m_gameTime);
- m_gameTime = thisTime;
-
- ///- if there is a shutdown timer
- if(m_ShutdownTimer > 0 && elapsed > 0)
- {
- ///- ... and it is overdue, stop the world (set m_stopEvent)
- if( m_ShutdownTimer <= elapsed )
- {
- if(!(m_ShutdownMask & SHUTDOWN_MASK_IDLE) || GetActiveAndQueuedSessionCount()==0)
- m_stopEvent = true;
- else
- m_ShutdownTimer = 1; // minimum timer value to wait idle state
- }
- ///- ... else decrease it and if necessary display a shutdown countdown to the users
- else
- {
- m_ShutdownTimer -= elapsed;
-
- ShutdownMsg();
- }
- }
-}
-
-/// Shutdown the server
-void World::ShutdownServ(uint32 time, uint32 options)
-{
- m_ShutdownMask = options;
-
- ///- If the shutdown time is 0, set m_stopEvent (except if shutdown is 'idle' with remaining sessions)
- if(time==0)
- {
- if(!(options & SHUTDOWN_MASK_IDLE) || GetActiveAndQueuedSessionCount()==0)
- m_stopEvent = true;
- else
- m_ShutdownTimer = 1; //So that the session count is re-evaluated at next world tick
- }
- ///- Else set the shutdown timer and warn users
- else
- {
- m_ShutdownTimer = time;
- ShutdownMsg(true);
- }
-}
-
-/// Display a shutdown message to the user(s)
-void World::ShutdownMsg(bool show, Player* player)
-{
- // not show messages for idle shutdown mode
- if(m_ShutdownMask & SHUTDOWN_MASK_IDLE)
- return;
-
- ///- Display a message every 12 hours, hours, 5 minutes, minute, 5 seconds and finally seconds
- if ( show ||
- (m_ShutdownTimer < 10) ||
- // < 30 sec; every 5 sec
- (m_ShutdownTimer<30 && (m_ShutdownTimer % 5 )==0) ||
- // < 5 min ; every 1 min
- (m_ShutdownTimer<5*MINUTE && (m_ShutdownTimer % MINUTE )==0) ||
- // < 30 min ; every 5 min
- (m_ShutdownTimer<30*MINUTE && (m_ShutdownTimer % (5*MINUTE))==0) ||
- // < 12 h ; every 1 h
- (m_ShutdownTimer<12*HOUR && (m_ShutdownTimer % HOUR )==0) ||
- // > 12 h ; every 12 h
- (m_ShutdownTimer>12*HOUR && (m_ShutdownTimer % (12*HOUR) )==0))
- {
- std::string str = secsToTimeString(m_ShutdownTimer);
-
- uint32 msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_TIME : SERVER_MSG_SHUTDOWN_TIME;
-
- SendServerMessage(msgid,str.c_str(),player);
- DEBUG_LOG("Server is %s in %s",(m_ShutdownMask & SHUTDOWN_MASK_RESTART ? "restart" : "shuttingdown"),str.c_str());
- }
-}
-
-/// Cancel a planned server shutdown
-void World::ShutdownCancel()
-{
- if(!m_ShutdownTimer)
- return;
-
- uint32 msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_CANCELLED : SERVER_MSG_SHUTDOWN_CANCELLED;
-
- m_ShutdownMask = 0;
- m_ShutdownTimer = 0;
- SendServerMessage(msgid);
-
- DEBUG_LOG("Server %s cancelled.",(m_ShutdownMask & SHUTDOWN_MASK_RESTART ? "restart" : "shuttingdown"));
-}
-
-/// Send a server message to the user(s)
-void World::SendServerMessage(uint32 type, const char *text, Player* player)
-{
- WorldPacket data(SMSG_SERVER_MESSAGE, 50); // guess size
- data << uint32(type);
- if(type <= SERVER_MSG_STRING)
- data << text;
-
- if(player)
- player->GetSession()->SendPacket(&data);
- else
- SendGlobalMessage( &data );
-}
-
-void World::UpdateSessions( time_t diff )
-{
- while(!addSessQueue.empty())
- {
- WorldSession* sess = addSessQueue.next ();
- AddSession_ (sess);
- }
-
- ///- Delete kicked sessions at add new session
- for (std::set<WorldSession*>::iterator itr = m_kicked_sessions.begin(); itr != m_kicked_sessions.end(); ++itr)
- delete *itr;
- m_kicked_sessions.clear();
-
- ///- Then send an update signal to remaining ones
- for (SessionMap::iterator itr = m_sessions.begin(), next; itr != m_sessions.end(); itr = next)
- {
- next = itr;
- ++next;
-
- if(!itr->second)
- continue;
-
- ///- and remove not active sessions from the list
- if(!itr->second->Update(diff)) // As interval = 0
- {
- delete itr->second;
- m_sessions.erase(itr);
- }
- }
-}
-
-// This handles the issued and queued CLI commands
-void World::ProcessCliCommands()
-{
- if (cliCmdQueue.empty()) return;
-
- CliCommandHolder *command;
- pPrintf p_zprintf;
- while (!cliCmdQueue.empty())
- {
- sLog.outDebug("CLI command under processing...");
- command = cliCmdQueue.next();
- command->Execute();
- p_zprintf=command->GetOutputMethod();
- delete command;
- }
- // print the console message here so it looks right
- p_zprintf("mangos>");
-}
-
-void World::InitResultQueue()
-{
- m_resultQueue = new SqlResultQueue;
- CharacterDatabase.SetResultQueue(m_resultQueue);
-}
-
-void World::UpdateResultQueue()
-{
- m_resultQueue->Update();
-}
-
-void World::UpdateRealmCharCount(uint32 accountId)
-{
- CharacterDatabase.AsyncPQuery(this, &World::_UpdateRealmCharCount, accountId,
- "SELECT COUNT(guid) FROM characters WHERE account = '%u'", accountId);
-}
-
-void World::_UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId)
-{
- if (resultCharCount)
- {
- Field *fields = resultCharCount->Fetch();
- uint32 charCount = fields[0].GetUInt32();
- delete resultCharCount;
- loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", accountId, realmID);
- loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charCount, accountId, realmID);
- }
-}
-
-void World::InitDailyQuestResetTime()
-{
- time_t mostRecentQuestTime;
-
- QueryResult* result = CharacterDatabase.Query("SELECT MAX(time) FROM character_queststatus_daily");
- if(result)
- {
- Field *fields = result->Fetch();
-
- mostRecentQuestTime = (time_t)fields[0].GetUInt64();
- delete result;
- }
- else
- mostRecentQuestTime = 0;
-
- // client built-in time for reset is 6:00 AM
- // FIX ME: client not show day start time
- time_t curTime = time(NULL);
- tm localTm = *localtime(&curTime);
- localTm.tm_hour = 6;
- localTm.tm_min = 0;
- localTm.tm_sec = 0;
-
- // current day reset time
- time_t curDayResetTime = mktime(&localTm);
-
- // last reset time before current moment
- time_t resetTime = (curTime < curDayResetTime) ? curDayResetTime - DAY : curDayResetTime;
-
- // need reset (if we have quest time before last reset time (not processed by some reason)
- if(mostRecentQuestTime && mostRecentQuestTime <= resetTime)
- m_NextDailyQuestReset = mostRecentQuestTime;
- else
- {
- // plan next reset time
- m_NextDailyQuestReset = (curTime >= curDayResetTime) ? curDayResetTime + DAY : curDayResetTime;
- }
-}
-
-void World::ResetDailyQuests()
-{
- sLog.outDetail("Daily quests reset for all characters.");
- CharacterDatabase.Execute("DELETE FROM character_queststatus_daily");
- for(SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
- if(itr->second->GetPlayer())
- itr->second->GetPlayer()->ResetDailyQuestStatus();
-}
-
-void World::SetPlayerLimit( int32 limit, bool needUpdate )
-{
- if(limit < -SEC_ADMINISTRATOR)
- limit = -SEC_ADMINISTRATOR;
-
- // lock update need
- bool db_update_need = needUpdate || (limit < 0) != (m_playerLimit < 0) || (limit < 0 && m_playerLimit < 0 && limit != m_playerLimit);
-
- m_playerLimit = limit;
-
- if(db_update_need)
- loginDatabase.PExecute("UPDATE realmlist SET allowedSecurityLevel = '%u' WHERE id = '%d'",uint8(GetPlayerSecurityLimit()),realmID);
-}
-
-void World::UpdateMaxSessionCounters()
-{
- m_maxActiveSessionCount = std::max(m_maxActiveSessionCount,uint32(m_sessions.size()-m_QueuedPlayer.size()));
- m_maxQueuedSessionCount = std::max(m_maxQueuedSessionCount,uint32(m_QueuedPlayer.size()));
-}
+/*
+ * 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 world
+*/
+
+#include "Common.h"
+//#include "WorldSocket.h"
+#include "Database/DatabaseEnv.h"
+#include "Config/ConfigEnv.h"
+#include "SystemConfig.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "WorldSession.h"
+#include "WorldPacket.h"
+#include "Weather.h"
+#include "Player.h"
+#include "SkillExtraItems.h"
+#include "SkillDiscovery.h"
+#include "World.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Chat.h"
+#include "Database/DBCStores.h"
+#include "LootMgr.h"
+#include "ItemEnchantmentMgr.h"
+#include "MapManager.h"
+#include "ScriptCalls.h"
+#include "CreatureAIRegistry.h"
+#include "Policies/SingletonImp.h"
+#include "BattleGroundMgr.h"
+#include "TemporarySummon.h"
+#include "WaypointMovementGenerator.h"
+#include "VMapFactory.h"
+#include "GlobalEvents.h"
+#include "GameEvent.h"
+#include "Database/DatabaseImpl.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "InstanceSaveMgr.h"
+#include "WaypointManager.h"
+#include "Util.h"
+
+INSTANTIATE_SINGLETON_1( World );
+
+volatile bool World::m_stopEvent = false;
+volatile uint32 World::m_worldLoopCounter = 0;
+
+float World::m_PlayerStartGold = 0; // starting gold
+float World::m_MaxVisibleDistanceForCreature = DEFAULT_VISIBILITY_DISTANCE;
+float World::m_MaxVisibleDistanceForPlayer = DEFAULT_VISIBILITY_DISTANCE;
+float World::m_MaxVisibleDistanceForObject = DEFAULT_VISIBILITY_DISTANCE;
+float World::m_MaxVisibleDistanceInFlight = DEFAULT_VISIBILITY_DISTANCE;
+float World::m_VisibleUnitGreyDistance = 0;
+float World::m_VisibleObjectGreyDistance = 0;
+
+// ServerMessages.dbc
+enum ServerMessageType
+{
+ SERVER_MSG_SHUTDOWN_TIME = 1,
+ SERVER_MSG_RESTART_TIME = 2,
+ SERVER_MSG_STRING = 3,
+ SERVER_MSG_SHUTDOWN_CANCELLED = 4,
+ SERVER_MSG_RESTART_CANCELLED = 5
+};
+
+struct ScriptAction
+{
+ uint64 sourceGUID;
+ uint64 targetGUID;
+ uint64 ownerGUID; // owner of source if source is item
+ ScriptInfo const* script; // pointer to static script data
+};
+
+/// World constructor
+World::World()
+{
+ m_playerLimit = 0;
+ m_allowMovement = true;
+ m_ShutdownMask = 0;
+ m_ShutdownTimer = 0;
+ m_gameTime=time(NULL);
+ m_startTime=m_gameTime;
+ m_maxActiveSessionCount = 0;
+ m_maxQueuedSessionCount = 0;
+ m_resultQueue = NULL;
+ m_NextDailyQuestReset = 0;
+
+ m_defaultDbcLocale = LOCALE_enUS;
+ m_availableDbcLocaleMask = 0;
+}
+
+/// World destructor
+World::~World()
+{
+ ///- Empty the kicked session set
+ for (std::set<WorldSession*>::iterator itr = m_kicked_sessions.begin(); itr != m_kicked_sessions.end(); ++itr)
+ delete *itr;
+
+ m_kicked_sessions.clear();
+
+ ///- Empty the WeatherMap
+ for (WeatherMap::iterator itr = m_weathers.begin(); itr != m_weathers.end(); ++itr)
+ delete itr->second;
+
+ m_weathers.clear();
+
+ VMAP::VMapFactory::clear();
+
+ if(m_resultQueue) delete m_resultQueue;
+
+ //TODO free addSessQueue
+}
+
+/// Find a player in a specified zone
+Player* World::FindPlayerInZone(uint32 zone)
+{
+ ///- circle through active sessions and return the first player found in the zone
+ SessionMap::iterator itr;
+ for (itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ {
+ if(!itr->second)
+ continue;
+ Player *player = itr->second->GetPlayer();
+ if(!player)
+ continue;
+ if( player->IsInWorld() && player->GetZoneId() == zone )
+ {
+ // Used by the weather system. We return the player to broadcast the change weather message to him and all players in the zone.
+ return player;
+ }
+ }
+ return NULL;
+}
+
+/// Find a session by its id
+WorldSession* World::FindSession(uint32 id) const
+{
+ SessionMap::const_iterator itr = m_sessions.find(id);
+
+ if(itr != m_sessions.end())
+ return itr->second; // also can return NULL for kicked session
+ else
+ return NULL;
+}
+
+/// Remove a given session
+bool World::RemoveSession(uint32 id)
+{
+ ///- Find the session, kick the user, but we can't delete session at this moment to prevent iterator invalidation
+ SessionMap::iterator itr = m_sessions.find(id);
+
+ if(itr != m_sessions.end() && itr->second)
+ {
+ if (itr->second->PlayerLoading())
+ return false;
+ itr->second->KickPlayer();
+ }
+
+ return true;
+}
+
+void World::AddSession(WorldSession* s)
+{
+ addSessQueue.add(s);
+}
+
+void
+World::AddSession_ (WorldSession* s)
+{
+ ASSERT (s);
+
+ //NOTE - Still there is race condition in WorldSession* being used in the Sockets
+
+ ///- kick already loaded player with same account (if any) and remove session
+ ///- if player is in loading and want to load again, return
+ if (!RemoveSession (s->GetAccountId ()))
+ {
+ s->KickPlayer ();
+ m_kicked_sessions.insert (s);
+ return;
+ }
+
+ WorldSession* old = m_sessions[s->GetAccountId ()];
+ m_sessions[s->GetAccountId ()] = s;
+
+ // if session already exist, prepare to it deleting at next world update
+ // NOTE - KickPlayer() should be called on "old" in RemoveSession()
+ if (old)
+ m_kicked_sessions.insert (old);
+
+ uint32 Sessions = GetActiveAndQueuedSessionCount ();
+ uint32 pLimit = GetPlayerAmountLimit ();
+ uint32 QueueSize = GetQueueSize (); //number of players in the queue
+ bool inQueue = false;
+ //so we don't count the user trying to
+ //login as a session and queue the socket that we are using
+ --Sessions;
+
+ if (pLimit > 0 && Sessions >= pLimit && s->GetSecurity () == SEC_PLAYER )
+ {
+ AddQueuedPlayer (s);
+ UpdateMaxSessionCounters ();
+ sLog.outDetail ("PlayerQueue: Account id %u is in Queue Position (%u).", s->GetAccountId (), ++QueueSize);
+ return;
+ }
+
+ WorldPacket packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1);
+ packet << uint8 (AUTH_OK);
+ packet << uint32 (0); // unknown random value...
+ packet << uint8 (0);
+ packet << uint32 (0);
+ packet << uint8 (s->IsTBC () ? 1 : 0); // 0 - normal, 1 - TBC, must be set in database manually for each account
+ s->SendPacket (&packet);
+
+ UpdateMaxSessionCounters ();
+
+ // Updates the population
+ if (pLimit > 0)
+ {
+ float popu = GetActiveSessionCount (); //updated number of users on the server
+ popu /= pLimit;
+ popu *= 2;
+ loginDatabase.PExecute ("UPDATE realmlist SET population = '%f' WHERE id = '%d'", popu, realmID);
+ sLog.outDetail ("Server Population (%f).", popu);
+ }
+}
+
+int32 World::GetQueuePos(WorldSession* sess)
+{
+ uint32 position = 1;
+
+ for(Queue::iterator iter = m_QueuedPlayer.begin(); iter != m_QueuedPlayer.end(); ++iter, ++position)
+ if((*iter) == sess)
+ return position;
+
+ return 0;
+}
+
+void World::AddQueuedPlayer(WorldSession* sess)
+{
+ m_QueuedPlayer.push_back (sess);
+
+ // The 1st SMSG_AUTH_RESPONSE needs to contain other info too.
+ WorldPacket packet (SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1);
+ packet << uint8 (AUTH_WAIT_QUEUE);
+ packet << uint32 (0); // unknown random value...
+ packet << uint8 (0);
+ packet << uint32 (0);
+ packet << uint8 (sess->IsTBC () ? 1 : 0); // 0 - normal, 1 - TBC, must be set in database manually for each account
+ packet << uint32(GetQueuePos (sess));
+ sess->SendPacket (&packet);
+
+ //sess->SendAuthWaitQue (GetQueuePos (sess));
+}
+
+void World::RemoveQueuedPlayer(WorldSession* sess)
+{
+ // sessions count including queued to remove (if removed_session set)
+ uint32 sessions = GetActiveSessionCount();
+
+ uint32 position = 1;
+ Queue::iterator iter = m_QueuedPlayer.begin();
+
+ // if session not queued then we need decrease sessions count (Remove socked callet before session removing from session list)
+ bool decrease_session = true;
+
+ // search to remove and count skipped positions
+ for(;iter != m_QueuedPlayer.end(); ++iter, ++position)
+ {
+ if(*iter==sess)
+ {
+ Queue::iterator iter2 = iter;
+ ++iter;
+ m_QueuedPlayer.erase(iter2);
+ decrease_session = false; // removing queued session
+ break;
+ }
+ }
+
+ // iter point to next socked after removed or end()
+ // position store position of removed socket and then new position next socket after removed
+
+ // decrease for case session queued for removing
+ if(decrease_session && sessions)
+ --sessions;
+
+ // accept first in queue
+ if( (!m_playerLimit || sessions < m_playerLimit) && !m_QueuedPlayer.empty() )
+ {
+ WorldSession * socket = m_QueuedPlayer.front();
+ socket->SendAuthWaitQue(0);
+ m_QueuedPlayer.pop_front();
+
+ // update iter to point first queued socket or end() if queue is empty now
+ iter = m_QueuedPlayer.begin();
+ position = 1;
+ }
+
+ // update position from iter to end()
+ // iter point to first not updated socket, position store new position
+ for(; iter != m_QueuedPlayer.end(); ++iter, ++position)
+ (*iter)->SendAuthWaitQue(position);
+}
+
+/// Find a Weather object by the given zoneid
+Weather* World::FindWeather(uint32 id) const
+{
+ WeatherMap::const_iterator itr = m_weathers.find(id);
+
+ if(itr != m_weathers.end())
+ return itr->second;
+ else
+ return 0;
+}
+
+/// Remove a Weather object for the given zoneid
+void World::RemoveWeather(uint32 id)
+{
+ // not called at the moment. Kept for completeness
+ WeatherMap::iterator itr = m_weathers.find(id);
+
+ if(itr != m_weathers.end())
+ {
+ delete itr->second;
+ m_weathers.erase(itr);
+ }
+}
+
+/// Add a Weather object to the list
+Weather* World::AddWeather(uint32 zone_id)
+{
+ WeatherZoneChances const* weatherChances = objmgr.GetWeatherChances(zone_id);
+
+ // zone not have weather, ignore
+ if(!weatherChances)
+ return NULL;
+
+ Weather* w = new Weather(zone_id,weatherChances);
+ m_weathers[w->GetZone()] = w;
+ w->ReGenerate();
+ w->UpdateWeather();
+ return w;
+}
+
+/// Initialize config values
+void World::LoadConfigSettings(bool reload)
+{
+ if(reload)
+ {
+ if(!sConfig.Reload())
+ {
+ sLog.outError("World settings reload fail: can't read settings from %s.",sConfig.GetFilename().c_str());
+ return;
+ }
+ //TODO Check if config is outdated
+ }
+
+ ///- Read the player limit and the Message of the day from the config file
+ SetPlayerLimit( sConfig.GetIntDefault("PlayerLimit", DEFAULT_PLAYER_LIMIT), true );
+ SetMotd( sConfig.GetStringDefault("Motd", "Welcome to the Massive Network Game Object Server." ) );
+
+ ///- Read all rates from the config file
+ rate_values[RATE_HEALTH] = sConfig.GetFloatDefault("Rate.Health", 1);
+ if(rate_values[RATE_HEALTH] < 0)
+ {
+ sLog.outError("Rate.Health (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_HEALTH]);
+ rate_values[RATE_HEALTH] = 1;
+ }
+ rate_values[RATE_POWER_MANA] = sConfig.GetFloatDefault("Rate.Mana", 1);
+ if(rate_values[RATE_POWER_MANA] < 0)
+ {
+ sLog.outError("Rate.Mana (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_POWER_MANA]);
+ rate_values[RATE_POWER_MANA] = 1;
+ }
+ rate_values[RATE_POWER_RAGE_INCOME] = sConfig.GetFloatDefault("Rate.Rage.Income", 1);
+ rate_values[RATE_POWER_RAGE_LOSS] = sConfig.GetFloatDefault("Rate.Rage.Loss", 1);
+ if(rate_values[RATE_POWER_RAGE_LOSS] < 0)
+ {
+ sLog.outError("Rate.Rage.Loss (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_POWER_RAGE_LOSS]);
+ rate_values[RATE_POWER_RAGE_LOSS] = 1;
+ }
+ rate_values[RATE_POWER_FOCUS] = sConfig.GetFloatDefault("Rate.Focus", 1.0f);
+ rate_values[RATE_LOYALTY] = sConfig.GetFloatDefault("Rate.Loyalty", 1.0f);
+ rate_values[RATE_SKILL_DISCOVERY] = sConfig.GetFloatDefault("Rate.Skill.Discovery", 1.0f);
+ rate_values[RATE_DROP_ITEM_POOR] = sConfig.GetFloatDefault("Rate.Drop.Item.Poor", 1.0f);
+ rate_values[RATE_DROP_ITEM_NORMAL] = sConfig.GetFloatDefault("Rate.Drop.Item.Normal", 1.0f);
+ rate_values[RATE_DROP_ITEM_UNCOMMON] = sConfig.GetFloatDefault("Rate.Drop.Item.Uncommon", 1.0f);
+ rate_values[RATE_DROP_ITEM_RARE] = sConfig.GetFloatDefault("Rate.Drop.Item.Rare", 1.0f);
+ rate_values[RATE_DROP_ITEM_EPIC] = sConfig.GetFloatDefault("Rate.Drop.Item.Epic", 1.0f);
+ rate_values[RATE_DROP_ITEM_LEGENDARY] = sConfig.GetFloatDefault("Rate.Drop.Item.Legendary", 1.0f);
+ rate_values[RATE_DROP_ITEM_ARTIFACT] = sConfig.GetFloatDefault("Rate.Drop.Item.Artifact", 1.0f);
+ rate_values[RATE_DROP_ITEM_REFERENCED] = sConfig.GetFloatDefault("Rate.Drop.Item.Referenced", 1.0f);
+ rate_values[RATE_DROP_MONEY] = sConfig.GetFloatDefault("Rate.Drop.Money", 1.0f);
+ rate_values[RATE_XP_KILL] = sConfig.GetFloatDefault("Rate.XP.Kill", 1.0f);
+ rate_values[RATE_XP_QUEST] = sConfig.GetFloatDefault("Rate.XP.Quest", 1.0f);
+ rate_values[RATE_XP_EXPLORE] = sConfig.GetFloatDefault("Rate.XP.Explore", 1.0f);
+ rate_values[RATE_XP_PAST_70] = sConfig.GetFloatDefault("Rate.XP.PastLevel70", 1.0f);
+ rate_values[RATE_REPUTATION_GAIN] = sConfig.GetFloatDefault("Rate.Reputation.Gain", 1.0f);
+ rate_values[RATE_CREATURE_NORMAL_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Normal.Damage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_ELITE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.Elite.Damage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RAREELITE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RAREELITE.Damage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.WORLDBOSS.Damage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RARE_DAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RARE.Damage", 1.0f);
+ rate_values[RATE_CREATURE_NORMAL_HP] = sConfig.GetFloatDefault("Rate.Creature.Normal.HP", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_ELITE_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.Elite.HP", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RAREELITE_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.RAREELITE.HP", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_WORLDBOSS_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.WORLDBOSS.HP", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RARE_HP] = sConfig.GetFloatDefault("Rate.Creature.Elite.RARE.HP", 1.0f);
+ rate_values[RATE_CREATURE_NORMAL_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Normal.SpellDamage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.Elite.SpellDamage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RAREELITE.SpellDamage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.WORLDBOSS.SpellDamage", 1.0f);
+ rate_values[RATE_CREATURE_ELITE_RARE_SPELLDAMAGE] = sConfig.GetFloatDefault("Rate.Creature.Elite.RARE.SpellDamage", 1.0f);
+ rate_values[RATE_CREATURE_AGGRO] = sConfig.GetFloatDefault("Rate.Creature.Aggro", 1.0f);
+ rate_values[RATE_REST_INGAME] = sConfig.GetFloatDefault("Rate.Rest.InGame", 1.0f);
+ rate_values[RATE_REST_OFFLINE_IN_TAVERN_OR_CITY] = sConfig.GetFloatDefault("Rate.Rest.Offline.InTavernOrCity", 1.0f);
+ rate_values[RATE_REST_OFFLINE_IN_WILDERNESS] = sConfig.GetFloatDefault("Rate.Rest.Offline.InWilderness", 1.0f);
+ rate_values[RATE_DAMAGE_FALL] = sConfig.GetFloatDefault("Rate.Damage.Fall", 1.0f);
+ rate_values[RATE_AUCTION_TIME] = sConfig.GetFloatDefault("Rate.Auction.Time", 1.0f);
+ rate_values[RATE_AUCTION_DEPOSIT] = sConfig.GetFloatDefault("Rate.Auction.Deposit", 1.0f);
+ rate_values[RATE_AUCTION_CUT] = sConfig.GetFloatDefault("Rate.Auction.Cut", 1.0f);
+ rate_values[RATE_HONOR] = sConfig.GetFloatDefault("Rate.Honor",1.0f);
+ rate_values[RATE_MINING_AMOUNT] = sConfig.GetFloatDefault("Rate.Mining.Amount",1.0f);
+ rate_values[RATE_MINING_NEXT] = sConfig.GetFloatDefault("Rate.Mining.Next",1.0f);
+ rate_values[RATE_INSTANCE_RESET_TIME] = sConfig.GetFloatDefault("Rate.InstanceResetTime",1.0f);
+ rate_values[RATE_TALENT] = sConfig.GetFloatDefault("Rate.Talent",1.0f);
+ if(rate_values[RATE_TALENT] < 0.0f)
+ {
+ sLog.outError("Rate.Talent (%f) mustbe > 0. Using 1 instead.",rate_values[RATE_TALENT]);
+ rate_values[RATE_TALENT] = 1.0f;
+ }
+ rate_values[RATE_CORPSE_DECAY_LOOTED] = sConfig.GetFloatDefault("Rate.Corpse.Decay.Looted",0.1f);
+
+ rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = sConfig.GetFloatDefault("TargetPosRecalculateRange",1.5f);
+ if(rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] < CONTACT_DISTANCE)
+ {
+ sLog.outError("TargetPosRecalculateRange (%f) must be >= %f. Using %f instead.",rate_values[RATE_TARGET_POS_RECALCULATION_RANGE],CONTACT_DISTANCE,CONTACT_DISTANCE);
+ rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = CONTACT_DISTANCE;
+ }
+ else if(rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] > ATTACK_DISTANCE)
+ {
+ sLog.outError("TargetPosRecalculateRange (%f) must be <= %f. Using %f instead.",rate_values[RATE_TARGET_POS_RECALCULATION_RANGE],ATTACK_DISTANCE,ATTACK_DISTANCE);
+ rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = ATTACK_DISTANCE;
+ }
+
+ rate_values[RATE_DURABILITY_LOSS_DAMAGE] = sConfig.GetFloatDefault("DurabilityLossChance.Damage",0.5f);
+ if(rate_values[RATE_DURABILITY_LOSS_DAMAGE] < 0.0f)
+ {
+ sLog.outError("DurabilityLossChance.Damage (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_DAMAGE]);
+ rate_values[RATE_DURABILITY_LOSS_DAMAGE] = 0.0f;
+ }
+ rate_values[RATE_DURABILITY_LOSS_ABSORB] = sConfig.GetFloatDefault("DurabilityLossChance.Absorb",0.5f);
+ if(rate_values[RATE_DURABILITY_LOSS_ABSORB] < 0.0f)
+ {
+ sLog.outError("DurabilityLossChance.Absorb (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_ABSORB]);
+ rate_values[RATE_DURABILITY_LOSS_ABSORB] = 0.0f;
+ }
+ rate_values[RATE_DURABILITY_LOSS_PARRY] = sConfig.GetFloatDefault("DurabilityLossChance.Parry",0.05f);
+ if(rate_values[RATE_DURABILITY_LOSS_PARRY] < 0.0f)
+ {
+ sLog.outError("DurabilityLossChance.Parry (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_PARRY]);
+ rate_values[RATE_DURABILITY_LOSS_PARRY] = 0.0f;
+ }
+ rate_values[RATE_DURABILITY_LOSS_BLOCK] = sConfig.GetFloatDefault("DurabilityLossChance.Block",0.05f);
+ if(rate_values[RATE_DURABILITY_LOSS_BLOCK] < 0.0f)
+ {
+ sLog.outError("DurabilityLossChance.Block (%f) must be >=0. Using 0.0 instead.",rate_values[RATE_DURABILITY_LOSS_BLOCK]);
+ rate_values[RATE_DURABILITY_LOSS_BLOCK] = 0.0f;
+ }
+
+ ///- Read other configuration items from the config file
+
+ m_configs[CONFIG_COMPRESSION] = sConfig.GetIntDefault("Compression", 1);
+ if(m_configs[CONFIG_COMPRESSION] < 1 || m_configs[CONFIG_COMPRESSION] > 9)
+ {
+ sLog.outError("Compression level (%i) must be in range 1..9. Using default compression level (1).",m_configs[CONFIG_COMPRESSION]);
+ m_configs[CONFIG_COMPRESSION] = 1;
+ }
+ m_configs[CONFIG_ADDON_CHANNEL] = sConfig.GetBoolDefault("AddonChannel", true);
+ m_configs[CONFIG_GRID_UNLOAD] = sConfig.GetBoolDefault("GridUnload", true);
+ m_configs[CONFIG_INTERVAL_SAVE] = sConfig.GetIntDefault("PlayerSaveInterval", 900000);
+
+ m_configs[CONFIG_INTERVAL_GRIDCLEAN] = sConfig.GetIntDefault("GridCleanUpDelay", 300000);
+ if(m_configs[CONFIG_INTERVAL_GRIDCLEAN] < MIN_GRID_DELAY)
+ {
+ sLog.outError("GridCleanUpDelay (%i) must be greater %u. Use this minimal value.",m_configs[CONFIG_INTERVAL_GRIDCLEAN],MIN_GRID_DELAY);
+ m_configs[CONFIG_INTERVAL_GRIDCLEAN] = MIN_GRID_DELAY;
+ }
+ if(reload)
+ MapManager::Instance().SetGridCleanUpDelay(m_configs[CONFIG_INTERVAL_GRIDCLEAN]);
+
+ m_configs[CONFIG_INTERVAL_MAPUPDATE] = sConfig.GetIntDefault("MapUpdateInterval", 100);
+ if(m_configs[CONFIG_INTERVAL_MAPUPDATE] < MIN_MAP_UPDATE_DELAY)
+ {
+ sLog.outError("MapUpdateInterval (%i) must be greater %u. Use this minimal value.",m_configs[CONFIG_INTERVAL_MAPUPDATE],MIN_MAP_UPDATE_DELAY);
+ m_configs[CONFIG_INTERVAL_MAPUPDATE] = MIN_MAP_UPDATE_DELAY;
+ }
+ if(reload)
+ MapManager::Instance().SetMapUpdateInterval(m_configs[CONFIG_INTERVAL_MAPUPDATE]);
+
+ m_configs[CONFIG_INTERVAL_CHANGEWEATHER] = sConfig.GetIntDefault("ChangeWeatherInterval", 600000);
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("WorldServerPort", DEFAULT_WORLDSERVER_PORT);
+ if(val!=m_configs[CONFIG_PORT_WORLD])
+ sLog.outError("WorldServerPort option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_PORT_WORLD]);
+ }
+ else
+ m_configs[CONFIG_PORT_WORLD] = sConfig.GetIntDefault("WorldServerPort", DEFAULT_WORLDSERVER_PORT);
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("SocketSelectTime", DEFAULT_SOCKET_SELECT_TIME);
+ if(val!=m_configs[CONFIG_SOCKET_SELECTTIME])
+ sLog.outError("SocketSelectTime option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[DEFAULT_SOCKET_SELECT_TIME]);
+ }
+ else
+ m_configs[CONFIG_SOCKET_SELECTTIME] = sConfig.GetIntDefault("SocketSelectTime", DEFAULT_SOCKET_SELECT_TIME);
+
+
+ m_configs[CONFIG_TCP_NO_DELAY] = sConfig.GetBoolDefault("TcpNoDelay", false);
+ m_configs[CONFIG_GROUP_XP_DISTANCE] = sConfig.GetIntDefault("MaxGroupXPDistance", 74);
+ /// \todo Add MonsterSight and GuarderSight (with meaning) in mangosd.conf or put them as define
+ m_configs[CONFIG_SIGHT_MONSTER] = sConfig.GetIntDefault("MonsterSight", 50);
+ m_configs[CONFIG_SIGHT_GUARDER] = sConfig.GetIntDefault("GuarderSight", 50);
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("GameType", 0);
+ if(val!=m_configs[CONFIG_GAME_TYPE])
+ sLog.outError("GameType option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_GAME_TYPE]);
+ }
+ else
+ m_configs[CONFIG_GAME_TYPE] = sConfig.GetIntDefault("GameType", 0);
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("RealmZone", REALM_ZONE_DEVELOPMENT);
+ if(val!=m_configs[CONFIG_REALM_ZONE])
+ sLog.outError("RealmZone option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_REALM_ZONE]);
+ }
+ else
+ m_configs[CONFIG_REALM_ZONE] = sConfig.GetIntDefault("RealmZone", REALM_ZONE_DEVELOPMENT);
+
+ m_configs[CONFIG_ALLOW_TWO_SIDE_ACCOUNTS] = sConfig.GetBoolDefault("AllowTwoSide.Accounts", false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Chat",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Channel",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Group",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Guild",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Auction",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL] = sConfig.GetBoolDefault("AllowTwoSide.Interaction.Mail",false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_WHO_LIST] = sConfig.GetBoolDefault("AllowTwoSide.WhoList", false);
+ m_configs[CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND] = sConfig.GetBoolDefault("AllowTwoSide.AddFriend", false);
+ m_configs[CONFIG_STRICT_PLAYER_NAMES] = sConfig.GetIntDefault("StrictPlayerNames", 0);
+ m_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfig.GetIntDefault("StrictCharterNames", 0);
+ m_configs[CONFIG_STRICT_PET_NAMES] = sConfig.GetIntDefault("StrictPetNames", 0);
+
+ m_configs[CONFIG_CHARACTERS_CREATING_DISABLED] = sConfig.GetIntDefault("CharactersCreatingDisabled", 0);
+
+ m_configs[CONFIG_CHARACTERS_PER_REALM] = sConfig.GetIntDefault("CharactersPerRealm", 10);
+ if(m_configs[CONFIG_CHARACTERS_PER_REALM] < 1 || m_configs[CONFIG_CHARACTERS_PER_REALM] > 10)
+ {
+ sLog.outError("CharactersPerRealm (%i) must be in range 1..10. Set to 10.",m_configs[CONFIG_CHARACTERS_PER_REALM]);
+ m_configs[CONFIG_CHARACTERS_PER_REALM] = 10;
+ }
+
+ // must be after CONFIG_CHARACTERS_PER_REALM
+ m_configs[CONFIG_CHARACTERS_PER_ACCOUNT] = sConfig.GetIntDefault("CharactersPerAccount", 50);
+ if(m_configs[CONFIG_CHARACTERS_PER_ACCOUNT] < m_configs[CONFIG_CHARACTERS_PER_REALM])
+ {
+ sLog.outError("CharactersPerAccount (%i) can't be less than CharactersPerRealm (%i).",m_configs[CONFIG_CHARACTERS_PER_ACCOUNT],m_configs[CONFIG_CHARACTERS_PER_REALM]);
+ m_configs[CONFIG_CHARACTERS_PER_ACCOUNT] = m_configs[CONFIG_CHARACTERS_PER_REALM];
+ }
+
+ m_configs[CONFIG_SKIP_CINEMATICS] = sConfig.GetIntDefault("SkipCinematics", 0);
+ if(m_configs[CONFIG_SKIP_CINEMATICS] < 0 || m_configs[CONFIG_SKIP_CINEMATICS] > 2)
+ {
+ sLog.outError("SkipCinematics (%i) must be in range 0..2. Set to 0.",m_configs[CONFIG_SKIP_CINEMATICS]);
+ m_configs[CONFIG_SKIP_CINEMATICS] = 0;
+ }
+
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("MaxPlayerLevel", 60);
+ if(val!=m_configs[CONFIG_MAX_PLAYER_LEVEL])
+ sLog.outError("MaxPlayerLevel option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_MAX_PLAYER_LEVEL]);
+ }
+ else
+ m_configs[CONFIG_MAX_PLAYER_LEVEL] = sConfig.GetIntDefault("MaxPlayerLevel", 60);
+ if(m_configs[CONFIG_MAX_PLAYER_LEVEL] > 255)
+ {
+ sLog.outError("MaxPlayerLevel (%i) must be in range 1..255. Set to 255.",m_configs[CONFIG_MAX_PLAYER_LEVEL]);
+ m_configs[CONFIG_MAX_PLAYER_LEVEL] = 255;
+ }
+
+ m_configs[CONFIG_START_PLAYER_LEVEL] = sConfig.GetIntDefault("StartPlayerLevel", 1);
+ if(m_configs[CONFIG_START_PLAYER_LEVEL] < 1)
+ {
+ sLog.outError("StartPlayerLevel (%i) must be in range 1..MaxPlayerLevel(%u). Set to 1.",m_configs[CONFIG_START_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL]);
+ m_configs[CONFIG_START_PLAYER_LEVEL] = 1;
+ }
+ else if(m_configs[CONFIG_START_PLAYER_LEVEL] > m_configs[CONFIG_MAX_PLAYER_LEVEL])
+ {
+ sLog.outError("StartPlayerLevel (%i) must be in range 1..MaxPlayerLevel(%u). Set to %u.",m_configs[CONFIG_START_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL]);
+ m_configs[CONFIG_START_PLAYER_LEVEL] = m_configs[CONFIG_MAX_PLAYER_LEVEL];
+ }
+ m_configs[CONFIG_MAX_HONOR_POINTS] = sConfig.GetIntDefault("MaxHonorPoints", 75000);
+ m_configs[CONFIG_MAX_ARENA_POINTS] = sConfig.GetIntDefault("MaxArenaPoints", 5000);
+
+ m_configs[CONFIG_INSTANCE_IGNORE_LEVEL] = sConfig.GetBoolDefault("Instance.IgnoreLevel", false);
+ m_configs[CONFIG_INSTANCE_IGNORE_RAID] = sConfig.GetBoolDefault("Instance.IgnoreRaid", false);
+
+ m_configs[CONFIG_BATTLEGROUND_CAST_DESERTER] = sConfig.GetBoolDefault("Battleground.CastDeserter", true);
+
+ m_configs[CONFIG_CAST_UNSTUCK] = sConfig.GetBoolDefault("CastUnstuck", true);
+ m_configs[CONFIG_INSTANCE_RESET_TIME_HOUR] = sConfig.GetIntDefault("Instance.ResetTimeHour", 4);
+ m_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfig.GetIntDefault("Instance.UnloadDelay", 1800000);
+
+ m_configs[CONFIG_MAX_PRIMARY_TRADE_SKILL] = sConfig.GetIntDefault("MaxPrimaryTradeSkill", 2);
+ m_configs[CONFIG_MIN_PETITION_SIGNS] = sConfig.GetIntDefault("MinPetitionSigns", 9);
+ if(m_configs[CONFIG_MIN_PETITION_SIGNS] > 9)
+ {
+ sLog.outError("MinPetitionSigns (%i) must be in range 0..9. Set to 9.",m_configs[CONFIG_MIN_PETITION_SIGNS]);
+ m_configs[CONFIG_MIN_PETITION_SIGNS] = 9;
+ }
+
+ 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);
+
+ m_configs[CONFIG_MAIL_DELIVERY_DELAY] = sConfig.GetIntDefault("MailDeliveryDelay",HOUR);
+
+ m_configs[CONFIG_UPTIME_UPDATE] = sConfig.GetIntDefault("UpdateUptimeInterval", 10);
+ if(m_configs[CONFIG_UPTIME_UPDATE]<=0)
+ {
+ sLog.outError("UpdateUptimeInterval (%i) must be > 0, set to default 10.",m_configs[CONFIG_UPTIME_UPDATE]);
+ m_configs[CONFIG_UPTIME_UPDATE] = 10;
+ }
+ if(reload)
+ {
+ m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*1000);
+ m_timers[WUPDATE_UPTIME].Reset();
+ }
+
+ m_configs[CONFIG_SKILL_CHANCE_ORANGE] = sConfig.GetIntDefault("SkillChance.Orange",100);
+ m_configs[CONFIG_SKILL_CHANCE_YELLOW] = sConfig.GetIntDefault("SkillChance.Yellow",75);
+ m_configs[CONFIG_SKILL_CHANCE_GREEN] = sConfig.GetIntDefault("SkillChance.Green",25);
+ m_configs[CONFIG_SKILL_CHANCE_GREY] = sConfig.GetIntDefault("SkillChance.Grey",0);
+
+ m_configs[CONFIG_SKILL_CHANCE_MINING_STEPS] = sConfig.GetIntDefault("SkillChance.MiningSteps",75);
+ m_configs[CONFIG_SKILL_CHANCE_SKINNING_STEPS] = sConfig.GetIntDefault("SkillChance.SkinningSteps",75);
+
+ m_configs[CONFIG_SKILL_PROSPECTING] = sConfig.GetBoolDefault("SkillChance.Prospecting",false);
+
+ m_configs[CONFIG_SKILL_GAIN_CRAFTING] = sConfig.GetIntDefault("SkillGain.Crafting", 1);
+ if(m_configs[CONFIG_SKILL_GAIN_CRAFTING] < 0)
+ {
+ sLog.outError("SkillGain.Crafting (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_CRAFTING]);
+ m_configs[CONFIG_SKILL_GAIN_CRAFTING] = 1;
+ }
+
+ m_configs[CONFIG_SKILL_GAIN_DEFENSE] = sConfig.GetIntDefault("SkillGain.Defense", 1);
+ if(m_configs[CONFIG_SKILL_GAIN_DEFENSE] < 0)
+ {
+ sLog.outError("SkillGain.Defense (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_DEFENSE]);
+ m_configs[CONFIG_SKILL_GAIN_DEFENSE] = 1;
+ }
+
+ m_configs[CONFIG_SKILL_GAIN_GATHERING] = sConfig.GetIntDefault("SkillGain.Gathering", 1);
+ if(m_configs[CONFIG_SKILL_GAIN_GATHERING] < 0)
+ {
+ sLog.outError("SkillGain.Gathering (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_GATHERING]);
+ m_configs[CONFIG_SKILL_GAIN_GATHERING] = 1;
+ }
+
+ m_configs[CONFIG_SKILL_GAIN_WEAPON] = sConfig.GetIntDefault("SkillGain.Weapon", 1);
+ if(m_configs[CONFIG_SKILL_GAIN_WEAPON] < 0)
+ {
+ sLog.outError("SkillGain.Weapon (%i) can't be negative. Set to 1.",m_configs[CONFIG_SKILL_GAIN_WEAPON]);
+ m_configs[CONFIG_SKILL_GAIN_WEAPON] = 1;
+ }
+
+ m_configs[CONFIG_MAX_OVERSPEED_PINGS] = sConfig.GetIntDefault("MaxOverspeedPings",2);
+ if(m_configs[CONFIG_MAX_OVERSPEED_PINGS] != 0 && m_configs[CONFIG_MAX_OVERSPEED_PINGS] < 2)
+ {
+ sLog.outError("MaxOverspeedPings (%i) must be in range 2..infinity (or 0 to disable check. Set to 2.",m_configs[CONFIG_MAX_OVERSPEED_PINGS]);
+ m_configs[CONFIG_MAX_OVERSPEED_PINGS] = 2;
+ }
+
+ m_configs[CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY] = sConfig.GetBoolDefault("SaveRespawnTimeImmediately",true);
+ m_configs[CONFIG_WEATHER] = sConfig.GetBoolDefault("ActivateWeather",true);
+
+ if(reload)
+ {
+ uint32 val = sConfig.GetIntDefault("Expansion",1);
+ if(val!=m_configs[CONFIG_EXPANSION])
+ sLog.outError("Expansion option can't be changed at mangosd.conf reload, using current value (%u).",m_configs[CONFIG_EXPANSION]);
+ }
+ else
+ m_configs[CONFIG_EXPANSION] = sConfig.GetIntDefault("Expansion",1);
+
+ m_configs[CONFIG_CHATFLOOD_MESSAGE_COUNT] = sConfig.GetIntDefault("ChatFlood.MessageCount",10);
+ m_configs[CONFIG_CHATFLOOD_MESSAGE_DELAY] = sConfig.GetIntDefault("ChatFlood.MessageDelay",1);
+ m_configs[CONFIG_CHATFLOOD_MUTE_TIME] = sConfig.GetIntDefault("ChatFlood.MuteTime",10);
+
+ m_configs[CONFIG_EVENT_ANNOUNCE] = sConfig.GetIntDefault("Event.Announce",0);
+
+ m_configs[CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS] = sConfig.GetIntDefault("CreatureFamilyAssistenceRadius",10);
+
+ m_configs[CONFIG_WORLD_BOSS_LEVEL_DIFF] = sConfig.GetIntDefault("WorldBossLevelDiff",3);
+
+ // note: disable value (-1) will assigned as 0xFFFFFFF, to prevent overflow at calculations limit it to max possible player level (255)
+ m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] = sConfig.GetIntDefault("Quests.LowLevelHideDiff",4);
+ if(m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] > 255)
+ m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] = 255;
+ m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] = sConfig.GetIntDefault("Quests.HighLevelHideDiff",7);
+ if(m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] > 255)
+ m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] = 255;
+
+ m_configs[CONFIG_RESTRICTED_LFG_CHANNEL] = sConfig.GetBoolDefault("Channel.RestrictedLfg", true);
+ m_configs[CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL] = sConfig.GetBoolDefault("Channel.SilentlyGMJoin", false);
+
+ m_configs[CONFIG_TALENTS_INSPECTING] = sConfig.GetBoolDefault("TalentsInspecting", true);
+ m_configs[CONFIG_CHAT_FAKE_MESSAGE_PREVENTING] = sConfig.GetBoolDefault("ChatFakeMessagePreventing", false);
+
+ m_configs[CONFIG_CORPSE_DECAY_NORMAL] = sConfig.GetIntDefault("Corpse.Decay.NORMAL", 60);
+ m_configs[CONFIG_CORPSE_DECAY_RARE] = sConfig.GetIntDefault("Corpse.Decay.RARE", 300);
+ m_configs[CONFIG_CORPSE_DECAY_ELITE] = sConfig.GetIntDefault("Corpse.Decay.ELITE", 300);
+ m_configs[CONFIG_CORPSE_DECAY_RAREELITE] = sConfig.GetIntDefault("Corpse.Decay.RAREELITE", 300);
+ m_configs[CONFIG_CORPSE_DECAY_WORLDBOSS] = sConfig.GetIntDefault("Corpse.Decay.WORLDBOSS", 3600);
+
+ m_configs[CONFIG_DEATH_SICKNESS_LEVEL] = sConfig.GetIntDefault("Death.SicknessLevel", 11);
+ m_configs[CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP] = sConfig.GetBoolDefault("Death.CorpseReclaimDelay.PvP", true);
+ m_configs[CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE] = sConfig.GetBoolDefault("Death.CorpseReclaimDelay.PvE", true);
+
+ m_configs[CONFIG_THREAT_RADIUS] = sConfig.GetIntDefault("ThreatRadius", 100);
+
+ // always use declined names in the russian client
+ m_configs[CONFIG_DECLINED_NAMES_USED] =
+ (m_configs[CONFIG_REALM_ZONE] == REALM_ZONE_RUSSIAN) ? true : sConfig.GetBoolDefault("DeclinedNames", false);
+
+ m_configs[CONFIG_LISTEN_RANGE_SAY] = sConfig.GetIntDefault("ListenRange.Say", 25);
+ m_configs[CONFIG_LISTEN_RANGE_TEXTEMOTE] = sConfig.GetIntDefault("ListenRange.TextEmote", 25);
+ m_configs[CONFIG_LISTEN_RANGE_YELL] = sConfig.GetIntDefault("ListenRange.Yell", 300);
+
+ m_PlayerStartGold = sConfig.GetFloatDefault("PlayerStart.Gold", 0);
+ if(m_PlayerStartGold < 0)
+ m_PlayerStartGold = 0;
+
+ if(m_PlayerStartGold > MAX_MONEY_AMOUNT)
+ m_PlayerStartGold = MAX_MONEY_AMOUNT;
+
+ m_configs[CONFIG_PLAYER_START_HONOR] = sConfig.GetIntDefault("PlayerStart.HonorPoints", 0);
+ if(m_configs[CONFIG_PLAYER_START_HONOR] < 0)
+ m_configs[CONFIG_PLAYER_START_HONOR] = 0;
+
+ m_configs[CONFIG_PLAYER_START_ARENAPTS] = sConfig.GetIntDefault("PlayerStart.ArenaPoints", 0);
+ if(m_configs[CONFIG_PLAYER_START_ARENAPTS] < 0)
+ m_configs[CONFIG_PLAYER_START_ARENAPTS] = 0;
+
+ m_configs[CONFIG_GM_START_LEVEL] = sConfig.GetIntDefault("GamemasterStartLevel", 70);
+ if(m_configs[CONFIG_GM_START_LEVEL] < 1)
+ m_configs[CONFIG_GM_START_LEVEL] = 1;
+
+ m_configs[CONFIG_INSTANT_LOGOUT] = sConfig.GetBoolDefault("PlayerInstantLogout", false);
+ m_configs[CONFIG_BG_START_MUSIC] = sConfig.GetBoolDefault("MusicInBattleground", false);
+ m_configs[CONFIG_START_ALL_SPELLS] = sConfig.GetBoolDefault("PlayerStart.AllSpells", false);
+ // Leaving GM queue option out for now, it's not 100% functional with the ACE patch
+ //m_configs[CONFIG_QUEUE_FOR_GM] = sConfig.GetBoolDefault("EnableQueueForGMs", false);
+ m_configs[CONFIG_HONOR_AFTER_DUEL] = sConfig.GetIntDefault("HonorPointsAfterDuel", 0);
+ if(m_configs[CONFIG_HONOR_AFTER_DUEL] < 0)
+ m_configs[CONFIG_HONOR_AFTER_DUEL]= 0;
+ m_configs[CONFIG_KICK_FROM_GMISLAND] = sConfig.GetBoolDefault("AntiCheat.GMIsland", false);
+ m_configs[CONFIG_START_ALL_EXPLORED] = sConfig.GetBoolDefault("PlayerStart.MapsExplored", false);
+ m_configs[CONFIG_DISABLE_BREATHING] = sConfig.GetBoolDefault("DisableWaterBreath", false);
+ m_configs[CONFIG_DISABLE_RES_SICKNESS] = sConfig.GetBoolDefault("DisableResurrectSickness", false);
+ m_configs[CONFIG_START_ALL_REP] = sConfig.GetBoolDefault("PlayerStart.AllReputation", false);
+ m_configs[CONFIG_ALWAYS_MAXSKILL] = sConfig.GetBoolDefault("AlwaysMaxWeaponSkill", false);
+ m_configs[CONFIG_START_ALL_TAXI] = sConfig.GetBoolDefault("PlayerStart.AllFlightPaths", false);
+ m_configs[CONFIG_PVP_TOKEN_ENABLE] = sConfig.GetBoolDefault("PvPToken.Enable", false);
+ m_configs[CONFIG_PVP_TOKEN_MAP_TYPE] = sConfig.GetIntDefault("PvPToken.MapAllowType", 4);
+ m_configs[CONFIG_PVP_TOKEN_ID] = sConfig.GetIntDefault("PvPToken.ItemID", 29434);
+ m_configs[CONFIG_PVP_TOKEN_COUNT] = sConfig.GetIntDefault("PvPToken.ItemCount", 1);
+ if(m_configs[CONFIG_PVP_TOKEN_COUNT] < 1)
+ m_configs[CONFIG_PVP_TOKEN_COUNT] = 1;
+ m_configs[CONFIG_NO_RESET_TALENT_COST] = sConfig.GetBoolDefault("NoResetTalentsCost", false);
+
+ m_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfig.GetIntDefault("Arena.MaxRatingDifference", 0);
+ m_configs[CONFIG_ARENA_RATING_DISCARD_TIMER] = sConfig.GetIntDefault("Arena.RatingDiscardTimer",300000);
+ m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfig.GetBoolDefault("Arena.AutoDistributePoints", false);
+ m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfig.GetIntDefault("Arena.AutoDistributeInterval", 7);
+
+ m_configs[CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER] = sConfig.GetIntDefault("BattleGround.PrematureFinishTimer", 0);
+
+ m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1);
+ if(m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.Grey.Unit can't be greater %f",MAX_VISIBILITY_DISTANCE);
+ m_VisibleUnitGreyDistance = MAX_VISIBILITY_DISTANCE;
+ }
+ m_VisibleObjectGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Object", 10);
+ if(m_VisibleObjectGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.Grey.Object can't be greater %f",MAX_VISIBILITY_DISTANCE);
+ m_VisibleObjectGreyDistance = MAX_VISIBILITY_DISTANCE;
+ }
+
+ m_MaxVisibleDistanceForCreature = sConfig.GetFloatDefault("Visibility.Distance.Creature", DEFAULT_VISIBILITY_DISTANCE);
+ if(m_MaxVisibleDistanceForCreature < 45*sWorld.getRate(RATE_CREATURE_AGGRO))
+ {
+ sLog.outError("Visibility.Distance.Creature can't be less max aggro radius %f",45*sWorld.getRate(RATE_CREATURE_AGGRO));
+ m_MaxVisibleDistanceForCreature = 45*sWorld.getRate(RATE_CREATURE_AGGRO);
+ }
+ else if(m_MaxVisibleDistanceForCreature + m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility. Distance .Creature can't be greater %f",MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance);
+ m_MaxVisibleDistanceForCreature = MAX_VISIBILITY_DISTANCE-m_VisibleUnitGreyDistance;
+ }
+ m_MaxVisibleDistanceForPlayer = sConfig.GetFloatDefault("Visibility.Distance.Player", DEFAULT_VISIBILITY_DISTANCE);
+ if(m_MaxVisibleDistanceForPlayer < 45*sWorld.getRate(RATE_CREATURE_AGGRO))
+ {
+ sLog.outError("Visibility.Distance.Player can't be less max aggro radius %f",45*sWorld.getRate(RATE_CREATURE_AGGRO));
+ m_MaxVisibleDistanceForPlayer = 45*sWorld.getRate(RATE_CREATURE_AGGRO);
+ }
+ else if(m_MaxVisibleDistanceForPlayer + m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.Player can't be greater %f",MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance);
+ m_MaxVisibleDistanceForPlayer = MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance;
+ }
+ m_MaxVisibleDistanceForObject = sConfig.GetFloatDefault("Visibility.Distance.Gameobject", DEFAULT_VISIBILITY_DISTANCE);
+ if(m_MaxVisibleDistanceForObject < INTERACTION_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.Object can't be less max aggro radius %f",float(INTERACTION_DISTANCE));
+ m_MaxVisibleDistanceForObject = INTERACTION_DISTANCE;
+ }
+ else if(m_MaxVisibleDistanceForObject + m_VisibleObjectGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.Object can't be greater %f",MAX_VISIBILITY_DISTANCE-m_VisibleObjectGreyDistance);
+ m_MaxVisibleDistanceForObject = MAX_VISIBILITY_DISTANCE - m_VisibleObjectGreyDistance;
+ }
+ m_MaxVisibleDistanceInFlight = sConfig.GetFloatDefault("Visibility.Distance.InFlight", DEFAULT_VISIBILITY_DISTANCE);
+ if(m_MaxVisibleDistanceInFlight + m_VisibleObjectGreyDistance > MAX_VISIBILITY_DISTANCE)
+ {
+ sLog.outError("Visibility.Distance.InFlight can't be greater %f",MAX_VISIBILITY_DISTANCE-m_VisibleObjectGreyDistance);
+ m_MaxVisibleDistanceInFlight = MAX_VISIBILITY_DISTANCE - m_VisibleObjectGreyDistance;
+ }
+
+ ///- Read the "Data" directory from the config file
+ std::string dataPath = sConfig.GetStringDefault("DataDir","./");
+ if( dataPath.at(dataPath.length()-1)!='/' && dataPath.at(dataPath.length()-1)!='\\' )
+ dataPath.append("/");
+
+ if(reload)
+ {
+ if(dataPath!=m_dataPath)
+ sLog.outError("DataDir option can't be changed at mangosd.conf reload, using current value (%s).",m_dataPath.c_str());
+ }
+ else
+ {
+ m_dataPath = dataPath;
+ sLog.outString("Using DataDir %s",m_dataPath.c_str());
+ }
+
+ bool enableLOS = sConfig.GetBoolDefault("vmap.enableLOS", false);
+ bool enableHeight = sConfig.GetBoolDefault("vmap.enableHeight", false);
+ std::string ignoreMapIds = sConfig.GetStringDefault("vmap.ignoreMapIds", "");
+ std::string ignoreSpellIds = sConfig.GetStringDefault("vmap.ignoreSpellIds", "");
+ VMAP::VMapFactory::createOrGetVMapManager()->setEnableLineOfSightCalc(enableLOS);
+ VMAP::VMapFactory::createOrGetVMapManager()->setEnableHeightCalc(enableHeight);
+ VMAP::VMapFactory::createOrGetVMapManager()->preventMapsFromBeingUsed(ignoreMapIds.c_str());
+ VMAP::VMapFactory::preventSpellsFromBeingTestedForLoS(ignoreSpellIds.c_str());
+ sLog.outString( "WORLD: VMap support included. LineOfSight:%i, getHeight:%i",enableLOS, enableHeight);
+ sLog.outString( "WORLD: VMap data directory is: %svmaps",m_dataPath.c_str());
+ sLog.outString( "WORLD: VMap config keys are: vmap.enableLOS, vmap.enableHeight, vmap.ignoreMapIds, vmap.ignoreSpellIds");
+}
+
+/// Initialize the World
+void World::SetInitialWorldSettings()
+{
+ ///- Initialize the random number generator
+ srand((unsigned int)time(NULL));
+
+ ///- Initialize config settings
+ LoadConfigSettings();
+
+ ///- Init highest guids before any table loading to prevent using not initialized guids in some code.
+ objmgr.SetHighestGuids();
+
+ ///- Check the existence of the map files for all races' startup areas.
+ if( !MapManager::ExistMapAndVMap(0,-6240.32f, 331.033f)
+ ||!MapManager::ExistMapAndVMap(0,-8949.95f,-132.493f)
+ ||!MapManager::ExistMapAndVMap(0,-8949.95f,-132.493f)
+ ||!MapManager::ExistMapAndVMap(1,-618.518f,-4251.67f)
+ ||!MapManager::ExistMapAndVMap(0, 1676.35f, 1677.45f)
+ ||!MapManager::ExistMapAndVMap(1, 10311.3f, 832.463f)
+ ||!MapManager::ExistMapAndVMap(1,-2917.58f,-257.98f)
+ ||m_configs[CONFIG_EXPANSION] && (
+ !MapManager::ExistMapAndVMap(530,10349.6f,-6357.29f) || !MapManager::ExistMapAndVMap(530,-3961.64f,-13931.2f) ) )
+ {
+ sLog.outError("Correct *.map files not found in path '%smaps' or *.vmap/*vmdir files in '%svmaps'. Please place *.map/*.vmap/*.vmdir files in appropriate directories or correct the DataDir value in the mangosd.conf file.",m_dataPath.c_str(),m_dataPath.c_str());
+ exit(1);
+ }
+
+ ///- Loading strings. Getting no records means core load has to be canceled because no error message can be output.
+ sLog.outString( "" );
+ sLog.outString( "Loading MaNGOS strings..." );
+ if (!objmgr.LoadMangosStrings())
+ exit(1); // Error message displayed in function already
+
+ ///- Update the realm entry in the database with the realm type from the config file
+ //No SQL injection as values are treated as integers
+
+ // not send custom type REALM_FFA_PVP to realm list
+ uint32 server_type = IsFFAPvPRealm() ? REALM_TYPE_PVP : getConfig(CONFIG_GAME_TYPE);
+ uint32 realm_zone = getConfig(CONFIG_REALM_ZONE);
+ loginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realmID);
+
+ ///- Remove the bones after a restart
+ CharacterDatabase.PExecute("DELETE FROM corpse WHERE corpse_type = '0'");
+
+ ///- Load the DBC files
+ sLog.outString("Initialize data stores...");
+ LoadDBCStores(m_dataPath);
+ DetectDBCLang();
+
+ sLog.outString( "Loading InstanceTemplate" );
+ objmgr.LoadInstanceTemplate();
+
+ sLog.outString( "Loading SkillLineAbilityMultiMap Data..." );
+ spellmgr.LoadSkillLineAbilityMap();
+
+ ///- Clean up and pack instances
+ sLog.outString( "Cleaning up instances..." );
+ sInstanceSaveManager.CleanupInstances(); // must be called before `creature_respawn`/`gameobject_respawn` tables
+
+ sLog.outString( "Packing instances..." );
+ sInstanceSaveManager.PackInstances();
+
+ sLog.outString( "Loading Localization strings..." );
+ objmgr.LoadCreatureLocales();
+ objmgr.LoadGameObjectLocales();
+ objmgr.LoadItemLocales();
+ objmgr.LoadQuestLocales();
+ objmgr.LoadNpcTextLocales();
+ objmgr.LoadPageTextLocales();
+ objmgr.SetDBCLocaleIndex(GetDefaultDbcLocale()); // Get once for all the locale index of DBC language (console/broadcasts)
+
+ sLog.outString( "Loading Page Texts..." );
+ objmgr.LoadPageTexts();
+
+ sLog.outString( "Loading Game Object Templates..." ); // must be after LoadPageTexts
+ objmgr.LoadGameobjectInfo();
+
+ sLog.outString( "Loading Spell Chain Data..." );
+ spellmgr.LoadSpellChains();
+
+ sLog.outString( "Loading Spell Elixir types..." );
+ spellmgr.LoadSpellElixirs();
+
+ sLog.outString( "Loading Spell Learn Skills..." );
+ spellmgr.LoadSpellLearnSkills(); // must be after LoadSpellChains
+
+ sLog.outString( "Loading Spell Learn Spells..." );
+ spellmgr.LoadSpellLearnSpells();
+
+ sLog.outString( "Loading Spell Proc Event conditions..." );
+ spellmgr.LoadSpellProcEvents();
+
+ sLog.outString( "Loading Aggro Spells Definitions...");
+ spellmgr.LoadSpellThreats();
+
+ sLog.outString( "Loading NPC Texts..." );
+ objmgr.LoadGossipText();
+
+ sLog.outString( "Loading Item Random Enchantments Table..." );
+ LoadRandomEnchantmentsTable();
+
+ sLog.outString( "Loading Items..." ); // must be after LoadRandomEnchantmentsTable and LoadPageTexts
+ objmgr.LoadItemPrototypes();
+
+ sLog.outString( "Loading Item Texts..." );
+ objmgr.LoadItemTexts();
+
+ sLog.outString( "Loading Creature Model Based Info Data..." );
+ objmgr.LoadCreatureModelInfo();
+
+ sLog.outString( "Loading Equipment templates...");
+ objmgr.LoadEquipmentTemplates();
+
+ sLog.outString( "Loading Creature templates..." );
+ objmgr.LoadCreatureTemplates();
+
+ sLog.outString( "Loading SpellsScriptTarget...");
+ spellmgr.LoadSpellScriptTarget(); // must be after LoadCreatureTemplates and LoadGameobjectInfo
+
+ sLog.outString( "Loading Creature Reputation OnKill Data..." );
+ objmgr.LoadReputationOnKill();
+
+ sLog.outString( "Loading Pet Create Spells..." );
+ objmgr.LoadPetCreateSpells();
+
+ sLog.outString( "Loading Creature Data..." );
+ objmgr.LoadCreatures();
+
+ sLog.outString( "Loading Creature Addon Data..." );
+ objmgr.LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures()
+
+ sLog.outString( "Loading Creature Respawn Data..." ); // must be after PackInstances()
+ objmgr.LoadCreatureRespawnTimes();
+
+ sLog.outString( "Loading Gameobject Data..." );
+ objmgr.LoadGameobjects();
+
+ sLog.outString( "Loading Gameobject Respawn Data..." ); // must be after PackInstances()
+ objmgr.LoadGameobjectRespawnTimes();
+
+ sLog.outString( "Loading Game Event Data...");
+ gameeventmgr.LoadFromDB();
+
+ sLog.outString( "Loading Weather Data..." );
+ objmgr.LoadWeatherZoneChances();
+
+ sLog.outString( "Loading Quests..." );
+ objmgr.LoadQuests(); // must be loaded after DBCs, creature_template, item_template, gameobject tables
+
+ sLog.outString( "Loading Quests Relations..." );
+ objmgr.LoadQuestRelations(); // must be after quest load
+
+ sLog.outString( "Loading AreaTrigger definitions..." );
+ objmgr.LoadAreaTriggerTeleports(); // must be after item template load
+
+ sLog.outString( "Loading Quest Area Triggers..." );
+ objmgr.LoadQuestAreaTriggers(); // must be after LoadQuests
+
+ sLog.outString( "Loading Tavern Area Triggers..." );
+ objmgr.LoadTavernAreaTriggers();
+
+ sLog.outString( "Loading AreaTrigger script names..." );
+ objmgr.LoadAreaTriggerScripts();
+
+
+ sLog.outString( "Loading Graveyard-zone links...");
+ objmgr.LoadGraveyardZones();
+
+ sLog.outString( "Loading Spell target coordinates..." );
+ spellmgr.LoadSpellTargetPositions();
+
+ sLog.outString( "Loading SpellAffect definitions..." );
+ spellmgr.LoadSpellAffects();
+
+ sLog.outString( "Loading spell pet auras..." );
+ spellmgr.LoadSpellPetAuras();
+
+ sLog.outString( "Loading player Create Info & Level Stats..." );
+ objmgr.LoadPlayerInfo();
+
+ sLog.outString( "Loading Exploration BaseXP Data..." );
+ objmgr.LoadExplorationBaseXP();
+
+ sLog.outString( "Loading Pet Name Parts..." );
+ objmgr.LoadPetNames();
+
+ sLog.outString( "Loading the max pet number..." );
+ objmgr.LoadPetNumber();
+
+ sLog.outString( "Loading pet level stats..." );
+ objmgr.LoadPetLevelInfo();
+
+ sLog.outString( "Loading Player Corpses..." );
+ objmgr.LoadCorpses();
+
+ sLog.outString( "Loading Loot Tables..." );
+ LoadLootTables();
+
+ sLog.outString( "Loading Skill Discovery Table..." );
+ LoadSkillDiscoveryTable();
+
+ sLog.outString( "Loading Skill Extra Item Table..." );
+ LoadSkillExtraItemTable();
+
+ sLog.outString( "Loading Skill Fishing base level requirements..." );
+ objmgr.LoadFishingBaseSkillLevel();
+
+ ///- Load dynamic data tables from the database
+ sLog.outString( "Loading Auctions..." );
+ objmgr.LoadAuctionItems();
+ objmgr.LoadAuctions();
+
+ sLog.outString( "Loading Guilds..." );
+ objmgr.LoadGuilds();
+
+ sLog.outString( "Loading ArenaTeams..." );
+ objmgr.LoadArenaTeams();
+
+ sLog.outString( "Loading Groups..." );
+ objmgr.LoadGroups();
+
+ sLog.outString( "Loading ReservedNames..." );
+ objmgr.LoadReservedPlayersNames();
+
+ sLog.outString( "Loading GameObject for quests..." );
+ objmgr.LoadGameObjectForQuests();
+
+ sLog.outString( "Loading BattleMasters..." );
+ objmgr.LoadBattleMastersEntry();
+
+ sLog.outString( "Loading GameTeleports..." );
+ objmgr.LoadGameTele();
+
+ sLog.outString( "Loading Npc Text Id..." );
+ objmgr.LoadNpcTextId(); // must be after load Creature and NpcText
+
+ sLog.outString( "Loading vendors..." );
+ objmgr.LoadVendors(); // must be after load CreatureTemplate and ItemTemplate
+
+ sLog.outString( "Loading trainers..." );
+ objmgr.LoadTrainerSpell(); // must be after load CreatureTemplate
+
+ sLog.outString( "Loading Waypoints..." );
+ WaypointMgr.Load();
+
+ ///- Handle outdated emails (delete/return)
+ sLog.outString( "Returning old mails..." );
+ objmgr.ReturnOrDeleteOldMails(false);
+
+ ///- Load and initialize scripts
+ sLog.outString( "Loading Scripts..." );
+ objmgr.LoadQuestStartScripts(); // must be after load Creature/Gameobject(Template/Data) and QuestTemplate
+ objmgr.LoadQuestEndScripts(); // must be after load Creature/Gameobject(Template/Data) and QuestTemplate
+ objmgr.LoadSpellScripts(); // must be after load Creature/Gameobject(Template/Data)
+ objmgr.LoadGameObjectScripts(); // must be after load Creature/Gameobject(Template/Data)
+ objmgr.LoadEventScripts(); // must be after load Creature/Gameobject(Template/Data)
+
+ sLog.outString( "Initializing Scripts..." );
+ if(!LoadScriptingModule())
+ exit(1);
+
+ ///- Initialize game time and timers
+ sLog.outString( "DEBUG:: Initialize game time and timers" );
+ m_gameTime = time(NULL);
+ m_startTime=m_gameTime;
+
+ tm local;
+ time_t curr;
+ time(&curr);
+ local=*(localtime(&curr)); // dereference and assign
+ char isoDate[128];
+ sprintf( isoDate, "%04d-%02d-%02d %02d:%02d:%02d",
+ local.tm_year+1900, local.tm_mon+1, local.tm_mday, local.tm_hour, local.tm_min, local.tm_sec);
+
+ WorldDatabase.PExecute("INSERT INTO uptime (startstring, starttime, uptime) VALUES('%s', %ld, 0)", isoDate, m_startTime );
+
+ m_timers[WUPDATE_OBJECTS].SetInterval(0);
+ m_timers[WUPDATE_SESSIONS].SetInterval(0);
+ m_timers[WUPDATE_WEATHERS].SetInterval(1000);
+ m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*1000); //set auction update interval to 1 minute
+ m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*1000);
+ //Update "uptime" table based on configuration entry in minutes.
+ m_timers[WUPDATE_CORPSES].SetInterval(20*MINUTE*1000); //erase corpses every 20 minutes
+
+ //to set mailtimer to return mails every day between 4 and 5 am
+ //mailtimer is increased when updating auctions
+ //one second is 1000 -(tested on win system)
+ mail_timer = ((((localtime( &m_gameTime )->tm_hour + 20) % 24)* HOUR * 1000) / m_timers[WUPDATE_AUCTIONS].GetInterval() );
+ //1440
+ mail_timer_expires = ( (DAY * 1000) / (m_timers[WUPDATE_AUCTIONS].GetInterval()));
+ sLog.outDebug("Mail timer set to: %u, mail return is called every %u minutes", mail_timer, mail_timer_expires);
+
+ ///- Initilize static helper structures
+ AIRegistry::Initialize();
+ WaypointMovementGenerator<Creature>::Initialize();
+ Player::InitVisibleBits();
+
+ ///- Initialize MapManager
+ sLog.outString( "Starting Map System" );
+ MapManager::Instance().Initialize();
+
+ ///- Initialize Battlegrounds
+ sLog.outString( "Starting BattleGround System" );
+ sBattleGroundMgr.CreateInitialBattleGrounds();
+ sBattleGroundMgr.InitAutomaticArenaPointDistribution();
+
+ //Not sure if this can be moved up in the sequence (with static data loading) as it uses MapManager
+ sLog.outString( "Loading Transports..." );
+ MapManager::Instance().LoadTransports();
+
+ sLog.outString("Deleting expired bans..." );
+ loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
+
+ sLog.outString("Calculate next daily quest reset time..." );
+ InitDailyQuestResetTime();
+
+ sLog.outString("Starting Game Event system..." );
+ uint32 nextGameEvent = gameeventmgr.Initialize();
+ m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent); //depend on next event
+
+ sLog.outString( "WORLD: World initialized" );
+}
+void World::DetectDBCLang()
+{
+ uint32 m_lang_confid = sConfig.GetIntDefault("DBC.Locale", 255);
+
+ if(m_lang_confid != 255 && m_lang_confid >= MAX_LOCALE)
+ {
+ sLog.outError("Incorrect DBC.Locale! Must be >= 0 and < %d (set to 0)",MAX_LOCALE);
+ m_lang_confid = LOCALE_enUS;
+ }
+
+ ChrRacesEntry const* race = sChrRacesStore.LookupEntry(1);
+
+ std::string availableLocalsStr;
+
+ int default_locale = MAX_LOCALE;
+ for (int i = MAX_LOCALE-1; i >= 0; --i)
+ {
+ if ( strlen(race->name[i]) > 0) // check by race names
+ {
+ default_locale = i;
+ m_availableDbcLocaleMask |= (1 << i);
+ availableLocalsStr += localeNames[i];
+ availableLocalsStr += " ";
+ }
+ }
+
+ if( default_locale != m_lang_confid && m_lang_confid < MAX_LOCALE &&
+ (m_availableDbcLocaleMask & (1 << m_lang_confid)) )
+ {
+ default_locale = m_lang_confid;
+ }
+
+ if(default_locale >= MAX_LOCALE)
+ {
+ sLog.outError("Unable to determine your DBC Locale! (corrupt DBC?)");
+ exit(1);
+ }
+
+ m_defaultDbcLocale = LocaleConstant(default_locale);
+
+ sLog.outString("Using %s DBC Locale as default. All available DBC locales: %s",localeNames[m_defaultDbcLocale],availableLocalsStr.empty() ? "<none>" : availableLocalsStr.c_str());
+}
+
+/// Update the World !
+void World::Update(time_t diff)
+{
+ ///- Update the different timers
+ for(int i = 0; i < WUPDATE_COUNT; i++)
+ if(m_timers[i].GetCurrent()>=0)
+ m_timers[i].Update(diff);
+ else m_timers[i].SetCurrent(0);
+
+ ///- Update the game time and check for shutdown time
+ _UpdateGameTime();
+
+ /// Handle daily quests reset time
+ if(m_gameTime > m_NextDailyQuestReset)
+ {
+ ResetDailyQuests();
+ m_NextDailyQuestReset += DAY;
+ }
+
+ /// <ul><li> Handle auctions when the timer has passed
+ if (m_timers[WUPDATE_AUCTIONS].Passed())
+ {
+ m_timers[WUPDATE_AUCTIONS].Reset();
+
+ ///- Update mails (return old mails with item, or delete them)
+ //(tested... works on win)
+ if (++mail_timer > mail_timer_expires)
+ {
+ mail_timer = 0;
+ objmgr.ReturnOrDeleteOldMails(true);
+ }
+
+ AuctionHouseObject* AuctionMap;
+ for (int i = 0; i < 3; i++)
+ {
+ switch (i)
+ {
+ case 0:
+ AuctionMap = objmgr.GetAuctionsMap( 6 );//horde
+ break;
+ case 1:
+ AuctionMap = objmgr.GetAuctionsMap( 2 );//alliance
+ break;
+ case 2:
+ AuctionMap = objmgr.GetAuctionsMap( 7 );//neutral
+ break;
+ }
+
+ ///- Handle expired auctions
+ AuctionHouseObject::AuctionEntryMap::iterator itr,next;
+ for (itr = AuctionMap->GetAuctionsBegin(); itr != AuctionMap->GetAuctionsEnd();itr = next)
+ {
+ next = itr;
+ ++next;
+ if (m_gameTime > (itr->second->time))
+ {
+ ///- Either cancel the auction if there was no bidder
+ if (itr->second->bidder == 0)
+ {
+ objmgr.SendAuctionExpiredMail( itr->second );
+ }
+ ///- Or perform the transaction
+ else
+ {
+ //we should send an "item sold" message if the seller is online
+ //we send the item to the winner
+ //we send the money to the seller
+ objmgr.SendAuctionSuccessfulMail( itr->second );
+ objmgr.SendAuctionWonMail( itr->second );
+ }
+
+ ///- In any case clear the auction
+ //No SQL injection (Id is integer)
+ CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",itr->second->Id);
+ objmgr.RemoveAItem(itr->second->item_guidlow);
+ delete itr->second;
+ AuctionMap->RemoveAuction(itr->first);
+ }
+ }
+ }
+ }
+
+ /// <li> Handle session updates when the timer has passed
+ if (m_timers[WUPDATE_SESSIONS].Passed())
+ {
+ m_timers[WUPDATE_SESSIONS].Reset();
+
+ UpdateSessions(diff);
+ }
+
+ /// <li> Handle weather updates when the timer has passed
+ if (m_timers[WUPDATE_WEATHERS].Passed())
+ {
+ m_timers[WUPDATE_WEATHERS].Reset();
+
+ ///- Send an update signal to Weather objects
+ WeatherMap::iterator itr, next;
+ for (itr = m_weathers.begin(); itr != m_weathers.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+
+ ///- and remove Weather objects for zones with no player
+ //As interval > WorldTick
+ if(!itr->second->Update(m_timers[WUPDATE_WEATHERS].GetInterval()))
+ {
+ delete itr->second;
+ m_weathers.erase(itr);
+ }
+ }
+ }
+ /// <li> Update uptime table
+ if (m_timers[WUPDATE_UPTIME].Passed())
+ {
+ uint32 tmpDiff = (m_gameTime - m_startTime);
+ uint32 maxClientsNum = sWorld.GetMaxActiveSessionCount();
+
+ m_timers[WUPDATE_UPTIME].Reset();
+ WorldDatabase.PExecute("UPDATE uptime SET uptime = %d, maxplayers = %d WHERE starttime = " I64FMTD, tmpDiff, maxClientsNum, uint64(m_startTime));
+ }
+
+ /// <li> Handle all other objects
+ if (m_timers[WUPDATE_OBJECTS].Passed())
+ {
+ m_timers[WUPDATE_OBJECTS].Reset();
+ ///- Update objects when the timer has passed (maps, transport, creatures,...)
+ MapManager::Instance().Update(diff); // As interval = 0
+
+ ///- Process necessary scripts
+ if (!m_scriptSchedule.empty())
+ ScriptsProcess();
+
+ sBattleGroundMgr.Update(diff);
+ }
+
+ // execute callbacks from sql queries that were queued recently
+ UpdateResultQueue();
+
+ ///- Erase corpses once every 20 minutes
+ if (m_timers[WUPDATE_CORPSES].Passed())
+ {
+ m_timers[WUPDATE_CORPSES].Reset();
+
+ CorpsesErase();
+ }
+
+ ///- Process Game events when necessary
+ if (m_timers[WUPDATE_EVENTS].Passed())
+ {
+ m_timers[WUPDATE_EVENTS].Reset(); // to give time for Update() to be processed
+ uint32 nextGameEvent = gameeventmgr.Update();
+ m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent);
+ m_timers[WUPDATE_EVENTS].Reset();
+ }
+
+ /// </ul>
+ ///- Move all creatures with "delayed move" and remove and delete all objects with "delayed remove"
+ MapManager::Instance().DoDelayedMovesAndRemoves();
+
+ // update the instance reset times
+ sInstanceSaveManager.Update();
+
+ // And last, but not least handle the issued cli commands
+ ProcessCliCommands();
+}
+
+/// Put scripts in the execution queue
+void World::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, Object* target)
+{
+ ///- Find the script map
+ ScriptMapMap::const_iterator s = scripts.find(id);
+ if (s == scripts.end())
+ return;
+
+ // prepare static data
+ uint64 sourceGUID = source->GetGUID();
+ uint64 targetGUID = target ? target->GetGUID() : (uint64)0;
+ uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0;
+
+ ///- Schedule script execution for all scripts in the script map
+ ScriptMap const *s2 = &(s->second);
+ bool immedScript = false;
+ for (ScriptMap::const_iterator iter = s2->begin(); iter != s2->end(); ++iter)
+ {
+ ScriptAction sa;
+ sa.sourceGUID = sourceGUID;
+ sa.targetGUID = targetGUID;
+ sa.ownerGUID = ownerGUID;
+
+ sa.script = &iter->second;
+ m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(m_gameTime + iter->first, sa));
+ if (iter->first == 0)
+ immedScript = true;
+ }
+ ///- If one of the effects should be immediate, launch the script execution
+ if (immedScript)
+ ScriptsProcess();
+}
+
+void World::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target)
+{
+ // NOTE: script record _must_ exist until command executed
+
+ // prepare static data
+ uint64 sourceGUID = source->GetGUID();
+ uint64 targetGUID = target ? target->GetGUID() : (uint64)0;
+ uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0;
+
+ ScriptAction sa;
+ sa.sourceGUID = sourceGUID;
+ sa.targetGUID = targetGUID;
+ sa.ownerGUID = ownerGUID;
+
+ sa.script = &script;
+ m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(m_gameTime + delay, sa));
+
+ ///- If effects should be immediate, launch the script execution
+ if(delay == 0)
+ ScriptsProcess();
+}
+
+/// Process queued scripts
+void World::ScriptsProcess()
+{
+ if (m_scriptSchedule.empty())
+ return;
+
+ ///- Process overdue queued scripts
+ std::multimap<time_t, ScriptAction>::iterator iter = m_scriptSchedule.begin();
+ // ok as multimap is a *sorted* associative container
+ while (!m_scriptSchedule.empty() && (iter->first <= m_gameTime))
+ {
+ ScriptAction const& step = iter->second;
+
+ Object* source = NULL;
+
+ if(step.sourceGUID)
+ {
+ switch(GUID_HIPART(step.sourceGUID))
+ {
+ case HIGHGUID_ITEM:
+ // case HIGHGUID_CONTAINER: ==HIGHGUID_ITEM
+ {
+ Player* player = HashMapHolder<Player>::Find(step.ownerGUID);
+ if(player)
+ source = player->GetItemByGuid(step.sourceGUID);
+ break;
+ }
+ case HIGHGUID_UNIT:
+ source = HashMapHolder<Creature>::Find(step.sourceGUID);
+ break;
+ case HIGHGUID_PET:
+ source = HashMapHolder<Pet>::Find(step.sourceGUID);
+ break;
+ case HIGHGUID_PLAYER:
+ source = HashMapHolder<Player>::Find(step.sourceGUID);
+ break;
+ case HIGHGUID_GAMEOBJECT:
+ source = HashMapHolder<GameObject>::Find(step.sourceGUID);
+ break;
+ case HIGHGUID_CORPSE:
+ source = HashMapHolder<Corpse>::Find(step.sourceGUID);
+ break;
+ default:
+ sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.sourceGUID));
+ break;
+ }
+ }
+
+ Object* target = NULL;
+
+ if(step.targetGUID)
+ {
+ switch(GUID_HIPART(step.targetGUID))
+ {
+ case HIGHGUID_UNIT:
+ target = HashMapHolder<Creature>::Find(step.targetGUID);
+ break;
+ case HIGHGUID_PET:
+ target = HashMapHolder<Pet>::Find(step.targetGUID);
+ break;
+ case HIGHGUID_PLAYER: // empty GUID case also
+ target = HashMapHolder<Player>::Find(step.targetGUID);
+ break;
+ case HIGHGUID_GAMEOBJECT:
+ target = HashMapHolder<GameObject>::Find(step.targetGUID);
+ break;
+ case HIGHGUID_CORPSE:
+ target = HashMapHolder<Corpse>::Find(step.targetGUID);
+ break;
+ default:
+ sLog.outError("*_script source with unsupported high guid value %u",GUID_HIPART(step.targetGUID));
+ break;
+ }
+ }
+
+ switch (step.script->command)
+ {
+ case SCRIPT_COMMAND_TALK:
+ {
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_TALK call for NULL creature.");
+ break;
+ }
+
+ if(source->GetTypeId()!=TYPEID_UNIT)
+ {
+ sLog.outError("SCRIPT_COMMAND_TALK call for non-creature (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+ if(step.script->datalong > 3)
+ {
+ sLog.outError("SCRIPT_COMMAND_TALK invalid chat type (%u), skipping.",step.script->datalong);
+ break;
+ }
+
+ uint64 unit_target = target ? target->GetGUID() : 0;
+
+ //datalong 0=normal say, 1=whisper, 2=yell, 3=emote text
+ switch(step.script->datalong)
+ {
+ case 0: // Say
+ ((Creature *)source)->Say(step.script->datatext.c_str(), LANG_UNIVERSAL, unit_target);
+ break;
+ case 1: // Whisper
+ if(!unit_target)
+ {
+ sLog.outError("SCRIPT_COMMAND_TALK attempt to whisper (%u) NULL, skipping.",step.script->datalong);
+ break;
+ }
+ ((Creature *)source)->Whisper(step.script->datatext.c_str(),unit_target);
+ break;
+ case 2: // Yell
+ ((Creature *)source)->Yell(step.script->datatext.c_str(), LANG_UNIVERSAL, unit_target);
+ break;
+ case 3: // Emote text
+ ((Creature *)source)->TextEmote(step.script->datatext.c_str(), unit_target);
+ break;
+ default:
+ break; // must be already checked at load
+ }
+ break;
+ }
+
+ case SCRIPT_COMMAND_EMOTE:
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_EMOTE call for NULL creature.");
+ break;
+ }
+
+ if(source->GetTypeId()!=TYPEID_UNIT)
+ {
+ sLog.outError("SCRIPT_COMMAND_EMOTE call for non-creature (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+
+ ((Creature *)source)->HandleEmoteCommand(step.script->datalong);
+ break;
+ case SCRIPT_COMMAND_FIELD_SET:
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_FIELD_SET call for NULL object.");
+ break;
+ }
+ if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount())
+ {
+ sLog.outError("SCRIPT_COMMAND_FIELD_SET call for wrong field %u (max count: %u) in object (TypeId: %u).",
+ step.script->datalong,source->GetValuesCount(),source->GetTypeId());
+ break;
+ }
+
+ source->SetUInt32Value(step.script->datalong, step.script->datalong2);
+ break;
+ case SCRIPT_COMMAND_MOVE_TO:
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_MOVE_TO call for NULL creature.");
+ break;
+ }
+
+ if(source->GetTypeId()!=TYPEID_UNIT)
+ {
+ sLog.outError("SCRIPT_COMMAND_MOVE_TO call for non-creature (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+ ((Unit *)source)->SendMonsterMoveWithSpeed(step.script->x, step.script->y, step.script->z, ((Unit *)source)->GetUnitMovementFlags(), step.script->datalong2 );
+ MapManager::Instance().GetMap(((Unit *)source)->GetMapId(), ((Unit *)source))->CreatureRelocation(((Creature *)source), step.script->x, step.script->y, step.script->z, 0);
+ break;
+ case SCRIPT_COMMAND_FLAG_SET:
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_FLAG_SET call for NULL object.");
+ break;
+ }
+ if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount())
+ {
+ sLog.outError("SCRIPT_COMMAND_FLAG_SET call for wrong field %u (max count: %u) in object (TypeId: %u).",
+ step.script->datalong,source->GetValuesCount(),source->GetTypeId());
+ break;
+ }
+
+ source->SetFlag(step.script->datalong, step.script->datalong2);
+ break;
+ case SCRIPT_COMMAND_FLAG_REMOVE:
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for NULL object.");
+ break;
+ }
+ if(step.script->datalong <= OBJECT_FIELD_ENTRY || step.script->datalong >= source->GetValuesCount())
+ {
+ sLog.outError("SCRIPT_COMMAND_FLAG_REMOVE call for wrong field %u (max count: %u) in object (TypeId: %u).",
+ step.script->datalong,source->GetValuesCount(),source->GetTypeId());
+ break;
+ }
+
+ source->RemoveFlag(step.script->datalong, step.script->datalong2);
+ break;
+
+ case SCRIPT_COMMAND_TELEPORT_TO:
+ {
+ // accept player in any one from target/source arg
+ if (!target && !source)
+ {
+ sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for NULL object.");
+ break;
+ }
+
+ // must be only Player
+ if((!target || target->GetTypeId() != TYPEID_PLAYER) && (!source || source->GetTypeId() != TYPEID_PLAYER))
+ {
+ sLog.outError("SCRIPT_COMMAND_TELEPORT_TO call for non-player (TypeIdSource: %u)(TypeIdTarget: %u), skipping.", source ? source->GetTypeId() : 0, target ? target->GetTypeId() : 0);
+ break;
+ }
+
+ Player* pSource = target && target->GetTypeId() == TYPEID_PLAYER ? (Player*)target : (Player*)source;
+
+ pSource->TeleportTo(step.script->datalong, step.script->x, step.script->y, step.script->z, step.script->o);
+ break;
+ }
+
+ case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
+ {
+ if(!step.script->datalong) // creature not specified
+ {
+ sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL creature.");
+ break;
+ }
+
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL world object.");
+ break;
+ }
+
+ WorldObject* summoner = dynamic_cast<WorldObject*>(source);
+
+ if(!summoner)
+ {
+ sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+
+ float x = step.script->x;
+ float y = step.script->y;
+ float z = step.script->z;
+ float o = step.script->o;
+
+ Creature* pCreature = summoner->SummonCreature(step.script->datalong, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,step.script->datalong2);
+ if (!pCreature)
+ {
+ sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON failed for creature (entry: %u).",step.script->datalong);
+ break;
+ }
+
+ break;
+ }
+
+ case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
+ {
+ if(!step.script->datalong) // gameobject not specified
+ {
+ sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL gameobject.");
+ break;
+ }
+
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL world object.");
+ break;
+ }
+
+ WorldObject* summoner = dynamic_cast<WorldObject*>(source);
+
+ if(!summoner)
+ {
+ sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for non-WorldObject (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+
+ GameObject *go = NULL;
+ int32 time_to_despawn = step.script->datalong2<5 ? 5 : (int32)step.script->datalong2;
+
+ CellPair p(MaNGOS::ComputeCellPair(summoner->GetPositionX(), summoner->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::GameObjectWithDbGUIDCheck go_check(*summoner,step.script->datalong);
+ MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(go,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(summoner->GetMapId(), summoner));
+
+ if ( !go )
+ {
+ sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT failed for gameobject(guid: %u).", step.script->datalong);
+ break;
+ }
+
+ if( go->GetGoType()==GAMEOBJECT_TYPE_FISHINGNODE ||
+ go->GetGoType()==GAMEOBJECT_TYPE_FISHINGNODE ||
+ go->GetGoType()==GAMEOBJECT_TYPE_DOOR ||
+ go->GetGoType()==GAMEOBJECT_TYPE_BUTTON ||
+ go->GetGoType()==GAMEOBJECT_TYPE_TRAP )
+ {
+ sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT can not be used with gameobject of type %u (guid: %u).", uint32(go->GetGoType()), step.script->datalong);
+ break;
+ }
+
+ if( go->isSpawned() )
+ break; //gameobject already spawned
+
+ go->SetLootState(GO_READY);
+ go->SetRespawnTime(time_to_despawn); //despawn object in ? seconds
+
+ MapManager::Instance().GetMap(go->GetMapId(), go)->Add(go);
+ break;
+ }
+ case SCRIPT_COMMAND_OPEN_DOOR:
+ {
+ if(!step.script->datalong) // door not specified
+ {
+ sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL door.");
+ break;
+ }
+
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for NULL unit.");
+ break;
+ }
+
+ if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player)
+ {
+ sLog.outError("SCRIPT_COMMAND_OPEN_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+
+ Unit* caster = (Unit*)source;
+
+ GameObject *door = NULL;
+ int32 time_to_close = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2;
+
+ CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong);
+ MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(door,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(caster->GetMapId(), (Unit*)source));
+
+ if ( !door )
+ {
+ sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for gameobject(guid: %u).", step.script->datalong);
+ break;
+ }
+ if ( door->GetGoType() != GAMEOBJECT_TYPE_DOOR )
+ {
+ sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for non-door(GoType: %u).", door->GetGoType());
+ break;
+ }
+
+ if( !door->GetGoState() )
+ break; //door already open
+
+ door->UseDoorOrButton(time_to_close);
+
+ if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON)
+ ((GameObject*)target)->UseDoorOrButton(time_to_close);
+ break;
+ }
+ case SCRIPT_COMMAND_CLOSE_DOOR:
+ {
+ if(!step.script->datalong) // guid for door not specified
+ {
+ sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL door.");
+ break;
+ }
+
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for NULL unit.");
+ break;
+ }
+
+ if(!source->isType(TYPEMASK_UNIT)) // must be any Unit (creature or player)
+ {
+ sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR call for non-unit (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+
+ Unit* caster = (Unit*)source;
+
+ GameObject *door = NULL;
+ int32 time_to_open = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2;
+
+ CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong);
+ MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(door,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(caster->GetMapId(), (Unit*)source));
+
+ if ( !door )
+ {
+ sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for gameobject(guid: %u).", step.script->datalong);
+ break;
+ }
+ if ( door->GetGoType() != GAMEOBJECT_TYPE_DOOR )
+ {
+ sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for non-door(GoType: %u).", door->GetGoType());
+ break;
+ }
+
+ if( door->GetGoState() )
+ break; //door already closed
+
+ door->UseDoorOrButton(time_to_open);
+
+ if(target && target->isType(TYPEMASK_GAMEOBJECT) && ((GameObject*)target)->GetGoType()==GAMEOBJECT_TYPE_BUTTON)
+ ((GameObject*)target)->UseDoorOrButton(time_to_open);
+
+ break;
+ }
+ case SCRIPT_COMMAND_QUEST_EXPLORED:
+ {
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL source.");
+ break;
+ }
+
+ if(!target)
+ {
+ sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for NULL target.");
+ break;
+ }
+
+ // when script called for item spell casting then target == (unit or GO) and source is player
+ WorldObject* worldObject;
+ Player* player;
+
+ if(target->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(source->GetTypeId()!=TYPEID_UNIT && source->GetTypeId()!=TYPEID_GAMEOBJECT)
+ {
+ sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+
+ worldObject = (WorldObject*)source;
+ player = (Player*)target;
+ }
+ else
+ {
+ if(target->GetTypeId()!=TYPEID_UNIT && target->GetTypeId()!=TYPEID_GAMEOBJECT)
+ {
+ sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u), skipping.",target->GetTypeId());
+ break;
+ }
+
+ if(source->GetTypeId()!=TYPEID_PLAYER)
+ {
+ sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-player(TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+
+ worldObject = (WorldObject*)target;
+ player = (Player*)source;
+ }
+
+ // quest id and flags checked at script loading
+ if( (worldObject->GetTypeId()!=TYPEID_UNIT || ((Unit*)worldObject)->isAlive()) &&
+ (step.script->datalong2==0 || worldObject->IsWithinDistInMap(player,float(step.script->datalong2))) )
+ player->AreaExploredOrEventHappens(step.script->datalong);
+ else
+ player->FailQuest(step.script->datalong);
+
+ break;
+ }
+
+ case SCRIPT_COMMAND_ACTIVATE_OBJECT:
+ {
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT must have source caster.");
+ break;
+ }
+
+ if(!source->isType(TYPEMASK_UNIT))
+ {
+ sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT source caster isn't unit (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+
+ if(!target)
+ {
+ sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for NULL gameobject.");
+ break;
+ }
+
+ if(target->GetTypeId()!=TYPEID_GAMEOBJECT)
+ {
+ sLog.outError("SCRIPT_COMMAND_ACTIVATE_OBJECT call for non-gameobject (TypeId: %u), skipping.",target->GetTypeId());
+ break;
+ }
+
+ Unit* caster = (Unit*)source;
+
+ GameObject *go = (GameObject*)target;
+
+ go->Use(caster);
+ break;
+ }
+
+ case SCRIPT_COMMAND_REMOVE_AURA:
+ {
+ Object* cmdTarget = step.script->datalong2 ? source : target;
+
+ if(!cmdTarget)
+ {
+ sLog.outError("SCRIPT_COMMAND_REMOVE_AURA call for NULL %s.",step.script->datalong2 ? "source" : "target");
+ break;
+ }
+
+ if(!cmdTarget->isType(TYPEMASK_UNIT))
+ {
+ sLog.outError("SCRIPT_COMMAND_REMOVE_AURA %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId());
+ break;
+ }
+
+ ((Unit*)cmdTarget)->RemoveAurasDueToSpell(step.script->datalong);
+ break;
+ }
+
+ case SCRIPT_COMMAND_CAST_SPELL:
+ {
+ if(!source)
+ {
+ sLog.outError("SCRIPT_COMMAND_CAST_SPELL must have source caster.");
+ break;
+ }
+
+ if(!source->isType(TYPEMASK_UNIT))
+ {
+ sLog.outError("SCRIPT_COMMAND_CAST_SPELL source caster isn't unit (TypeId: %u), skipping.",source->GetTypeId());
+ break;
+ }
+
+ Object* cmdTarget = step.script->datalong2 ? source : target;
+
+ if(!cmdTarget)
+ {
+ sLog.outError("SCRIPT_COMMAND_CAST_SPELL call for NULL %s.",step.script->datalong2 ? "source" : "target");
+ break;
+ }
+
+ if(!cmdTarget->isType(TYPEMASK_UNIT))
+ {
+ sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId());
+ break;
+ }
+
+ Unit* spellTarget = (Unit*)cmdTarget;
+
+ //TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast
+ ((Unit*)source)->CastSpell(spellTarget,step.script->datalong,false);
+
+ break;
+ }
+
+ default:
+ sLog.outError("Unknown script command %u called.",step.script->command);
+ break;
+ }
+
+ m_scriptSchedule.erase(iter);
+
+ iter = m_scriptSchedule.begin();
+ }
+ return;
+}
+
+/// Send a packet to all players (except self if mentioned)
+void World::SendGlobalMessage(WorldPacket *packet, WorldSession *self, uint32 team)
+{
+ SessionMap::iterator itr;
+ for (itr = m_sessions.begin(); itr != m_sessions.end(); itr++)
+ {
+ if (itr->second &&
+ itr->second->GetPlayer() &&
+ itr->second->GetPlayer()->IsInWorld() &&
+ itr->second != self &&
+ (team == 0 || itr->second->GetPlayer()->GetTeam() == team) )
+ {
+ itr->second->SendPacket(packet);
+ }
+ }
+}
+
+/// Send a System Message to all players (except self if mentioned)
+void World::SendWorldText(int32 string_id, ...)
+{
+ std::vector<std::vector<WorldPacket*> > data_cache; // 0 = default, i => i-1 locale index
+
+ for(SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ {
+ if(!itr->second || !itr->second->GetPlayer() || !itr->second->GetPlayer()->IsInWorld() )
+ continue;
+
+ uint32 loc_idx = itr->second->GetSessionDbLocaleIndex();
+ uint32 cache_idx = loc_idx+1;
+
+ std::vector<WorldPacket*>* data_list;
+
+ // create if not cached yet
+ if(data_cache.size() < cache_idx+1 || data_cache[cache_idx].empty())
+ {
+ if(data_cache.size() < cache_idx+1)
+ data_cache.resize(cache_idx+1);
+
+ data_list = &data_cache[cache_idx];
+
+ char const* text = objmgr.GetMangosString(string_id,loc_idx);
+
+ char buf[1000];
+
+ va_list argptr;
+ va_start( argptr, string_id );
+ vsnprintf( buf,1000, text, argptr );
+ va_end( argptr );
+
+ char* pos = &buf[0];
+
+ while(char* line = ChatHandler::LineFromMessage(pos))
+ {
+ WorldPacket* data = new WorldPacket();
+ ChatHandler::FillMessageData(data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, 0, line, NULL);
+ data_list->push_back(data);
+ }
+ }
+ else
+ data_list = &data_cache[cache_idx];
+
+ for(int i = 0; i < data_list->size(); ++i)
+ itr->second->SendPacket((*data_list)[i]);
+ }
+
+ // free memory
+ for(int i = 0; i < data_cache.size(); ++i)
+ for(int j = 0; j < data_cache[i].size(); ++j)
+ delete data_cache[i][j];
+}
+
+/// Send a packet to all players (or players selected team) in the zone (except self if mentioned)
+void World::SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self, uint32 team)
+{
+ SessionMap::iterator itr;
+ for (itr = m_sessions.begin(); itr != m_sessions.end(); itr++)
+ {
+ if (itr->second &&
+ itr->second->GetPlayer() &&
+ itr->second->GetPlayer()->IsInWorld() &&
+ itr->second->GetPlayer()->GetZoneId() == zone &&
+ itr->second != self &&
+ (team == 0 || itr->second->GetPlayer()->GetTeam() == team) )
+ {
+ itr->second->SendPacket(packet);
+ }
+ }
+}
+
+/// Send a System Message to all players in the zone (except self if mentioned)
+void World::SendZoneText(uint32 zone, const char* text, WorldSession *self, uint32 team)
+{
+ WorldPacket data;
+ ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, 0, text, NULL);
+ SendZoneMessage(zone, &data, self,team);
+}
+
+/// Kick (and save) all players
+void World::KickAll()
+{
+ // session not removed at kick and will removed in next update tick
+ for (SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ itr->second->KickPlayer();
+}
+
+/// Kick (and save) all players with security level less `sec`
+void World::KickAllLess(AccountTypes sec)
+{
+ // session not removed at kick and will removed in next update tick
+ for (SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ if(itr->second->GetSecurity() < sec)
+ itr->second->KickPlayer();
+}
+
+/// Kick all queued players
+void World::KickAllQueued()
+{
+ // session not removed at kick and will removed in next update tick
+ //TODO here
+// for (Queue::iterator itr = m_QueuedPlayer.begin(); itr != m_QueuedPlayer.end(); ++itr)
+// if(WorldSession* session = (*itr)->GetSession())
+// session->KickPlayer();
+
+ m_QueuedPlayer.empty();
+}
+
+/// Kick (and save) the designated player
+bool World::KickPlayer(std::string playerName)
+{
+ SessionMap::iterator itr;
+
+ // session not removed at kick and will removed in next update tick
+ for (itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ {
+ if(!itr->second)
+ continue;
+ Player *player = itr->second->GetPlayer();
+ if(!player)
+ continue;
+ if( player->IsInWorld() )
+ {
+ if (playerName == player->GetName())
+ {
+ itr->second->KickPlayer();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/// Ban an account or ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
+uint8 World::BanAccount(std::string type, std::string nameOrIP, std::string duration, std::string reason, std::string author)
+{
+ loginDatabase.escape_string(nameOrIP);
+ loginDatabase.escape_string(reason);
+ std::string safe_author=author;
+ loginDatabase.escape_string(safe_author);
+
+ if(type != "ip" && !normalizePlayerName(nameOrIP))
+ return BAN_NOTFOUND; // Nobody to ban
+
+ uint32 duration_secs = TimeStringToSecs(duration);
+ QueryResult *resultAccounts = NULL; //used for kicking
+
+ ///- Update the database with ban information
+
+ if(type=="ip")
+ {
+ //No SQL injection as strings are escaped
+ resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE last_ip = '%s'",nameOrIP.c_str());
+ loginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+%u,'%s','%s')",nameOrIP.c_str(),duration_secs,safe_author.c_str(),reason.c_str());
+ }
+ else if(type=="account")
+ {
+ //No SQL injection as string is escaped
+ resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",nameOrIP.c_str());
+ }
+ else if(type=="character")
+ {
+ //No SQL injection as string is escaped
+ resultAccounts = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'",nameOrIP.c_str());
+ }
+ else
+ return BAN_SYNTAX_ERROR; //Syntax problem
+
+ if(!resultAccounts)
+ if(type=="ip")
+ return BAN_SUCCESS; // ip correctly banned but nobody affected (yet)
+ else
+ return BAN_NOTFOUND; // Nobody to ban
+
+ ///- Disconnect all affected players (for IP it can be several)
+ do
+ {
+ Field* fieldsAccount = resultAccounts->Fetch();
+ uint32 account = fieldsAccount->GetUInt32();
+
+ if(type != "ip")
+ //No SQL injection as strings are escaped
+ loginDatabase.PExecute("INSERT INTO account_banned VALUES ('%u', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+%u, '%s', '%s', '1')",
+ account,duration_secs,safe_author.c_str(),reason.c_str());
+
+ WorldSession* sess = FindSession(account);
+ if( sess )
+ if(std::string(sess->GetPlayerName()) != author)
+ sess->KickPlayer();
+ }
+ while( resultAccounts->NextRow() );
+
+ delete resultAccounts;
+ return BAN_SUCCESS;
+}
+
+/// Remove a ban from an account or IP address
+bool World::RemoveBanAccount(std::string type, std::string nameOrIP)
+{
+ if(type == "ip")
+ {
+ loginDatabase.escape_string(nameOrIP);
+ loginDatabase.PExecute("DELETE FROM ip_banned WHERE ip = '%s'",nameOrIP.c_str());
+ }
+ else
+ {
+ uint32 account=0;
+ if(type == "account")
+ {
+ //NO SQL injection as name is escaped
+ loginDatabase.escape_string(nameOrIP);
+ QueryResult *resultAccounts = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'",nameOrIP.c_str());
+ if(!resultAccounts)
+ return false;
+ Field* fieldsAccount = resultAccounts->Fetch();
+ account = fieldsAccount->GetUInt32();
+
+ delete resultAccounts;
+ }
+ else if(type == "character")
+ {
+ if(!normalizePlayerName(nameOrIP))
+ return false;
+
+ //NO SQL injection as name is escaped
+ loginDatabase.escape_string(nameOrIP);
+ QueryResult *resultAccounts = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'",nameOrIP.c_str());
+ if(!resultAccounts)
+ return false;
+ Field* fieldsAccount = resultAccounts->Fetch();
+ account = fieldsAccount->GetUInt32();
+
+ delete resultAccounts;
+ }
+ if(!account)
+ return false;
+ //NO SQL injection as account is uint32
+ loginDatabase.PExecute("UPDATE account_banned SET active = '0' WHERE id = '%u'",account);
+ }
+ return true;
+}
+
+/// Update the game time
+void World::_UpdateGameTime()
+{
+ ///- update the time
+ time_t thisTime = time(NULL);
+ uint32 elapsed = uint32(thisTime - m_gameTime);
+ m_gameTime = thisTime;
+
+ ///- if there is a shutdown timer
+ if(m_ShutdownTimer > 0 && elapsed > 0)
+ {
+ ///- ... and it is overdue, stop the world (set m_stopEvent)
+ if( m_ShutdownTimer <= elapsed )
+ {
+ if(!(m_ShutdownMask & SHUTDOWN_MASK_IDLE) || GetActiveAndQueuedSessionCount()==0)
+ m_stopEvent = true;
+ else
+ m_ShutdownTimer = 1; // minimum timer value to wait idle state
+ }
+ ///- ... else decrease it and if necessary display a shutdown countdown to the users
+ else
+ {
+ m_ShutdownTimer -= elapsed;
+
+ ShutdownMsg();
+ }
+ }
+}
+
+/// Shutdown the server
+void World::ShutdownServ(uint32 time, uint32 options)
+{
+ m_ShutdownMask = options;
+
+ ///- If the shutdown time is 0, set m_stopEvent (except if shutdown is 'idle' with remaining sessions)
+ if(time==0)
+ {
+ if(!(options & SHUTDOWN_MASK_IDLE) || GetActiveAndQueuedSessionCount()==0)
+ m_stopEvent = true;
+ else
+ m_ShutdownTimer = 1; //So that the session count is re-evaluated at next world tick
+ }
+ ///- Else set the shutdown timer and warn users
+ else
+ {
+ m_ShutdownTimer = time;
+ ShutdownMsg(true);
+ }
+}
+
+/// Display a shutdown message to the user(s)
+void World::ShutdownMsg(bool show, Player* player)
+{
+ // not show messages for idle shutdown mode
+ if(m_ShutdownMask & SHUTDOWN_MASK_IDLE)
+ return;
+
+ ///- Display a message every 12 hours, hours, 5 minutes, minute, 5 seconds and finally seconds
+ if ( show ||
+ (m_ShutdownTimer < 10) ||
+ // < 30 sec; every 5 sec
+ (m_ShutdownTimer<30 && (m_ShutdownTimer % 5 )==0) ||
+ // < 5 min ; every 1 min
+ (m_ShutdownTimer<5*MINUTE && (m_ShutdownTimer % MINUTE )==0) ||
+ // < 30 min ; every 5 min
+ (m_ShutdownTimer<30*MINUTE && (m_ShutdownTimer % (5*MINUTE))==0) ||
+ // < 12 h ; every 1 h
+ (m_ShutdownTimer<12*HOUR && (m_ShutdownTimer % HOUR )==0) ||
+ // > 12 h ; every 12 h
+ (m_ShutdownTimer>12*HOUR && (m_ShutdownTimer % (12*HOUR) )==0))
+ {
+ std::string str = secsToTimeString(m_ShutdownTimer);
+
+ uint32 msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_TIME : SERVER_MSG_SHUTDOWN_TIME;
+
+ SendServerMessage(msgid,str.c_str(),player);
+ DEBUG_LOG("Server is %s in %s",(m_ShutdownMask & SHUTDOWN_MASK_RESTART ? "restart" : "shuttingdown"),str.c_str());
+ }
+}
+
+/// Cancel a planned server shutdown
+void World::ShutdownCancel()
+{
+ if(!m_ShutdownTimer)
+ return;
+
+ uint32 msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_CANCELLED : SERVER_MSG_SHUTDOWN_CANCELLED;
+
+ m_ShutdownMask = 0;
+ m_ShutdownTimer = 0;
+ SendServerMessage(msgid);
+
+ DEBUG_LOG("Server %s cancelled.",(m_ShutdownMask & SHUTDOWN_MASK_RESTART ? "restart" : "shuttingdown"));
+}
+
+/// Send a server message to the user(s)
+void World::SendServerMessage(uint32 type, const char *text, Player* player)
+{
+ WorldPacket data(SMSG_SERVER_MESSAGE, 50); // guess size
+ data << uint32(type);
+ if(type <= SERVER_MSG_STRING)
+ data << text;
+
+ if(player)
+ player->GetSession()->SendPacket(&data);
+ else
+ SendGlobalMessage( &data );
+}
+
+void World::UpdateSessions( time_t diff )
+{
+ while(!addSessQueue.empty())
+ {
+ WorldSession* sess = addSessQueue.next ();
+ AddSession_ (sess);
+ }
+
+ ///- Delete kicked sessions at add new session
+ for (std::set<WorldSession*>::iterator itr = m_kicked_sessions.begin(); itr != m_kicked_sessions.end(); ++itr)
+ delete *itr;
+ m_kicked_sessions.clear();
+
+ ///- Then send an update signal to remaining ones
+ for (SessionMap::iterator itr = m_sessions.begin(), next; itr != m_sessions.end(); itr = next)
+ {
+ next = itr;
+ ++next;
+
+ if(!itr->second)
+ continue;
+
+ ///- and remove not active sessions from the list
+ if(!itr->second->Update(diff)) // As interval = 0
+ {
+ delete itr->second;
+ m_sessions.erase(itr);
+ }
+ }
+}
+
+// This handles the issued and queued CLI commands
+void World::ProcessCliCommands()
+{
+ if (cliCmdQueue.empty()) return;
+
+ CliCommandHolder *command;
+ pPrintf p_zprintf;
+ while (!cliCmdQueue.empty())
+ {
+ sLog.outDebug("CLI command under processing...");
+ command = cliCmdQueue.next();
+ command->Execute();
+ p_zprintf=command->GetOutputMethod();
+ delete command;
+ }
+ // print the console message here so it looks right
+ p_zprintf("mangos>");
+}
+
+void World::InitResultQueue()
+{
+ m_resultQueue = new SqlResultQueue;
+ CharacterDatabase.SetResultQueue(m_resultQueue);
+}
+
+void World::UpdateResultQueue()
+{
+ m_resultQueue->Update();
+}
+
+void World::UpdateRealmCharCount(uint32 accountId)
+{
+ CharacterDatabase.AsyncPQuery(this, &World::_UpdateRealmCharCount, accountId,
+ "SELECT COUNT(guid) FROM characters WHERE account = '%u'", accountId);
+}
+
+void World::_UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId)
+{
+ if (resultCharCount)
+ {
+ Field *fields = resultCharCount->Fetch();
+ uint32 charCount = fields[0].GetUInt32();
+ delete resultCharCount;
+ loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", accountId, realmID);
+ loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charCount, accountId, realmID);
+ }
+}
+
+void World::InitDailyQuestResetTime()
+{
+ time_t mostRecentQuestTime;
+
+ QueryResult* result = CharacterDatabase.Query("SELECT MAX(time) FROM character_queststatus_daily");
+ if(result)
+ {
+ Field *fields = result->Fetch();
+
+ mostRecentQuestTime = (time_t)fields[0].GetUInt64();
+ delete result;
+ }
+ else
+ mostRecentQuestTime = 0;
+
+ // client built-in time for reset is 6:00 AM
+ // FIX ME: client not show day start time
+ time_t curTime = time(NULL);
+ tm localTm = *localtime(&curTime);
+ localTm.tm_hour = 6;
+ localTm.tm_min = 0;
+ localTm.tm_sec = 0;
+
+ // current day reset time
+ time_t curDayResetTime = mktime(&localTm);
+
+ // last reset time before current moment
+ time_t resetTime = (curTime < curDayResetTime) ? curDayResetTime - DAY : curDayResetTime;
+
+ // need reset (if we have quest time before last reset time (not processed by some reason)
+ if(mostRecentQuestTime && mostRecentQuestTime <= resetTime)
+ m_NextDailyQuestReset = mostRecentQuestTime;
+ else
+ {
+ // plan next reset time
+ m_NextDailyQuestReset = (curTime >= curDayResetTime) ? curDayResetTime + DAY : curDayResetTime;
+ }
+}
+
+void World::ResetDailyQuests()
+{
+ sLog.outDetail("Daily quests reset for all characters.");
+ CharacterDatabase.Execute("DELETE FROM character_queststatus_daily");
+ for(SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
+ if(itr->second->GetPlayer())
+ itr->second->GetPlayer()->ResetDailyQuestStatus();
+}
+
+void World::SetPlayerLimit( int32 limit, bool needUpdate )
+{
+ if(limit < -SEC_ADMINISTRATOR)
+ limit = -SEC_ADMINISTRATOR;
+
+ // lock update need
+ bool db_update_need = needUpdate || (limit < 0) != (m_playerLimit < 0) || (limit < 0 && m_playerLimit < 0 && limit != m_playerLimit);
+
+ m_playerLimit = limit;
+
+ if(db_update_need)
+ loginDatabase.PExecute("UPDATE realmlist SET allowedSecurityLevel = '%u' WHERE id = '%d'",uint8(GetPlayerSecurityLimit()),realmID);
+}
+
+void World::UpdateMaxSessionCounters()
+{
+ m_maxActiveSessionCount = std::max(m_maxActiveSessionCount,uint32(m_sessions.size()-m_QueuedPlayer.size()));
+ m_maxQueuedSessionCount = std::max(m_maxQueuedSessionCount,uint32(m_QueuedPlayer.size()));
+}
diff --git a/src/game/World.h b/src/game/World.h
index 7c383f8baf7..c67d975ed07 100644
--- a/src/game/World.h
+++ b/src/game/World.h
@@ -1,568 +1,568 @@
-/*
- * 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_PLAYER_START_HONOR,
- CONFIG_PLAYER_START_ARENAPTS,
- CONFIG_GM_START_LEVEL,
- CONFIG_INSTANT_LOGOUT,
- CONFIG_BG_START_MUSIC,
- CONFIG_START_ALL_SPELLS,
- CONFIG_QUEUE_FOR_GM,
- CONFIG_HONOR_AFTER_DUEL,
- CONFIG_KICK_FROM_GMISLAND,
- CONFIG_START_ALL_EXPLORED,
- CONFIG_DISABLE_BREATHING,
- CONFIG_DISABLE_RES_SICKNESS,
- CONFIG_START_ALL_REP,
- CONFIG_ALWAYS_MAXSKILL,
- CONFIG_START_ALL_TAXI,
- CONFIG_PVP_TOKEN_ENABLE,
- CONFIG_PVP_TOKEN_MAP_TYPE,
- CONFIG_PVP_TOKEN_ID,
- CONFIG_PVP_TOKEN_COUNT,
- CONFIG_NO_RESET_TALENT_COST,
-
- 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(); }
-
- static float PlayerStartGold() { return m_PlayerStartGold; }
-
- // 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;
-
- static float m_PlayerStartGold;
-
- // 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_PLAYER_START_HONOR,
+ CONFIG_PLAYER_START_ARENAPTS,
+ CONFIG_GM_START_LEVEL,
+ CONFIG_INSTANT_LOGOUT,
+ CONFIG_BG_START_MUSIC,
+ CONFIG_START_ALL_SPELLS,
+ CONFIG_QUEUE_FOR_GM,
+ CONFIG_HONOR_AFTER_DUEL,
+ CONFIG_KICK_FROM_GMISLAND,
+ CONFIG_START_ALL_EXPLORED,
+ CONFIG_DISABLE_BREATHING,
+ CONFIG_DISABLE_RES_SICKNESS,
+ CONFIG_START_ALL_REP,
+ CONFIG_ALWAYS_MAXSKILL,
+ CONFIG_START_ALL_TAXI,
+ CONFIG_PVP_TOKEN_ENABLE,
+ CONFIG_PVP_TOKEN_MAP_TYPE,
+ CONFIG_PVP_TOKEN_ID,
+ CONFIG_PVP_TOKEN_COUNT,
+ CONFIG_NO_RESET_TALENT_COST,
+
+ 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(); }
+
+ static float PlayerStartGold() { return m_PlayerStartGold; }
+
+ // 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;
+
+ static float m_PlayerStartGold;
+
+ // 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 9a0265b823a..b8a3f9ec74e 100644
--- a/src/game/WorldSession.cpp
+++ b/src/game/WorldSession.cpp
@@ -1,511 +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(), -1, true);
-
- // 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);
- }
- }
-
-
+/*
+ * 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(), -1, true);
+
+ // 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 637d833e2de..b510f5043e8 100644
--- a/src/game/WorldSession.h
+++ b/src/game/WorldSession.h
@@ -1,643 +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 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
-/// @}
+/*
+ * 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/Database/DBCStructure.h b/src/shared/Database/DBCStructure.h
index 307dcb0655b..bca75923394 100644
--- a/src/shared/Database/DBCStructure.h
+++ b/src/shared/Database/DBCStructure.h
@@ -1,938 +1,938 @@
-/*
- * 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
- */
-
-#ifndef DBCSTRUCTURE_H
-#define DBCSTRUCTURE_H
-
-#include "Platform/Define.h"
-
-#include <map>
-#include <set>
-#include <vector>
-
-// Structures using to access raw DBC data and required packing to portability
-
-// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
-#if defined( __GNUC__ )
-#pragma pack(1)
-#else
-#pragma pack(push,1)
-#endif
-
-enum AreaTeams
-{
- AREATEAM_NONE = 0,
- AREATEAM_ALLY = 2,
- AREATEAM_HORDE = 4
-};
-
-enum AreaFlags
-{
- AREA_FLAG_SNOW = 0x00000001, // snow (only Dun Morogh, Naxxramas, Razorfen Downs and Winterspring)
- AREA_FLAG_UNK1 = 0x00000002, // unknown, (only Naxxramas and Razorfen Downs)
- AREA_FLAG_UNK2 = 0x00000004, // Only used on development map
- AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // slave capital city flag?
- AREA_FLAG_UNK3 = 0x00000010, // unknown
- AREA_FLAG_SLAVE_CAPITAL2 = 0x00000020, // slave capital city flag?
- AREA_FLAG_UNK4 = 0x00000040, // many zones have this flag
- AREA_FLAG_ARENA = 0x00000080, // arena, both instanced and world arenas
- AREA_FLAG_CAPITAL = 0x00000100, // main capital city flag
- AREA_FLAG_CITY = 0x00000200, // only for one zone named "City" (where it located?)
- AREA_FLAG_OUTLAND = 0x00000400, // outland zones? (only Eye of the Storm not have this flag, but have 0x00004000 flag)
- AREA_FLAG_SANCTUARY = 0x00000800, // sanctuary area (PvP disabled)
- AREA_FLAG_NEED_FLY = 0x00001000, // only Netherwing Ledge, Socrethar's Seat, Tempest Keep, The Arcatraz, The Botanica, The Mechanar, Sorrow Wing Point, Dragonspine Ridge, Netherwing Mines, Dragonmaw Base Camp, Dragonmaw Skyway
- AREA_FLAG_UNUSED1 = 0x00002000, // not used now (no area/zones with this flag set in 2.4.2)
- AREA_FLAG_OUTLAND2 = 0x00004000, // outland zones? (only Circle of Blood Arena not have this flag, but have 0x00000400 flag)
- AREA_FLAG_PVP = 0x00008000, // pvp objective area? (Death's Door also has this flag although it's no pvp object area)
- AREA_FLAG_ARENA_INSTANCE = 0x00010000, // used by instanced arenas only
- AREA_FLAG_UNUSED2 = 0x00020000, // not used now (no area/zones with this flag set in 2.4.2)
- AREA_FLAG_UNK5 = 0x00040000, // just used for Amani Pass, Hatchet Hills
- AREA_FLAG_LOWLEVEL = 0x00100000 // used for some starting areas with area_level <=15
-};
-
-enum FactionTemplateFlags
-{
- FACTION_TEMPLATE_FLAG_CONTESTED_GUARD = 0x00001000, // faction will attack players that were involved in PvP combats
-};
-
-struct AreaTableEntry
-{
- uint32 ID; // 0
- uint32 mapid; // 1
- uint32 zone; // 2 if 0 then it's zone, else it's zone id of this area
- uint32 exploreFlag; // 3, main index
- uint32 flags; // 4, unknown value but 312 for all cities
- // 5-9 unused
- int32 area_level; // 10
- char* area_name[16]; // 11-26
- // 27, string flags, unused
- uint32 team; // 28
-};
-
-struct AreaTriggerEntry
-{
- uint32 id; // 0
- uint32 mapid; // 1
- float x; // 2
- float y; // 3
- float z; // 4
- float radius; // 5
- float box_x; // 6 extent x edge
- float box_y; // 7 extent y edge
- float box_z; // 8 extent z edge
- float box_orientation; // 9 extent rotation by about z axis
-};
-
-struct BankBagSlotPricesEntry
-{
- uint32 ID;
- uint32 price;
-};
-
-struct BattlemasterListEntry
-{
- uint32 id; // 0
- uint32 mapid[3]; // 1-3 mapid
- // 4-8 unused
- uint32 type; // 9 (3 - BG, 4 - arena)
- uint32 minlvl; // 10
- uint32 maxlvl; // 11
- uint32 maxplayersperteam; // 12
- // 13-14 unused
- char* name[16]; // 15-30
- // 31 string flag, unused
- // 32 unused
-};
-
-struct CharTitlesEntry
-{
- uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId()
- //uint32 unk1; // 1 flags?
- //char* name[16]; // 2-17, unused
- // 18 string flag, unused
- //char* name2[16]; // 19-34, unused
- // 35 string flag, unused
- uint32 bit_index; // 36 used in PLAYER_CHOSEN_TITLE and 1<<index in PLAYER__FIELD_KNOWN_TITLES
-};
-
-struct ChatChannelsEntry
-{
- uint32 ChannelID; // 0
- uint32 flags; // 1
- char* pattern[16]; // 3-18
- // 19 string flags, unused
- //char* name[16]; // 20-35 unused
- // 36 string flag, unused
-};
-
-struct ChrClassesEntry
-{
- uint32 ClassID; // 0
- // 1-2, unused
- uint32 powerType; // 3
- // 4, unused
- //char* name[16]; // 5-20 unused
- // 21 string flag, unused
- //char* string1[16]; // 21-36 unused
- // 37 string flag, unused
- //char* string2[16]; // 38-53 unused
- // 54 string flag, unused
- // 55, unused
- uint32 spellfamily; // 56
- // 57, unused
-};
-
-struct ChrRacesEntry
-{
- uint32 RaceID; // 0
- // 1 unused
- uint32 FactionID; // 2 facton template id
- // 3 unused
- uint32 model_m; // 4
- uint32 model_f; // 5
- // 6-7 unused
- uint32 TeamID; // 8 (7-Alliance 1-Horde)
- // 9-12 unused
- uint32 startmovie; // 13 id from CinematicCamera.dbc
- char* name[16]; // 14-29 used for DBC language detection/selection
- // 30 string flags, unused
- //char* string1[16]; // 31-46 used for DBC language detection/selection
- // 47 string flags, unused
- //char* string2[16]; // 48-63 used for DBC language detection/selection
- // 64 string flags, unused
- // 65-67 unused
- //uint32 addon // 68 (0 - original race, 1 - tbc addon, ...) unused
-};
-
-struct CreatureDisplayInfoEntry
-{
- uint32 Displayid; // 0
- // 1-3,unused
- float scale; // 4
- // 5-13,unused
-};
-
-struct CreatureFamilyEntry
-{
- uint32 ID; // 0
- float minScale; // 1
- uint32 minScaleLevel; // 2 0/1
- float maxScale; // 3
- uint32 maxScaleLevel; // 4 0/60
- uint32 skillLine; // 5
- uint32 skillLine2; // 6
- uint32 petFoodMask; // 7
- char* Name[16]; // 8-23
- // 24 string flags, unused
- // 25 icon, unused
-};
-
-struct CreatureSpellDataEntry
-{
- uint32 ID; // 0
- //uint32 spellId[4]; // 1-4 hunter pet learned spell (for later use)
-};
-
-struct DurabilityCostsEntry
-{
- uint32 Itemlvl; // 0
- uint32 multiplier[29]; // 1-29
-};
-
-struct DurabilityQualityEntry
-{
- uint32 Id; // 0
- float quality_mod; // 1
-};
-
-struct EmotesTextEntry
-{
- uint32 Id;
- uint32 textid;
-};
-
-struct FactionEntry
-{
- uint32 ID; // 0
- int32 reputationListID; // 1
- uint32 BaseRepRaceMask[4]; // 2-5 Base reputation race masks (see enum Races)
- uint32 BaseRepClassMask[4]; // 6-9 Base reputation class masks (see enum Classes)
- int32 BaseRepValue[4]; // 10-13 Base reputation values
- uint32 ReputationFlags[4]; // 14-17 Default flags to apply
- uint32 team; // 18 enum Team
- char* name[16]; // 19-34
- // 35 string flags, unused
- //char* description[16]; // 36-51 unused
- // 52 string flags, unused
-};
-
-enum FactionMasks
-{
- FACTION_MASK_PLAYER = 1, // any player
- FACTION_MASK_ALLIANCE = 2, // player or creature from alliance team
- FACTION_MASK_HORDE = 4, // player or creature from horde team
- FACTION_MASK_MONSTER = 8 // aggressive creature from monster team
- // if none flags set then non-aggressive creature
-};
-
-struct FactionTemplateEntry
-{
- uint32 ID; // 0
- uint32 faction; // 1
- uint32 factionFlags; // 2 specific flags for that faction
- uint32 ourMask; // 3 if mask set (see FactionMasks) then faction included in masked team
- uint32 friendlyMask; // 4 if mask set (see FactionMasks) then faction friendly to masked team
- uint32 hostileMask; // 5 if mask set (see FactionMasks) then faction hostile to masked team
- uint32 enemyFaction1; // 6
- uint32 enemyFaction2; // 7
- uint32 enemyFaction3; // 8
- uint32 enemyFaction4; // 9
- uint32 friendFaction1; // 10
- uint32 friendFaction2; // 11
- uint32 friendFaction3; // 12
- uint32 friendFaction4; // 13
- //------------------------------------------------------- end structure
-
- // helpers
- bool IsFriendlyTo(FactionTemplateEntry const& entry) const
- {
- if(enemyFaction1 == entry.faction || enemyFaction2 == entry.faction || enemyFaction3 == entry.faction || enemyFaction4 == entry.faction )
- return false;
- if(friendFaction1 == entry.faction || friendFaction2 == entry.faction || friendFaction3 == entry.faction || friendFaction4 == entry.faction )
- return true;
- return (friendlyMask & entry.ourMask) || (ourMask & entry.friendlyMask);
- }
- bool IsHostileTo(FactionTemplateEntry const& entry) const
- {
- if(enemyFaction1 == entry.faction || enemyFaction2 == entry.faction || enemyFaction3 == entry.faction || enemyFaction4 == entry.faction )
- return true;
- if(friendFaction1 == entry.faction || friendFaction2 == entry.faction || friendFaction3 == entry.faction || friendFaction4 == entry.faction )
- return false;
- return (hostileMask & entry.ourMask) != 0;
- }
- bool IsHostileToPlayers() const { return (hostileMask & FACTION_MASK_PLAYER) !=0; }
- bool IsNeutralToAll() const { return hostileMask == 0 && friendlyMask == 0 && enemyFaction1==0 && enemyFaction2==0 && enemyFaction3==0 && enemyFaction4==0; }
- bool IsContestedGuardFaction() const { return (factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD)!=0; }
-};
-
-struct GemPropertiesEntry
-{
- uint32 ID;
- uint32 spellitemenchantement;
- uint32 color;
-};
-
-#define GT_MAX_LEVEL 100
-struct GtCombatRatingsEntry
-{
- float ratio;
-};
-
-struct GtChanceToMeleeCritBaseEntry
-{
- float base;
-};
-
-struct GtChanceToMeleeCritEntry
-{
- float ratio;
-};
-
-struct GtChanceToSpellCritBaseEntry
-{
- float base;
-};
-
-struct GtChanceToSpellCritEntry
-{
- float ratio;
-};
-
-struct GtOCTRegenHPEntry
-{
- float ratio;
-};
-
-//struct GtOCTRegenMPEntry
-//{
-// float ratio;
-//};
-
-struct GtRegenHPPerSptEntry
-{
- float ratio;
-};
-
-struct GtRegenMPPerSptEntry
-{
- float ratio;
-};
-
-struct ItemEntry
-{
- uint32 ID;
- uint32 DisplayId;
- uint32 InventoryType;
- uint32 Sheath;
-};
-
-struct ItemDisplayInfoEntry
-{
- uint32 ID;
- uint32 randomPropertyChance;
-};
-
-//struct ItemCondExtCostsEntry
-//{
-// uint32 ID;
-// uint32 condExtendedCost; // ItemPrototype::CondExtendedCost
-// uint32 itemextendedcostentry; // ItemPrototype::ExtendedCost
-// uint32 arenaseason; // arena season number(1-4)
-//};
-
-struct ItemExtendedCostEntry
-{
- uint32 ID; // 0 extended-cost entry id
- uint32 reqhonorpoints; // 1 required honor points
- uint32 reqarenapoints; // 2 required arena points
- uint32 reqitem[5]; // 3-7 required item id
- uint32 reqitemcount[5]; // 8-12 required count of 1st item
- uint32 reqpersonalarenarating; // 13 required personal arena rating
-};
-
-struct ItemRandomPropertiesEntry
-{
- uint32 ID; // 0
- //char* internalName // 1 unused
- uint32 enchant_id[3]; // 2-4
- // 5-6 unused, 0 only values, reserved for additional enchantments?
- //char* nameSuffix[16] // 7-22, unused
- // 23 nameSufix flags, unused
-};
-
-struct ItemRandomSuffixEntry
-{
- uint32 ID; // 0
- //char* name[16] // 1-16 unused
- // 17, name flags, unused
- // 18 unused
- uint32 enchant_id[3]; // 19-21
- uint32 prefix[3]; // 22-24
-};
-
-struct ItemSetEntry
-{
- //uint32 id // 0 item set ID
- char* name[16]; // 1-16
- // 17 string flags, unused
- // 18-28 items from set, but not have all items listed, use ItemPrototype::ItemSet instead
- // 29-34 unused
- uint32 spells[8]; // 35-42
- uint32 items_to_triggerspell[8]; // 43-50
- uint32 required_skill_id; // 51
- uint32 required_skill_value; // 52
-};
-
-struct LockEntry
-{
- uint32 ID; // 0
- uint32 keytype[5]; // 1-5
- // 6-8, not used
- uint32 key[5]; // 9-13
- // 14-16, not used
- uint32 requiredminingskill; // 17
- uint32 requiredlockskill; // 18
- // 19-32, not used
-};
-
-struct MailTemplateEntry
-{
- uint32 ID; // 0
- //char* subject[16]; // 1-16
- // 17 name flags, unused
- //char* content[16]; // 18-33
-};
-
-enum MapTypes
-{
- MAP_COMMON = 0,
- MAP_INSTANCE = 1,
- MAP_RAID = 2,
- MAP_BATTLEGROUND = 3,
- MAP_ARENA = 4
-};
-
-struct MapEntry
-{
- uint32 MapID; // 0
- //char* internalname; // 1 unused
- uint32 map_type; // 2
- // 3 unused
- char* name[16]; // 4-19
- // 20 name flags, unused
- // 21-23 unused (something PvPZone related - levels?)
- // 24-26
- uint32 linked_zone; // 27 common zone for instance and continent map
- //char* hordeIntro // 28-43 text for PvP Zones
- // 44 intro text flags
- //char* allianceIntro // 45-60 text for PvP Zones
- // 46 intro text flags
- // 47-61 not used
- uint32 multimap_id; // 62
- // 63-65 not used
- //chat* unknownText1 // 66-81 unknown empty text fields, possible normal Intro text.
- // 82 text flags
- //chat* heroicIntroText // 83-98 heroic mode requirement text
- // 99 text flags
- //chat* unknownText2 // 100-115 unknown empty text fields
- // 116 text flags
- int32 parent_map; // 117 map_id of parent map
- //float start_x // 118 enter x coordinate (if exist single entry)
- //float start_y // 119 enter y coordinate (if exist single entry)
- uint32 resetTimeRaid; // 120
- uint32 resetTimeHeroic; // 121
- // 122-123
- uint32 addon; // 124 (0-original maps,1-tbc addon)
-
- // Helpers
- bool IsExpansionMap() const { return addon != 0; }
-
-
- bool IsDungeon() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID; }
- bool Instanceable() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID || map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; }
- bool IsRaid() const { return map_type == MAP_RAID; }
- bool IsBattleGround() const { return map_type == MAP_BATTLEGROUND; }
- bool IsBattleArena() const { return map_type == MAP_ARENA; }
- bool IsBattleGroundOrArena() const { return map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; }
- bool SupportsHeroicMode() const { return resetTimeHeroic && !resetTimeRaid; }
- bool HasResetTime() const { return resetTimeHeroic || resetTimeRaid; }
-
- bool IsMountAllowed() const
- {
- return !IsDungeon() ||
- MapID==568 || MapID==309 || MapID==209 || MapID==534 ||
- MapID==560 || MapID==509 || MapID==269;
- }
-};
-
-struct QuestSortEntry
-{
- uint32 id; // 0, sort id
- //char* name[16]; // 1-16, unused
- // 17 name flags, unused
-};
-
-struct RandomPropertiesPointsEntry
-{
- //uint32 Id; // 0 hidden key
- uint32 itemLevel; // 1
- uint32 EpicPropertiesPoints[5]; // 2-6
- uint32 RarePropertiesPoints[5]; // 7-11
- uint32 UncommonPropertiesPoints[5]; // 12-16
-};
-
-//struct SkillLineCategoryEntry{
-// uint32 id; // 0 hidden key
-// char* name[16]; // 1 - 17 Category name
-// // 18 string flag
-// uint32 displayOrder; // Display order in character tab
-//};
-
-//struct SkillRaceClassInfoEntry{
-// uint32 id; // 0
-// uint32 skillId; // 1 present some refrences to unknown skill
-// uint32 raceMask; // 2
-// uint32 classMask; // 3
-// uint32 flags; // 4 mask for some thing
-// uint32 reqLevel; // 5
-// uint32 skillTierId; // 6
-// uint32 skillCostID; // 7
-//};
-
-//struct SkillTiersEntry{
-// uint32 id; // 0
-// uint32 skillValue[16]; // 1-17 unknown possibly add value on learn?
-// uint32 maxSkillValue[16]; // Max value for rank
-//};
-
-struct SkillLineEntry
-{
- uint32 id; // 0
- uint32 categoryId; // 1 (index from SkillLineCategory.dbc)
- //uint32 skillCostID; // 2 not used
- char* name[16]; // 3-18
- // 19 string flags, not used
- //char* description[16]; // 20-35, not used
- // 36 string flags, not used
- uint32 spellIcon; // 37
-};
-
-enum AbilytyLearnType
-{
- ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1,
- ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2
-};
-
-struct SkillLineAbilityEntry
-{
- uint32 id; // 0, INDEX
- uint32 skillId; // 1
- uint32 spellId; // 2
- uint32 racemask; // 3
- uint32 classmask; // 4
- //uint32 racemaskNot; // 5 always 0 in 2.4.2
- //uint32 classmaskNot; // 6 always 0 in 2.4.2
- uint32 req_skill_value; // 7 for trade skill.not for training.
- uint32 forward_spellid; // 8
- uint32 learnOnGetSkill; // 9 can be 1 or 2 for spells learned on get skill
- uint32 max_value; // 10
- uint32 min_value; // 11
- // 12-13, unknown, always 0
- uint32 reqtrainpoints; // 14
-};
-
-struct SoundEntriesEntry
-{
- uint32 Id; // 0, sound id
- //uint32 Type; // 1, sound type (10 generally for creature, etc)
- //char* InternalName; // 2, internal name, for use in lookup command for example
- //char* FileName[10]; // 3-12, file names
- //uint32 Unk13[10]; // 13-22, linked with file names?
- //char* Path; // 23
- // 24-28, unknown
-};
-
-struct SpellEntry
-{
- uint32 Id; // 0 normally counted from 0 field (but some tools start counting from 1, check this before tool use for data view!)
- uint32 Category; // 1
- //uint32 castUI // 2 not used
- uint32 Dispel; // 3
- uint32 Mechanic; // 4
- uint32 Attributes; // 5
- uint32 AttributesEx; // 6
- uint32 AttributesEx2; // 7
- uint32 AttributesEx3; // 8
- uint32 AttributesEx4; // 9
- uint32 AttributesEx5; // 10
- //uint32 AttributesEx6; // 11 not used
- uint32 Stances; // 12
- uint32 StancesNot; // 13
- uint32 Targets; // 14
- uint32 TargetCreatureType; // 15
- uint32 RequiresSpellFocus; // 16
- uint32 FacingCasterFlags; // 17
- uint32 CasterAuraState; // 18
- uint32 TargetAuraState; // 19
- uint32 CasterAuraStateNot; // 20
- uint32 TargetAuraStateNot; // 21
- uint32 CastingTimeIndex; // 22
- uint32 RecoveryTime; // 23
- uint32 CategoryRecoveryTime; // 24
- uint32 InterruptFlags; // 25
- uint32 AuraInterruptFlags; // 26
- uint32 ChannelInterruptFlags; // 27
- uint32 procFlags; // 28
- uint32 procChance; // 29
- uint32 procCharges; // 30
- uint32 maxLevel; // 31
- uint32 baseLevel; // 32
- uint32 spellLevel; // 33
- uint32 DurationIndex; // 34
- uint32 powerType; // 35
- uint32 manaCost; // 36
- uint32 manaCostPerlevel; // 37
- uint32 manaPerSecond; // 38
- uint32 manaPerSecondPerLevel; // 39
- uint32 rangeIndex; // 40
- float speed; // 41
- //uint32 modalNextSpell; // 42
- uint32 StackAmount; // 43
- uint32 Totem[2]; // 44-45
- int32 Reagent[8]; // 46-53
- uint32 ReagentCount[8]; // 54-61
- int32 EquippedItemClass; // 62 (value)
- int32 EquippedItemSubClassMask; // 63 (mask)
- int32 EquippedItemInventoryTypeMask; // 64 (mask)
- uint32 Effect[3]; // 65-67
- int32 EffectDieSides[3]; // 68-70
- uint32 EffectBaseDice[3]; // 71-73
- float EffectDicePerLevel[3]; // 74-76
- float EffectRealPointsPerLevel[3]; // 77-79
- int32 EffectBasePoints[3]; // 80-82 (don't must be used in spell/auras explicitly, must be used cached Spell::m_currentBasePoints)
- uint32 EffectMechanic[3]; // 83-85
- uint32 EffectImplicitTargetA[3]; // 86-88
- uint32 EffectImplicitTargetB[3]; // 89-91
- uint32 EffectRadiusIndex[3]; // 92-94 - spellradius.dbc
- uint32 EffectApplyAuraName[3]; // 95-97
- uint32 EffectAmplitude[3]; // 98-100
- float EffectMultipleValue[3]; // 101-103
- uint32 EffectChainTarget[3]; // 104-106
- uint32 EffectItemType[3]; // 107-109
- int32 EffectMiscValue[3]; // 110-112
- int32 EffectMiscValueB[3]; // 113-115
- uint32 EffectTriggerSpell[3]; // 116-118
- float EffectPointsPerComboPoint[3]; // 119-121
- uint32 SpellVisual; // 122
- // 123 not used
- uint32 SpellIconID; // 124
- uint32 activeIconID; // 125
- //uint32 spellPriority; // 126
- char* SpellName[16]; // 127-142
- //uint32 SpellNameFlag; // 143
- char* Rank[16]; // 144-159
- //uint32 RankFlags; // 160
- //char* Description[16]; // 161-176 not used
- //uint32 DescriptionFlags; // 177 not used
- //char* ToolTip[16]; // 178-193 not used
- //uint32 ToolTipFlags; // 194 not used
- uint32 ManaCostPercentage; // 195
- uint32 StartRecoveryCategory; // 196
- uint32 StartRecoveryTime; // 197
- uint32 MaxTargetLevel; // 198
- uint32 SpellFamilyName; // 199
- uint64 SpellFamilyFlags; // 200+201
- uint32 MaxAffectedTargets; // 202
- uint32 DmgClass; // 203 defenseType
- uint32 PreventionType; // 204
- //uint32 StanceBarOrder; // 205 not used
- float DmgMultiplier[3]; // 206-208
- //uint32 MinFactionId; // 209 not used, and 0 in 2.4.2
- //uint32 MinReputation; // 210 not used, and 0 in 2.4.2
- //uint32 RequiredAuraVision; // 211 not used
- uint32 TotemCategory[2]; // 212-213
- uint32 AreaId; // 214
- uint32 SchoolMask; // 215 school mask
-
- private:
- // prevent creating custom entries (copy data from original in fact)
- SpellEntry(SpellEntry const&); // DON'T must have implementation
-};
-
-typedef std::set<uint32> SpellCategorySet;
-typedef std::map<uint32,SpellCategorySet > SpellCategoryStore;
-typedef std::set<uint32> PetFamilySpellsSet;
-typedef std::map<uint32,PetFamilySpellsSet > PetFamilySpellsStore;
-
-struct SpellCastTimesEntry
-{
- uint32 ID; // 0
- int32 CastTime; // 1
- //float CastTimePerLevel; // 2 unsure / per skill?
- //int32 MinCastTime; // 3 unsure
-};
-
-struct SpellFocusObjectEntry
-{
- uint32 ID; // 0
- //char* Name[16]; // 1-15 unused
- // 16 string flags, unused
-};
-
-// stored in SQL table
-struct SpellThreatEntry
-{
- uint32 spellId;
- int32 threat;
-};
-
-struct SpellRadiusEntry
-{
- uint32 ID;
- float Radius;
- float Radius2;
-};
-
-struct SpellRangeEntry
-{
- uint32 ID;
- float minRange;
- float maxRange;
-};
-
-struct SpellShapeshiftEntry
-{
- uint32 ID; // 0
- //uint32 buttonPosition; // 1 unused
- //char* Name[16]; // 2-17 unused
- //uint32 NameFlags; // 18 unused
- uint32 flags1; // 19
- int32 creatureType; // 20 <=0 humanoid, other normal creature types
- //uint32 unk1; // 21 unused
- uint32 attackSpeed; // 22
- //uint32 modelID; // 23 unused, alliance modelid (where horde case?)
- //uint32 unk2; // 24 unused
- //uint32 unk3; // 25 unused
- //uint32 unk4; // 26 unused
- //uint32 unk5; // 27 unused
- //uint32 unk6; // 28 unused
- //uint32 unk7; // 29 unused
- //uint32 unk8; // 30 unused
- //uint32 unk9; // 31 unused
- //uint32 unk10; // 32 unused
- //uint32 unk11; // 33 unused
- //uint32 unk12; // 34 unused
-};
-
-struct SpellDurationEntry
-{
- uint32 ID;
- int32 Duration[3];
-};
-
-enum ItemEnchantmentType
-{
- ITEM_ENCHANTMENT_TYPE_NONE = 0,
- ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL = 1,
- ITEM_ENCHANTMENT_TYPE_DAMAGE = 2,
- ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL = 3,
- ITEM_ENCHANTMENT_TYPE_RESISTANCE = 4,
- ITEM_ENCHANTMENT_TYPE_STAT = 5,
- ITEM_ENCHANTMENT_TYPE_TOTEM = 6
-};
-
-struct SpellItemEnchantmentEntry
-{
- uint32 ID; // 0
- uint32 type[3]; // 1-3
- uint32 amount[3]; // 4-6
- //uint32 amount2[3] // 7-9 always same as similar `amount` value
- uint32 spellid[3]; // 10-12
- char* description[16]; // 13-29
- // 30 description flags
- uint32 aura_id; // 31
- uint32 slot; // 32
- uint32 GemID; // 33
- uint32 EnchantmentCondition; // 34
-};
-
-struct SpellItemEnchantmentConditionEntry
-{
- uint32 ID;
- uint8 Color[5];
- uint8 Comparator[5];
- uint8 CompareColor[5];
- uint32 Value[5];
-};
-
-struct StableSlotPricesEntry
-{
- uint32 Slot;
- uint32 Price;
-};
-
-struct TalentEntry
-{
- uint32 TalentID; // 0
- uint32 TalentTab; // 1 index in TalentTab.dbc (TalentTabEntry)
- uint32 Row; // 2
- uint32 Col; // 3
- uint32 RankID[5]; // 4-8
- // 9-12 not used, always 0, maybe not used high ranks
- uint32 DependsOn; // 13 index in Talent.dbc (TalentEntry)
- // 14-15 not used
- uint32 DependsOnRank; // 16
- // 17-19 not used
- uint32 DependsOnSpell; // 20 req.spell
-};
-
-struct TalentTabEntry
-{
- uint32 TalentTabID; // 0
- //char* name[16]; // 1-16, unused
- //uint32 nameFlags; // 17, unused
- //unit32 spellicon; // 18
- // 19 not used
- uint32 ClassMask; // 20
- uint32 tabpage; // 21
- //char* internalname; // 22
-};
-
-struct TaxiPathEntry
-{
- uint32 ID;
- uint32 from;
- uint32 to;
- uint32 price;
-};
-
-struct TaxiNodesEntry
-{
- uint32 ID; // 0
- uint32 map_id; // 1
- float x; // 2
- float y; // 3
- float z; // 4
- //char* name[16]; // 5-21
- // 22 string flags, unused
- uint32 horde_mount_type; // 23
- uint32 alliance_mount_type; // 24
-};
-
-enum TotemCategoryType
-{
- TOTEM_CATEGORY_TYPE_KNIFE = 1,
- TOTEM_CATEGORY_TYPE_TOTEM = 2,
- TOTEM_CATEGORY_TYPE_ROD = 3,
- TOTEM_CATEGORY_TYPE_PICK = 21,
- TOTEM_CATEGORY_TYPE_STONE = 22,
- TOTEM_CATEGORY_TYPE_HAMMER = 23,
- TOTEM_CATEGORY_TYPE_SPANNER = 24
-};
-
-struct TotemCategoryEntry
-{
- uint32 ID; // 0
- //char* name[16]; // 1-16
- // 17 string flags, unused
- uint32 categoryType; // 18 (one for specialization)
- uint32 categoryMask; // 19 (compatibility mask for same type: different for totems, compatible from high to low for rods)
-};
-
-struct WorldMapAreaEntry
-{
- //uint32 ID; // 0
- uint32 map_id; // 1
- uint32 area_id; // 2 index (continent 0 areas ignored)
- //char* internal_name // 3
- float y1; // 4
- float y2; // 5
- float x1; // 6
- float x2; // 7
- int32 virtual_map_id; // 8 -1 (map_id have correct map) other: virtual map where zone show (map_id - where zone in fact internally)
-};
-
-struct WorldSafeLocsEntry
-{
- uint32 ID; // 0
- uint32 map_id; // 1
- float x; // 2
- float y; // 3
- float z; // 4
- //char* name[16] // 5-20 name, unused
- // 21 name flags, unused
-};
-
-// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
-#if defined( __GNUC__ )
-#pragma pack()
-#else
-#pragma pack(pop)
-#endif
-
-// Structures not used for casting to loaded DBC data and not required then packing
-struct TalentSpellPos
-{
- TalentSpellPos() : talent_id(0), rank(0) {}
- TalentSpellPos(uint16 _talent_id, uint8 _rank) : talent_id(_talent_id), rank(_rank) {}
-
- uint16 talent_id;
- uint8 rank;
-};
-
-typedef std::map<uint32,TalentSpellPos> TalentSpellPosMap;
-
-struct TaxiPathBySourceAndDestination
-{
- TaxiPathBySourceAndDestination() : ID(0),price(0) {}
- TaxiPathBySourceAndDestination(uint32 _id,uint32 _price) : ID(_id),price(_price) {}
-
- uint32 ID;
- uint32 price;
-};
-typedef std::map<uint32,TaxiPathBySourceAndDestination> TaxiPathSetForSource;
-typedef std::map<uint32,TaxiPathSetForSource> TaxiPathSetBySource;
-
-struct TaxiPathNode
-{
- TaxiPathNode() : mapid(0), x(0),y(0),z(0),actionFlag(0),delay(0) {}
- TaxiPathNode(uint32 _mapid, float _x, float _y, float _z, uint32 _actionFlag, uint32 _delay) : mapid(_mapid), x(_x),y(_y),z(_z),actionFlag(_actionFlag),delay(_delay) {}
-
- uint32 mapid;
- float x;
- float y;
- float z;
- uint32 actionFlag;
- uint32 delay;
-};
-typedef std::vector<TaxiPathNode> TaxiPathNodeList;
-typedef std::vector<TaxiPathNodeList> TaxiPathNodesByPath;
-
-#define TaxiMaskSize 16
-typedef uint32 TaxiMask[TaxiMaskSize];
-#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
+ */
+
+#ifndef DBCSTRUCTURE_H
+#define DBCSTRUCTURE_H
+
+#include "Platform/Define.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+// Structures using to access raw DBC data and required packing to portability
+
+// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack(1)
+#else
+#pragma pack(push,1)
+#endif
+
+enum AreaTeams
+{
+ AREATEAM_NONE = 0,
+ AREATEAM_ALLY = 2,
+ AREATEAM_HORDE = 4
+};
+
+enum AreaFlags
+{
+ AREA_FLAG_SNOW = 0x00000001, // snow (only Dun Morogh, Naxxramas, Razorfen Downs and Winterspring)
+ AREA_FLAG_UNK1 = 0x00000002, // unknown, (only Naxxramas and Razorfen Downs)
+ AREA_FLAG_UNK2 = 0x00000004, // Only used on development map
+ AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // slave capital city flag?
+ AREA_FLAG_UNK3 = 0x00000010, // unknown
+ AREA_FLAG_SLAVE_CAPITAL2 = 0x00000020, // slave capital city flag?
+ AREA_FLAG_UNK4 = 0x00000040, // many zones have this flag
+ AREA_FLAG_ARENA = 0x00000080, // arena, both instanced and world arenas
+ AREA_FLAG_CAPITAL = 0x00000100, // main capital city flag
+ AREA_FLAG_CITY = 0x00000200, // only for one zone named "City" (where it located?)
+ AREA_FLAG_OUTLAND = 0x00000400, // outland zones? (only Eye of the Storm not have this flag, but have 0x00004000 flag)
+ AREA_FLAG_SANCTUARY = 0x00000800, // sanctuary area (PvP disabled)
+ AREA_FLAG_NEED_FLY = 0x00001000, // only Netherwing Ledge, Socrethar's Seat, Tempest Keep, The Arcatraz, The Botanica, The Mechanar, Sorrow Wing Point, Dragonspine Ridge, Netherwing Mines, Dragonmaw Base Camp, Dragonmaw Skyway
+ AREA_FLAG_UNUSED1 = 0x00002000, // not used now (no area/zones with this flag set in 2.4.2)
+ AREA_FLAG_OUTLAND2 = 0x00004000, // outland zones? (only Circle of Blood Arena not have this flag, but have 0x00000400 flag)
+ AREA_FLAG_PVP = 0x00008000, // pvp objective area? (Death's Door also has this flag although it's no pvp object area)
+ AREA_FLAG_ARENA_INSTANCE = 0x00010000, // used by instanced arenas only
+ AREA_FLAG_UNUSED2 = 0x00020000, // not used now (no area/zones with this flag set in 2.4.2)
+ AREA_FLAG_UNK5 = 0x00040000, // just used for Amani Pass, Hatchet Hills
+ AREA_FLAG_LOWLEVEL = 0x00100000 // used for some starting areas with area_level <=15
+};
+
+enum FactionTemplateFlags
+{
+ FACTION_TEMPLATE_FLAG_CONTESTED_GUARD = 0x00001000, // faction will attack players that were involved in PvP combats
+};
+
+struct AreaTableEntry
+{
+ uint32 ID; // 0
+ uint32 mapid; // 1
+ uint32 zone; // 2 if 0 then it's zone, else it's zone id of this area
+ uint32 exploreFlag; // 3, main index
+ uint32 flags; // 4, unknown value but 312 for all cities
+ // 5-9 unused
+ int32 area_level; // 10
+ char* area_name[16]; // 11-26
+ // 27, string flags, unused
+ uint32 team; // 28
+};
+
+struct AreaTriggerEntry
+{
+ uint32 id; // 0
+ uint32 mapid; // 1
+ float x; // 2
+ float y; // 3
+ float z; // 4
+ float radius; // 5
+ float box_x; // 6 extent x edge
+ float box_y; // 7 extent y edge
+ float box_z; // 8 extent z edge
+ float box_orientation; // 9 extent rotation by about z axis
+};
+
+struct BankBagSlotPricesEntry
+{
+ uint32 ID;
+ uint32 price;
+};
+
+struct BattlemasterListEntry
+{
+ uint32 id; // 0
+ uint32 mapid[3]; // 1-3 mapid
+ // 4-8 unused
+ uint32 type; // 9 (3 - BG, 4 - arena)
+ uint32 minlvl; // 10
+ uint32 maxlvl; // 11
+ uint32 maxplayersperteam; // 12
+ // 13-14 unused
+ char* name[16]; // 15-30
+ // 31 string flag, unused
+ // 32 unused
+};
+
+struct CharTitlesEntry
+{
+ uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId()
+ //uint32 unk1; // 1 flags?
+ //char* name[16]; // 2-17, unused
+ // 18 string flag, unused
+ //char* name2[16]; // 19-34, unused
+ // 35 string flag, unused
+ uint32 bit_index; // 36 used in PLAYER_CHOSEN_TITLE and 1<<index in PLAYER__FIELD_KNOWN_TITLES
+};
+
+struct ChatChannelsEntry
+{
+ uint32 ChannelID; // 0
+ uint32 flags; // 1
+ char* pattern[16]; // 3-18
+ // 19 string flags, unused
+ //char* name[16]; // 20-35 unused
+ // 36 string flag, unused
+};
+
+struct ChrClassesEntry
+{
+ uint32 ClassID; // 0
+ // 1-2, unused
+ uint32 powerType; // 3
+ // 4, unused
+ //char* name[16]; // 5-20 unused
+ // 21 string flag, unused
+ //char* string1[16]; // 21-36 unused
+ // 37 string flag, unused
+ //char* string2[16]; // 38-53 unused
+ // 54 string flag, unused
+ // 55, unused
+ uint32 spellfamily; // 56
+ // 57, unused
+};
+
+struct ChrRacesEntry
+{
+ uint32 RaceID; // 0
+ // 1 unused
+ uint32 FactionID; // 2 facton template id
+ // 3 unused
+ uint32 model_m; // 4
+ uint32 model_f; // 5
+ // 6-7 unused
+ uint32 TeamID; // 8 (7-Alliance 1-Horde)
+ // 9-12 unused
+ uint32 startmovie; // 13 id from CinematicCamera.dbc
+ char* name[16]; // 14-29 used for DBC language detection/selection
+ // 30 string flags, unused
+ //char* string1[16]; // 31-46 used for DBC language detection/selection
+ // 47 string flags, unused
+ //char* string2[16]; // 48-63 used for DBC language detection/selection
+ // 64 string flags, unused
+ // 65-67 unused
+ //uint32 addon // 68 (0 - original race, 1 - tbc addon, ...) unused
+};
+
+struct CreatureDisplayInfoEntry
+{
+ uint32 Displayid; // 0
+ // 1-3,unused
+ float scale; // 4
+ // 5-13,unused
+};
+
+struct CreatureFamilyEntry
+{
+ uint32 ID; // 0
+ float minScale; // 1
+ uint32 minScaleLevel; // 2 0/1
+ float maxScale; // 3
+ uint32 maxScaleLevel; // 4 0/60
+ uint32 skillLine; // 5
+ uint32 skillLine2; // 6
+ uint32 petFoodMask; // 7
+ char* Name[16]; // 8-23
+ // 24 string flags, unused
+ // 25 icon, unused
+};
+
+struct CreatureSpellDataEntry
+{
+ uint32 ID; // 0
+ //uint32 spellId[4]; // 1-4 hunter pet learned spell (for later use)
+};
+
+struct DurabilityCostsEntry
+{
+ uint32 Itemlvl; // 0
+ uint32 multiplier[29]; // 1-29
+};
+
+struct DurabilityQualityEntry
+{
+ uint32 Id; // 0
+ float quality_mod; // 1
+};
+
+struct EmotesTextEntry
+{
+ uint32 Id;
+ uint32 textid;
+};
+
+struct FactionEntry
+{
+ uint32 ID; // 0
+ int32 reputationListID; // 1
+ uint32 BaseRepRaceMask[4]; // 2-5 Base reputation race masks (see enum Races)
+ uint32 BaseRepClassMask[4]; // 6-9 Base reputation class masks (see enum Classes)
+ int32 BaseRepValue[4]; // 10-13 Base reputation values
+ uint32 ReputationFlags[4]; // 14-17 Default flags to apply
+ uint32 team; // 18 enum Team
+ char* name[16]; // 19-34
+ // 35 string flags, unused
+ //char* description[16]; // 36-51 unused
+ // 52 string flags, unused
+};
+
+enum FactionMasks
+{
+ FACTION_MASK_PLAYER = 1, // any player
+ FACTION_MASK_ALLIANCE = 2, // player or creature from alliance team
+ FACTION_MASK_HORDE = 4, // player or creature from horde team
+ FACTION_MASK_MONSTER = 8 // aggressive creature from monster team
+ // if none flags set then non-aggressive creature
+};
+
+struct FactionTemplateEntry
+{
+ uint32 ID; // 0
+ uint32 faction; // 1
+ uint32 factionFlags; // 2 specific flags for that faction
+ uint32 ourMask; // 3 if mask set (see FactionMasks) then faction included in masked team
+ uint32 friendlyMask; // 4 if mask set (see FactionMasks) then faction friendly to masked team
+ uint32 hostileMask; // 5 if mask set (see FactionMasks) then faction hostile to masked team
+ uint32 enemyFaction1; // 6
+ uint32 enemyFaction2; // 7
+ uint32 enemyFaction3; // 8
+ uint32 enemyFaction4; // 9
+ uint32 friendFaction1; // 10
+ uint32 friendFaction2; // 11
+ uint32 friendFaction3; // 12
+ uint32 friendFaction4; // 13
+ //------------------------------------------------------- end structure
+
+ // helpers
+ bool IsFriendlyTo(FactionTemplateEntry const& entry) const
+ {
+ if(enemyFaction1 == entry.faction || enemyFaction2 == entry.faction || enemyFaction3 == entry.faction || enemyFaction4 == entry.faction )
+ return false;
+ if(friendFaction1 == entry.faction || friendFaction2 == entry.faction || friendFaction3 == entry.faction || friendFaction4 == entry.faction )
+ return true;
+ return (friendlyMask & entry.ourMask) || (ourMask & entry.friendlyMask);
+ }
+ bool IsHostileTo(FactionTemplateEntry const& entry) const
+ {
+ if(enemyFaction1 == entry.faction || enemyFaction2 == entry.faction || enemyFaction3 == entry.faction || enemyFaction4 == entry.faction )
+ return true;
+ if(friendFaction1 == entry.faction || friendFaction2 == entry.faction || friendFaction3 == entry.faction || friendFaction4 == entry.faction )
+ return false;
+ return (hostileMask & entry.ourMask) != 0;
+ }
+ bool IsHostileToPlayers() const { return (hostileMask & FACTION_MASK_PLAYER) !=0; }
+ bool IsNeutralToAll() const { return hostileMask == 0 && friendlyMask == 0 && enemyFaction1==0 && enemyFaction2==0 && enemyFaction3==0 && enemyFaction4==0; }
+ bool IsContestedGuardFaction() const { return (factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD)!=0; }
+};
+
+struct GemPropertiesEntry
+{
+ uint32 ID;
+ uint32 spellitemenchantement;
+ uint32 color;
+};
+
+#define GT_MAX_LEVEL 100
+struct GtCombatRatingsEntry
+{
+ float ratio;
+};
+
+struct GtChanceToMeleeCritBaseEntry
+{
+ float base;
+};
+
+struct GtChanceToMeleeCritEntry
+{
+ float ratio;
+};
+
+struct GtChanceToSpellCritBaseEntry
+{
+ float base;
+};
+
+struct GtChanceToSpellCritEntry
+{
+ float ratio;
+};
+
+struct GtOCTRegenHPEntry
+{
+ float ratio;
+};
+
+//struct GtOCTRegenMPEntry
+//{
+// float ratio;
+//};
+
+struct GtRegenHPPerSptEntry
+{
+ float ratio;
+};
+
+struct GtRegenMPPerSptEntry
+{
+ float ratio;
+};
+
+struct ItemEntry
+{
+ uint32 ID;
+ uint32 DisplayId;
+ uint32 InventoryType;
+ uint32 Sheath;
+};
+
+struct ItemDisplayInfoEntry
+{
+ uint32 ID;
+ uint32 randomPropertyChance;
+};
+
+//struct ItemCondExtCostsEntry
+//{
+// uint32 ID;
+// uint32 condExtendedCost; // ItemPrototype::CondExtendedCost
+// uint32 itemextendedcostentry; // ItemPrototype::ExtendedCost
+// uint32 arenaseason; // arena season number(1-4)
+//};
+
+struct ItemExtendedCostEntry
+{
+ uint32 ID; // 0 extended-cost entry id
+ uint32 reqhonorpoints; // 1 required honor points
+ uint32 reqarenapoints; // 2 required arena points
+ uint32 reqitem[5]; // 3-7 required item id
+ uint32 reqitemcount[5]; // 8-12 required count of 1st item
+ uint32 reqpersonalarenarating; // 13 required personal arena rating
+};
+
+struct ItemRandomPropertiesEntry
+{
+ uint32 ID; // 0
+ //char* internalName // 1 unused
+ uint32 enchant_id[3]; // 2-4
+ // 5-6 unused, 0 only values, reserved for additional enchantments?
+ //char* nameSuffix[16] // 7-22, unused
+ // 23 nameSufix flags, unused
+};
+
+struct ItemRandomSuffixEntry
+{
+ uint32 ID; // 0
+ //char* name[16] // 1-16 unused
+ // 17, name flags, unused
+ // 18 unused
+ uint32 enchant_id[3]; // 19-21
+ uint32 prefix[3]; // 22-24
+};
+
+struct ItemSetEntry
+{
+ //uint32 id // 0 item set ID
+ char* name[16]; // 1-16
+ // 17 string flags, unused
+ // 18-28 items from set, but not have all items listed, use ItemPrototype::ItemSet instead
+ // 29-34 unused
+ uint32 spells[8]; // 35-42
+ uint32 items_to_triggerspell[8]; // 43-50
+ uint32 required_skill_id; // 51
+ uint32 required_skill_value; // 52
+};
+
+struct LockEntry
+{
+ uint32 ID; // 0
+ uint32 keytype[5]; // 1-5
+ // 6-8, not used
+ uint32 key[5]; // 9-13
+ // 14-16, not used
+ uint32 requiredminingskill; // 17
+ uint32 requiredlockskill; // 18
+ // 19-32, not used
+};
+
+struct MailTemplateEntry
+{
+ uint32 ID; // 0
+ //char* subject[16]; // 1-16
+ // 17 name flags, unused
+ //char* content[16]; // 18-33
+};
+
+enum MapTypes
+{
+ MAP_COMMON = 0,
+ MAP_INSTANCE = 1,
+ MAP_RAID = 2,
+ MAP_BATTLEGROUND = 3,
+ MAP_ARENA = 4
+};
+
+struct MapEntry
+{
+ uint32 MapID; // 0
+ //char* internalname; // 1 unused
+ uint32 map_type; // 2
+ // 3 unused
+ char* name[16]; // 4-19
+ // 20 name flags, unused
+ // 21-23 unused (something PvPZone related - levels?)
+ // 24-26
+ uint32 linked_zone; // 27 common zone for instance and continent map
+ //char* hordeIntro // 28-43 text for PvP Zones
+ // 44 intro text flags
+ //char* allianceIntro // 45-60 text for PvP Zones
+ // 46 intro text flags
+ // 47-61 not used
+ uint32 multimap_id; // 62
+ // 63-65 not used
+ //chat* unknownText1 // 66-81 unknown empty text fields, possible normal Intro text.
+ // 82 text flags
+ //chat* heroicIntroText // 83-98 heroic mode requirement text
+ // 99 text flags
+ //chat* unknownText2 // 100-115 unknown empty text fields
+ // 116 text flags
+ int32 parent_map; // 117 map_id of parent map
+ //float start_x // 118 enter x coordinate (if exist single entry)
+ //float start_y // 119 enter y coordinate (if exist single entry)
+ uint32 resetTimeRaid; // 120
+ uint32 resetTimeHeroic; // 121
+ // 122-123
+ uint32 addon; // 124 (0-original maps,1-tbc addon)
+
+ // Helpers
+ bool IsExpansionMap() const { return addon != 0; }
+
+
+ bool IsDungeon() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID; }
+ bool Instanceable() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID || map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; }
+ bool IsRaid() const { return map_type == MAP_RAID; }
+ bool IsBattleGround() const { return map_type == MAP_BATTLEGROUND; }
+ bool IsBattleArena() const { return map_type == MAP_ARENA; }
+ bool IsBattleGroundOrArena() const { return map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; }
+ bool SupportsHeroicMode() const { return resetTimeHeroic && !resetTimeRaid; }
+ bool HasResetTime() const { return resetTimeHeroic || resetTimeRaid; }
+
+ bool IsMountAllowed() const
+ {
+ return !IsDungeon() ||
+ MapID==568 || MapID==309 || MapID==209 || MapID==534 ||
+ MapID==560 || MapID==509 || MapID==269;
+ }
+};
+
+struct QuestSortEntry
+{
+ uint32 id; // 0, sort id
+ //char* name[16]; // 1-16, unused
+ // 17 name flags, unused
+};
+
+struct RandomPropertiesPointsEntry
+{
+ //uint32 Id; // 0 hidden key
+ uint32 itemLevel; // 1
+ uint32 EpicPropertiesPoints[5]; // 2-6
+ uint32 RarePropertiesPoints[5]; // 7-11
+ uint32 UncommonPropertiesPoints[5]; // 12-16
+};
+
+//struct SkillLineCategoryEntry{
+// uint32 id; // 0 hidden key
+// char* name[16]; // 1 - 17 Category name
+// // 18 string flag
+// uint32 displayOrder; // Display order in character tab
+//};
+
+//struct SkillRaceClassInfoEntry{
+// uint32 id; // 0
+// uint32 skillId; // 1 present some refrences to unknown skill
+// uint32 raceMask; // 2
+// uint32 classMask; // 3
+// uint32 flags; // 4 mask for some thing
+// uint32 reqLevel; // 5
+// uint32 skillTierId; // 6
+// uint32 skillCostID; // 7
+//};
+
+//struct SkillTiersEntry{
+// uint32 id; // 0
+// uint32 skillValue[16]; // 1-17 unknown possibly add value on learn?
+// uint32 maxSkillValue[16]; // Max value for rank
+//};
+
+struct SkillLineEntry
+{
+ uint32 id; // 0
+ uint32 categoryId; // 1 (index from SkillLineCategory.dbc)
+ //uint32 skillCostID; // 2 not used
+ char* name[16]; // 3-18
+ // 19 string flags, not used
+ //char* description[16]; // 20-35, not used
+ // 36 string flags, not used
+ uint32 spellIcon; // 37
+};
+
+enum AbilytyLearnType
+{
+ ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1,
+ ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2
+};
+
+struct SkillLineAbilityEntry
+{
+ uint32 id; // 0, INDEX
+ uint32 skillId; // 1
+ uint32 spellId; // 2
+ uint32 racemask; // 3
+ uint32 classmask; // 4
+ //uint32 racemaskNot; // 5 always 0 in 2.4.2
+ //uint32 classmaskNot; // 6 always 0 in 2.4.2
+ uint32 req_skill_value; // 7 for trade skill.not for training.
+ uint32 forward_spellid; // 8
+ uint32 learnOnGetSkill; // 9 can be 1 or 2 for spells learned on get skill
+ uint32 max_value; // 10
+ uint32 min_value; // 11
+ // 12-13, unknown, always 0
+ uint32 reqtrainpoints; // 14
+};
+
+struct SoundEntriesEntry
+{
+ uint32 Id; // 0, sound id
+ //uint32 Type; // 1, sound type (10 generally for creature, etc)
+ //char* InternalName; // 2, internal name, for use in lookup command for example
+ //char* FileName[10]; // 3-12, file names
+ //uint32 Unk13[10]; // 13-22, linked with file names?
+ //char* Path; // 23
+ // 24-28, unknown
+};
+
+struct SpellEntry
+{
+ uint32 Id; // 0 normally counted from 0 field (but some tools start counting from 1, check this before tool use for data view!)
+ uint32 Category; // 1
+ //uint32 castUI // 2 not used
+ uint32 Dispel; // 3
+ uint32 Mechanic; // 4
+ uint32 Attributes; // 5
+ uint32 AttributesEx; // 6
+ uint32 AttributesEx2; // 7
+ uint32 AttributesEx3; // 8
+ uint32 AttributesEx4; // 9
+ uint32 AttributesEx5; // 10
+ //uint32 AttributesEx6; // 11 not used
+ uint32 Stances; // 12
+ uint32 StancesNot; // 13
+ uint32 Targets; // 14
+ uint32 TargetCreatureType; // 15
+ uint32 RequiresSpellFocus; // 16
+ uint32 FacingCasterFlags; // 17
+ uint32 CasterAuraState; // 18
+ uint32 TargetAuraState; // 19
+ uint32 CasterAuraStateNot; // 20
+ uint32 TargetAuraStateNot; // 21
+ uint32 CastingTimeIndex; // 22
+ uint32 RecoveryTime; // 23
+ uint32 CategoryRecoveryTime; // 24
+ uint32 InterruptFlags; // 25
+ uint32 AuraInterruptFlags; // 26
+ uint32 ChannelInterruptFlags; // 27
+ uint32 procFlags; // 28
+ uint32 procChance; // 29
+ uint32 procCharges; // 30
+ uint32 maxLevel; // 31
+ uint32 baseLevel; // 32
+ uint32 spellLevel; // 33
+ uint32 DurationIndex; // 34
+ uint32 powerType; // 35
+ uint32 manaCost; // 36
+ uint32 manaCostPerlevel; // 37
+ uint32 manaPerSecond; // 38
+ uint32 manaPerSecondPerLevel; // 39
+ uint32 rangeIndex; // 40
+ float speed; // 41
+ //uint32 modalNextSpell; // 42
+ uint32 StackAmount; // 43
+ uint32 Totem[2]; // 44-45
+ int32 Reagent[8]; // 46-53
+ uint32 ReagentCount[8]; // 54-61
+ int32 EquippedItemClass; // 62 (value)
+ int32 EquippedItemSubClassMask; // 63 (mask)
+ int32 EquippedItemInventoryTypeMask; // 64 (mask)
+ uint32 Effect[3]; // 65-67
+ int32 EffectDieSides[3]; // 68-70
+ uint32 EffectBaseDice[3]; // 71-73
+ float EffectDicePerLevel[3]; // 74-76
+ float EffectRealPointsPerLevel[3]; // 77-79
+ int32 EffectBasePoints[3]; // 80-82 (don't must be used in spell/auras explicitly, must be used cached Spell::m_currentBasePoints)
+ uint32 EffectMechanic[3]; // 83-85
+ uint32 EffectImplicitTargetA[3]; // 86-88
+ uint32 EffectImplicitTargetB[3]; // 89-91
+ uint32 EffectRadiusIndex[3]; // 92-94 - spellradius.dbc
+ uint32 EffectApplyAuraName[3]; // 95-97
+ uint32 EffectAmplitude[3]; // 98-100
+ float EffectMultipleValue[3]; // 101-103
+ uint32 EffectChainTarget[3]; // 104-106
+ uint32 EffectItemType[3]; // 107-109
+ int32 EffectMiscValue[3]; // 110-112
+ int32 EffectMiscValueB[3]; // 113-115
+ uint32 EffectTriggerSpell[3]; // 116-118
+ float EffectPointsPerComboPoint[3]; // 119-121
+ uint32 SpellVisual; // 122
+ // 123 not used
+ uint32 SpellIconID; // 124
+ uint32 activeIconID; // 125
+ //uint32 spellPriority; // 126
+ char* SpellName[16]; // 127-142
+ //uint32 SpellNameFlag; // 143
+ char* Rank[16]; // 144-159
+ //uint32 RankFlags; // 160
+ //char* Description[16]; // 161-176 not used
+ //uint32 DescriptionFlags; // 177 not used
+ //char* ToolTip[16]; // 178-193 not used
+ //uint32 ToolTipFlags; // 194 not used
+ uint32 ManaCostPercentage; // 195
+ uint32 StartRecoveryCategory; // 196
+ uint32 StartRecoveryTime; // 197
+ uint32 MaxTargetLevel; // 198
+ uint32 SpellFamilyName; // 199
+ uint64 SpellFamilyFlags; // 200+201
+ uint32 MaxAffectedTargets; // 202
+ uint32 DmgClass; // 203 defenseType
+ uint32 PreventionType; // 204
+ //uint32 StanceBarOrder; // 205 not used
+ float DmgMultiplier[3]; // 206-208
+ //uint32 MinFactionId; // 209 not used, and 0 in 2.4.2
+ //uint32 MinReputation; // 210 not used, and 0 in 2.4.2
+ //uint32 RequiredAuraVision; // 211 not used
+ uint32 TotemCategory[2]; // 212-213
+ uint32 AreaId; // 214
+ uint32 SchoolMask; // 215 school mask
+
+ private:
+ // prevent creating custom entries (copy data from original in fact)
+ SpellEntry(SpellEntry const&); // DON'T must have implementation
+};
+
+typedef std::set<uint32> SpellCategorySet;
+typedef std::map<uint32,SpellCategorySet > SpellCategoryStore;
+typedef std::set<uint32> PetFamilySpellsSet;
+typedef std::map<uint32,PetFamilySpellsSet > PetFamilySpellsStore;
+
+struct SpellCastTimesEntry
+{
+ uint32 ID; // 0
+ int32 CastTime; // 1
+ //float CastTimePerLevel; // 2 unsure / per skill?
+ //int32 MinCastTime; // 3 unsure
+};
+
+struct SpellFocusObjectEntry
+{
+ uint32 ID; // 0
+ //char* Name[16]; // 1-15 unused
+ // 16 string flags, unused
+};
+
+// stored in SQL table
+struct SpellThreatEntry
+{
+ uint32 spellId;
+ int32 threat;
+};
+
+struct SpellRadiusEntry
+{
+ uint32 ID;
+ float Radius;
+ float Radius2;
+};
+
+struct SpellRangeEntry
+{
+ uint32 ID;
+ float minRange;
+ float maxRange;
+};
+
+struct SpellShapeshiftEntry
+{
+ uint32 ID; // 0
+ //uint32 buttonPosition; // 1 unused
+ //char* Name[16]; // 2-17 unused
+ //uint32 NameFlags; // 18 unused
+ uint32 flags1; // 19
+ int32 creatureType; // 20 <=0 humanoid, other normal creature types
+ //uint32 unk1; // 21 unused
+ uint32 attackSpeed; // 22
+ //uint32 modelID; // 23 unused, alliance modelid (where horde case?)
+ //uint32 unk2; // 24 unused
+ //uint32 unk3; // 25 unused
+ //uint32 unk4; // 26 unused
+ //uint32 unk5; // 27 unused
+ //uint32 unk6; // 28 unused
+ //uint32 unk7; // 29 unused
+ //uint32 unk8; // 30 unused
+ //uint32 unk9; // 31 unused
+ //uint32 unk10; // 32 unused
+ //uint32 unk11; // 33 unused
+ //uint32 unk12; // 34 unused
+};
+
+struct SpellDurationEntry
+{
+ uint32 ID;
+ int32 Duration[3];
+};
+
+enum ItemEnchantmentType
+{
+ ITEM_ENCHANTMENT_TYPE_NONE = 0,
+ ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL = 1,
+ ITEM_ENCHANTMENT_TYPE_DAMAGE = 2,
+ ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL = 3,
+ ITEM_ENCHANTMENT_TYPE_RESISTANCE = 4,
+ ITEM_ENCHANTMENT_TYPE_STAT = 5,
+ ITEM_ENCHANTMENT_TYPE_TOTEM = 6
+};
+
+struct SpellItemEnchantmentEntry
+{
+ uint32 ID; // 0
+ uint32 type[3]; // 1-3
+ uint32 amount[3]; // 4-6
+ //uint32 amount2[3] // 7-9 always same as similar `amount` value
+ uint32 spellid[3]; // 10-12
+ char* description[16]; // 13-29
+ // 30 description flags
+ uint32 aura_id; // 31
+ uint32 slot; // 32
+ uint32 GemID; // 33
+ uint32 EnchantmentCondition; // 34
+};
+
+struct SpellItemEnchantmentConditionEntry
+{
+ uint32 ID;
+ uint8 Color[5];
+ uint8 Comparator[5];
+ uint8 CompareColor[5];
+ uint32 Value[5];
+};
+
+struct StableSlotPricesEntry
+{
+ uint32 Slot;
+ uint32 Price;
+};
+
+struct TalentEntry
+{
+ uint32 TalentID; // 0
+ uint32 TalentTab; // 1 index in TalentTab.dbc (TalentTabEntry)
+ uint32 Row; // 2
+ uint32 Col; // 3
+ uint32 RankID[5]; // 4-8
+ // 9-12 not used, always 0, maybe not used high ranks
+ uint32 DependsOn; // 13 index in Talent.dbc (TalentEntry)
+ // 14-15 not used
+ uint32 DependsOnRank; // 16
+ // 17-19 not used
+ uint32 DependsOnSpell; // 20 req.spell
+};
+
+struct TalentTabEntry
+{
+ uint32 TalentTabID; // 0
+ //char* name[16]; // 1-16, unused
+ //uint32 nameFlags; // 17, unused
+ //unit32 spellicon; // 18
+ // 19 not used
+ uint32 ClassMask; // 20
+ uint32 tabpage; // 21
+ //char* internalname; // 22
+};
+
+struct TaxiPathEntry
+{
+ uint32 ID;
+ uint32 from;
+ uint32 to;
+ uint32 price;
+};
+
+struct TaxiNodesEntry
+{
+ uint32 ID; // 0
+ uint32 map_id; // 1
+ float x; // 2
+ float y; // 3
+ float z; // 4
+ //char* name[16]; // 5-21
+ // 22 string flags, unused
+ uint32 horde_mount_type; // 23
+ uint32 alliance_mount_type; // 24
+};
+
+enum TotemCategoryType
+{
+ TOTEM_CATEGORY_TYPE_KNIFE = 1,
+ TOTEM_CATEGORY_TYPE_TOTEM = 2,
+ TOTEM_CATEGORY_TYPE_ROD = 3,
+ TOTEM_CATEGORY_TYPE_PICK = 21,
+ TOTEM_CATEGORY_TYPE_STONE = 22,
+ TOTEM_CATEGORY_TYPE_HAMMER = 23,
+ TOTEM_CATEGORY_TYPE_SPANNER = 24
+};
+
+struct TotemCategoryEntry
+{
+ uint32 ID; // 0
+ //char* name[16]; // 1-16
+ // 17 string flags, unused
+ uint32 categoryType; // 18 (one for specialization)
+ uint32 categoryMask; // 19 (compatibility mask for same type: different for totems, compatible from high to low for rods)
+};
+
+struct WorldMapAreaEntry
+{
+ //uint32 ID; // 0
+ uint32 map_id; // 1
+ uint32 area_id; // 2 index (continent 0 areas ignored)
+ //char* internal_name // 3
+ float y1; // 4
+ float y2; // 5
+ float x1; // 6
+ float x2; // 7
+ int32 virtual_map_id; // 8 -1 (map_id have correct map) other: virtual map where zone show (map_id - where zone in fact internally)
+};
+
+struct WorldSafeLocsEntry
+{
+ uint32 ID; // 0
+ uint32 map_id; // 1
+ float x; // 2
+ float y; // 3
+ float z; // 4
+ //char* name[16] // 5-20 name, unused
+ // 21 name flags, unused
+};
+
+// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack()
+#else
+#pragma pack(pop)
+#endif
+
+// Structures not used for casting to loaded DBC data and not required then packing
+struct TalentSpellPos
+{
+ TalentSpellPos() : talent_id(0), rank(0) {}
+ TalentSpellPos(uint16 _talent_id, uint8 _rank) : talent_id(_talent_id), rank(_rank) {}
+
+ uint16 talent_id;
+ uint8 rank;
+};
+
+typedef std::map<uint32,TalentSpellPos> TalentSpellPosMap;
+
+struct TaxiPathBySourceAndDestination
+{
+ TaxiPathBySourceAndDestination() : ID(0),price(0) {}
+ TaxiPathBySourceAndDestination(uint32 _id,uint32 _price) : ID(_id),price(_price) {}
+
+ uint32 ID;
+ uint32 price;
+};
+typedef std::map<uint32,TaxiPathBySourceAndDestination> TaxiPathSetForSource;
+typedef std::map<uint32,TaxiPathSetForSource> TaxiPathSetBySource;
+
+struct TaxiPathNode
+{
+ TaxiPathNode() : mapid(0), x(0),y(0),z(0),actionFlag(0),delay(0) {}
+ TaxiPathNode(uint32 _mapid, float _x, float _y, float _z, uint32 _actionFlag, uint32 _delay) : mapid(_mapid), x(_x),y(_y),z(_z),actionFlag(_actionFlag),delay(_delay) {}
+
+ uint32 mapid;
+ float x;
+ float y;
+ float z;
+ uint32 actionFlag;
+ uint32 delay;
+};
+typedef std::vector<TaxiPathNode> TaxiPathNodeList;
+typedef std::vector<TaxiPathNodeList> TaxiPathNodesByPath;
+
+#define TaxiMaskSize 16
+typedef uint32 TaxiMask[TaxiMaskSize];
+#endif
diff --git a/src/shared/Database/DBCfmt.cpp b/src/shared/Database/DBCfmt.cpp
index c7806e24690..e131d9379c5 100644
--- a/src/shared/Database/DBCfmt.cpp
+++ b/src/shared/Database/DBCfmt.cpp
@@ -1,78 +1,78 @@
-/*
- * 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
- */
-
-const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxx";
-const char AreaTriggerEntryfmt[]="niffffffff";
-const char BankBagSlotPricesEntryfmt[]="ni";
-const char BattlemasterListEntryfmt[]="niiixxxxxiiiixxssssssssssssssssxx";
-const char CharTitlesEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
-const char ChatChannelsEntryfmt[]="iixssssssssssssssssxxxxxxxxxxxxxxxxxx";
- // ChatChannelsEntryfmt, index not used (more compact store)
-const char ChrClassesEntryfmt[]="nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix";
-const char ChrRacesEntryfmt[]="nxixiixxixxxxissssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
-const char CreatureDisplayInfofmt[]="nxxxfxxxxxxxxx";
-const char CreatureFamilyfmt[]="nfifiiiissssssssssssssssxx";
-const char CreatureSpellDatafmt[]="nxxxxxxxx";
-const char DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii";
-const char DurabilityQualityfmt[]="nf";
-const char EmoteEntryfmt[]="nxixxxxxxxxxxxxxxxx";
-const char FactionEntryfmt[]="niiiiiiiiiiiiiiiiiissssssssssssssssxxxxxxxxxxxxxxxxxx";
-const char FactionTemplateEntryfmt[]="niiiiiiiiiiiii";
-const char GemPropertiesEntryfmt[]="nixxi";
-const char GtCombatRatingsfmt[]="f";
-const char GtChanceToMeleeCritBasefmt[]="f";
-const char GtChanceToMeleeCritfmt[]="f";
-const char GtChanceToSpellCritBasefmt[]="f";
-const char GtChanceToSpellCritfmt[]="f";
-const char GtOCTRegenHPfmt[]="f";
-//const char GtOCTRegenMPfmt[]="f";
-const char GtRegenHPPerSptfmt[]="f";
-const char GtRegenMPPerSptfmt[]="f";
-const char Itemfmt[]="niii";
-//const char ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx";
-//const char ItemCondExtCostsEntryfmt[]="xiii";
-const char ItemExtendedCostEntryfmt[]="niiiiiiiiiiiii";
-const char ItemRandomPropertiesfmt[]="nxiiixxxxxxxxxxxxxxxxxxx";
-const char ItemRandomSuffixfmt[]="nxxxxxxxxxxxxxxxxxxiiiiii";
-const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii";
-const char LockEntryfmt[]="niiiiixxxiiiiixxxiixxxxxxxxxxxxxx";
-const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
-const char MapEntryfmt[]="nxixssssssssssssssssxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxiixxi";
-const char QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx";
-const char RandomPropertiesPointsfmt[]="niiiiiiiiiiiiiii";
-const char SkillLinefmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxxi";
-const char SkillLineAbilityfmt[]="niiiixxiiiiixxi";
-const char SoundEntriesfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
-const char SpellCastTimefmt[]="nixx";
-const char SpellDurationfmt[]="niii";
-const char SpellEntryfmt[]="nixiiiiiiiixiiiiiiiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffffffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffixiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiixfffxxxiiii";
-const char SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx";
-const char SpellItemEnchantmentfmt[]="niiiiiixxxiiissssssssssssssssxiiii";
-const char SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX";
-const char SpellRadiusfmt[]="nfxf";
-const char SpellRangefmt[]="nffxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
-const char SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxx";
-const char StableSlotPricesfmt[] = "ni";
-const char TalentEntryfmt[]="niiiiiiiixxxxixxixxxi";
-const char TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiix";
-const char TaxiNodesEntryfmt[]="nifffxxxxxxxxxxxxxxxxxii";
-const char TaxiPathEntryfmt[]="niii";
-const char TaxiPathNodeEntryfmt[]="diiifffiixx";
-const char TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii";
-const char WorldMapAreaEntryfmt[]="xinxffffi";
-const char WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx";
+/*
+ * 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
+ */
+
+const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxx";
+const char AreaTriggerEntryfmt[]="niffffffff";
+const char BankBagSlotPricesEntryfmt[]="ni";
+const char BattlemasterListEntryfmt[]="niiixxxxxiiiixxssssssssssssssssxx";
+const char CharTitlesEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
+const char ChatChannelsEntryfmt[]="iixssssssssssssssssxxxxxxxxxxxxxxxxxx";
+ // ChatChannelsEntryfmt, index not used (more compact store)
+const char ChrClassesEntryfmt[]="nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix";
+const char ChrRacesEntryfmt[]="nxixiixxixxxxissssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+const char CreatureDisplayInfofmt[]="nxxxfxxxxxxxxx";
+const char CreatureFamilyfmt[]="nfifiiiissssssssssssssssxx";
+const char CreatureSpellDatafmt[]="nxxxxxxxx";
+const char DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii";
+const char DurabilityQualityfmt[]="nf";
+const char EmoteEntryfmt[]="nxixxxxxxxxxxxxxxxx";
+const char FactionEntryfmt[]="niiiiiiiiiiiiiiiiiissssssssssssssssxxxxxxxxxxxxxxxxxx";
+const char FactionTemplateEntryfmt[]="niiiiiiiiiiiii";
+const char GemPropertiesEntryfmt[]="nixxi";
+const char GtCombatRatingsfmt[]="f";
+const char GtChanceToMeleeCritBasefmt[]="f";
+const char GtChanceToMeleeCritfmt[]="f";
+const char GtChanceToSpellCritBasefmt[]="f";
+const char GtChanceToSpellCritfmt[]="f";
+const char GtOCTRegenHPfmt[]="f";
+//const char GtOCTRegenMPfmt[]="f";
+const char GtRegenHPPerSptfmt[]="f";
+const char GtRegenMPPerSptfmt[]="f";
+const char Itemfmt[]="niii";
+//const char ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx";
+//const char ItemCondExtCostsEntryfmt[]="xiii";
+const char ItemExtendedCostEntryfmt[]="niiiiiiiiiiiii";
+const char ItemRandomPropertiesfmt[]="nxiiixxxxxxxxxxxxxxxxxxx";
+const char ItemRandomSuffixfmt[]="nxxxxxxxxxxxxxxxxxxiiiiii";
+const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii";
+const char LockEntryfmt[]="niiiiixxxiiiiixxxiixxxxxxxxxxxxxx";
+const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+const char MapEntryfmt[]="nxixssssssssssssssssxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxiixxi";
+const char QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx";
+const char RandomPropertiesPointsfmt[]="niiiiiiiiiiiiiii";
+const char SkillLinefmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxxi";
+const char SkillLineAbilityfmt[]="niiiixxiiiiixxi";
+const char SoundEntriesfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+const char SpellCastTimefmt[]="nixx";
+const char SpellDurationfmt[]="niii";
+const char SpellEntryfmt[]="nixiiiiiiiixiiiiiiiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffffffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffixiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiixfffxxxiiii";
+const char SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx";
+const char SpellItemEnchantmentfmt[]="niiiiiixxxiiissssssssssssssssxiiii";
+const char SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX";
+const char SpellRadiusfmt[]="nfxf";
+const char SpellRangefmt[]="nffxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+const char SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxx";
+const char StableSlotPricesfmt[] = "ni";
+const char TalentEntryfmt[]="niiiiiiiixxxxixxixxxi";
+const char TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiix";
+const char TaxiNodesEntryfmt[]="nifffxxxxxxxxxxxxxxxxxii";
+const char TaxiPathEntryfmt[]="niii";
+const char TaxiPathNodeEntryfmt[]="diiifffiixx";
+const char TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii";
+const char WorldMapAreaEntryfmt[]="xinxffffi";
+const char WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx";
diff --git a/src/shared/Database/DatabaseEnv.h b/src/shared/Database/DatabaseEnv.h
index d3210458dbb..405dcbdfa33 100644
--- a/src/shared/Database/DatabaseEnv.h
+++ b/src/shared/Database/DatabaseEnv.h
@@ -1,54 +1,54 @@
-/*
- * 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
- */
-
-#if !defined(DATABASEENV_H)
-#define DATABASEENV_H
-
-#include "Common.h"
-#include "Log.h"
-#include "Errors.h"
-
-#include "Database/DBCStores.h"
-#include "Database/Field.h"
-#include "Database/QueryResult.h"
-
-#ifdef DO_POSTGRESQL
-#include "Database/QueryResultPostgre.h"
-#include "Database/Database.h"
-#include "Database/DatabasePostgre.h"
-typedef DatabasePostgre DatabaseType;
-#define _LIKE_ "ILIKE"
-#define _TABLE_SIM_ "\""
-#define _CONCAT3_(A,B,C) "( " A " || " B " || " C " )"
-#else
-#include "Database/QueryResultMysql.h"
-#include "Database/QueryResultSqlite.h"
-#include "Database/Database.h"
-#include "Database/DatabaseMysql.h"
-#include "Database/DatabaseSqlite.h"
-typedef DatabaseMysql DatabaseType;
-#define _LIKE_ "LIKE"
-#define _TABLE_SIM_ "`"
-#define _CONCAT3_(A,B,C) "CONCAT( " A " , " B " , " C " )"
-#endif
-
-extern DatabaseType WorldDatabase;
-extern DatabaseType CharacterDatabase;
-extern DatabaseType loginDatabase;
-
-#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
+ */
+
+#if !defined(DATABASEENV_H)
+#define DATABASEENV_H
+
+#include "Common.h"
+#include "Log.h"
+#include "Errors.h"
+
+#include "Database/DBCStores.h"
+#include "Database/Field.h"
+#include "Database/QueryResult.h"
+
+#ifdef DO_POSTGRESQL
+#include "Database/QueryResultPostgre.h"
+#include "Database/Database.h"
+#include "Database/DatabasePostgre.h"
+typedef DatabasePostgre DatabaseType;
+#define _LIKE_ "ILIKE"
+#define _TABLE_SIM_ "\""
+#define _CONCAT3_(A,B,C) "( " A " || " B " || " C " )"
+#else
+#include "Database/QueryResultMysql.h"
+#include "Database/QueryResultSqlite.h"
+#include "Database/Database.h"
+#include "Database/DatabaseMysql.h"
+#include "Database/DatabaseSqlite.h"
+typedef DatabaseMysql DatabaseType;
+#define _LIKE_ "LIKE"
+#define _TABLE_SIM_ "`"
+#define _CONCAT3_(A,B,C) "CONCAT( " A " , " B " , " C " )"
+#endif
+
+extern DatabaseType WorldDatabase;
+extern DatabaseType CharacterDatabase;
+extern DatabaseType loginDatabase;
+
+#endif
diff --git a/src/shared/WheatyExceptionReport.cpp b/src/shared/WheatyExceptionReport.cpp
index 27fae70e4c1..41d2a5e2727 100644
--- a/src/shared/WheatyExceptionReport.cpp
+++ b/src/shared/WheatyExceptionReport.cpp
@@ -1,1014 +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 <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;
-}
+//==========================================
+// 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 055da9a7a5d..896d9c72f36 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 );
-
- 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
+#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